mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 10:56:31 +00:00
refactor external clients
put model batch size as parameter in config
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MessagePack" Version="3.1.0" />
|
||||
<PackageReference Include="MessagePack.Annotations" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" />
|
||||
<PackageReference Include="NetMQ" Version="4.0.1.13" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
|
||||
|
||||
@@ -11,10 +11,10 @@ public class RemoteCommand(CommandType commandType, byte[]? data = null)
|
||||
[Key("Data")]
|
||||
public byte[]? Data { get; set; } = data;
|
||||
|
||||
public static byte[] Serialize<T>(CommandType commandType, T data) where T : class
|
||||
public static RemoteCommand Create<T>(CommandType commandType, T data) where T : class
|
||||
{
|
||||
var dataBytes = MessagePackSerializer.Serialize(data);
|
||||
return MessagePackSerializer.Serialize(new RemoteCommand(commandType, dataBytes ));
|
||||
return new RemoteCommand(commandType, dataBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -1,11 +1,16 @@
|
||||
namespace Azaion.CommonSecurity.DTO;
|
||||
|
||||
public class PythonConfig
|
||||
public abstract class ExternalClientConfig
|
||||
{
|
||||
public string ZeroMqHost { get; set; } = "";
|
||||
public int ZeroMqPort { get; set; }
|
||||
public double OneTryTimeoutSeconds { get; set; }
|
||||
public int RetryCount {get;set;}
|
||||
}
|
||||
|
||||
public class InferenceClientConfig : ExternalClientConfig
|
||||
{
|
||||
public string ResourcesFolder { get; set; } = "";
|
||||
}
|
||||
|
||||
public class GpsDeniedClientConfig : ExternalClientConfig;
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
public class SecureAppConfig
|
||||
{
|
||||
public PythonConfig PythonConfig { get; set; } = null!;
|
||||
public InferenceClientConfig InferenceClientConfig { get; set; } = null!;
|
||||
public GpsDeniedClientConfig GpsDeniedClientConfig { get; set; } = null!;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Azaion.CommonSecurity;
|
||||
using Azaion.CommonSecurity.DTO;
|
||||
|
||||
namespace Azaion.CommonSecurity;
|
||||
|
||||
public class SecurityConstants
|
||||
{
|
||||
@@ -6,13 +8,36 @@ public class SecurityConstants
|
||||
|
||||
public const string DUMMY_DIR = "dummy";
|
||||
|
||||
#region PythonConfig
|
||||
public const string AZAION_INFERENCE_PATH = "azaion-inference.exe";
|
||||
#region ExternalClientsConfig
|
||||
public const string EXTERNAL_INFERENCE_PATH = "azaion-inference.exe";
|
||||
public const string EXTERNAL_GPS_DENIED_PATH = "image-matcher.exe";
|
||||
|
||||
public const string DEFAULT_ZMQ_INFERENCE_HOST = "127.0.0.1";
|
||||
public const int DEFAULT_ZMQ_INFERENCE_PORT = 5227;
|
||||
|
||||
public const string DEFAULT_ZMQ_GPS_DENIED_HOST = "127.0.0.1";
|
||||
public const int DEFAULT_ZMQ_GPS_DENIED_PORT = 5227;
|
||||
|
||||
public const string DEFAULT_ZMQ_HOST = "127.0.0.1";
|
||||
public const int DEFAULT_ZMQ_PORT = 5127;
|
||||
public const int DEFAULT_RETRY_COUNT = 25;
|
||||
public const int DEFAULT_TIMEOUT_SECONDS = 5;
|
||||
|
||||
#endregion PythonConfig
|
||||
public static readonly SecureAppConfig DefaultSecureAppConfig = new()
|
||||
{
|
||||
InferenceClientConfig = new InferenceClientConfig
|
||||
{
|
||||
ZeroMqHost = DEFAULT_ZMQ_INFERENCE_HOST,
|
||||
ZeroMqPort = DEFAULT_ZMQ_INFERENCE_PORT,
|
||||
OneTryTimeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
|
||||
RetryCount = DEFAULT_RETRY_COUNT,
|
||||
ResourcesFolder = ""
|
||||
},
|
||||
GpsDeniedClientConfig = new GpsDeniedClientConfig
|
||||
{
|
||||
ZeroMqHost = DEFAULT_ZMQ_GPS_DENIED_HOST,
|
||||
ZeroMqPort = DEFAULT_ZMQ_GPS_DENIED_PORT,
|
||||
OneTryTimeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
|
||||
RetryCount = DEFAULT_RETRY_COUNT,
|
||||
}
|
||||
};
|
||||
#endregion ExternalClientsConfig
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Azaion.CommonSecurity.DTO;
|
||||
using Azaion.CommonSecurity.DTO.Commands;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Azaion.CommonSecurity.Services;
|
||||
|
||||
public interface IAuthProvider
|
||||
{
|
||||
void Login(ApiCredentials credentials);
|
||||
User CurrentUser { get; }
|
||||
}
|
||||
|
||||
public class AuthProvider([FromKeyedServices(SecurityConstants.EXTERNAL_INFERENCE_PATH)] IExternalClient externalClient) : IAuthProvider
|
||||
{
|
||||
public User CurrentUser { get; private set; } = null!;
|
||||
|
||||
public void Login(ApiCredentials credentials)
|
||||
{
|
||||
externalClient.Send(RemoteCommand.Create(CommandType.Login, credentials));
|
||||
var user = externalClient.Get<User>();
|
||||
if (user == null)
|
||||
throw new Exception("Can't get user from Auth provider");
|
||||
|
||||
CurrentUser = user;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Azaion.CommonSecurity.DTO;
|
||||
using Azaion.CommonSecurity.DTO.Commands;
|
||||
using MessagePack;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
|
||||
namespace Azaion.CommonSecurity.Services;
|
||||
|
||||
public interface IExternalClient
|
||||
{
|
||||
void Stop();
|
||||
|
||||
void Send(RemoteCommand create);
|
||||
T? Get<T>(int retries = 24, int tryTimeoutSeconds = 5, CancellationToken ct = default) where T : class;
|
||||
byte[]? GetBytes(int retries = 24, int tryTimeoutSeconds = 5, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
public abstract class BaseZeroMqExternalClient : IExternalClient
|
||||
{
|
||||
private readonly DealerSocket _dealer = new();
|
||||
private readonly Guid _clientId = Guid.NewGuid();
|
||||
|
||||
private readonly ExternalClientConfig _externalClientConfig;
|
||||
|
||||
protected abstract string ClientPath { get; }
|
||||
|
||||
protected BaseZeroMqExternalClient(ExternalClientConfig config)
|
||||
{
|
||||
_externalClientConfig = config;
|
||||
Start();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var process = new Process();
|
||||
process.StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = ClientPath
|
||||
//Arguments = $"-e {credentials.Email} -p {credentials.Password} -f {apiConfig.ResourcesFolder}",
|
||||
//RedirectStandardOutput = true,
|
||||
//RedirectStandardError = true,
|
||||
//CreateNoWindow = true
|
||||
};
|
||||
|
||||
process.OutputDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
|
||||
process.ErrorDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
|
||||
process.Start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
|
||||
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
|
||||
_dealer.Connect($"tcp://{_externalClientConfig.ZeroMqHost}:{_externalClientConfig.ZeroMqPort}");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (!_dealer.IsDisposed)
|
||||
{
|
||||
_dealer.SendFrame(MessagePackSerializer.Serialize(new RemoteCommand(CommandType.Exit)));
|
||||
_dealer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(RemoteCommand command)
|
||||
{
|
||||
_dealer.SendFrame(MessagePackSerializer.Serialize(command));
|
||||
}
|
||||
|
||||
public T? Get<T>(int retries = 24, int tryTimeoutSeconds = 5, CancellationToken ct = default) where T : class
|
||||
{
|
||||
var bytes = GetBytes(retries, tryTimeoutSeconds, ct);
|
||||
return bytes != null ? MessagePackSerializer.Deserialize<T>(bytes, cancellationToken: ct) : null;
|
||||
}
|
||||
|
||||
public byte[]? GetBytes(int retries = 24, int tryTimeoutSeconds = 5, CancellationToken ct = default)
|
||||
{
|
||||
var tryNum = 0;
|
||||
while (!ct.IsCancellationRequested && tryNum++ < retries)
|
||||
{
|
||||
if (!_dealer.TryReceiveFrameBytes(TimeSpan.FromSeconds(tryTimeoutSeconds), out var bytes))
|
||||
continue;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
if (!ct.IsCancellationRequested)
|
||||
throw new Exception($"Unable to get bytes after {tryNum} retries, {tryTimeoutSeconds} seconds each");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class InferenceExternalClient(IOptions<InferenceClientConfig> inferenceClientConfig)
|
||||
: BaseZeroMqExternalClient(inferenceClientConfig.Value)
|
||||
{
|
||||
protected override string ClientPath => SecurityConstants.EXTERNAL_INFERENCE_PATH;
|
||||
}
|
||||
|
||||
public class GpsDeniedExternalClient(IOptions<GpsDeniedClientConfig> gpsDeniedClientConfig)
|
||||
: BaseZeroMqExternalClient(gpsDeniedClientConfig.Value)
|
||||
{
|
||||
protected override string ClientPath => SecurityConstants.EXTERNAL_GPS_DENIED_PATH;
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using Azaion.CommonSecurity.DTO;
|
||||
using Azaion.CommonSecurity.DTO.Commands;
|
||||
using MessagePack;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
|
||||
namespace Azaion.CommonSecurity.Services;
|
||||
|
||||
public interface IResourceLoader
|
||||
{
|
||||
MemoryStream LoadFileFromPython(string fileName, string? folder = null);
|
||||
void StopPython();
|
||||
}
|
||||
|
||||
public interface IAuthProvider
|
||||
{
|
||||
User CurrentUser { get; }
|
||||
}
|
||||
|
||||
public class PythonResourceLoader : IResourceLoader, IAuthProvider
|
||||
{
|
||||
private readonly DealerSocket _dealer = new();
|
||||
private readonly Guid _clientId = Guid.NewGuid();
|
||||
|
||||
public User CurrentUser { get; set; } = null!;
|
||||
|
||||
public PythonResourceLoader(PythonConfig config)
|
||||
{
|
||||
StartPython();
|
||||
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
|
||||
_dealer.Connect($"tcp://{config.ZeroMqHost}:{config.ZeroMqPort}");
|
||||
}
|
||||
|
||||
private void StartPython()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var process = new Process();
|
||||
process.StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = SecurityConstants.AZAION_INFERENCE_PATH,
|
||||
//Arguments = $"-e {credentials.Email} -p {credentials.Password} -f {apiConfig.ResourcesFolder}",
|
||||
//UseShellExecute = false,
|
||||
//RedirectStandardOutput = true,
|
||||
// RedirectStandardError = true,
|
||||
//CreateNoWindow = true
|
||||
};
|
||||
|
||||
process.OutputDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
|
||||
process.ErrorDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
|
||||
process.Start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Login(ApiCredentials credentials)
|
||||
{
|
||||
_dealer.SendFrame(RemoteCommand.Serialize(CommandType.Login, credentials));
|
||||
var user = _dealer.Get<User>();
|
||||
if (user == null)
|
||||
throw new Exception("Can't get user from Auth provider");
|
||||
|
||||
CurrentUser = user;
|
||||
}
|
||||
|
||||
public void StopPython()
|
||||
{
|
||||
if (!_dealer.IsDisposed)
|
||||
{
|
||||
_dealer.SendFrame(MessagePackSerializer.Serialize(new RemoteCommand(CommandType.Exit)));
|
||||
_dealer.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public MemoryStream LoadFileFromPython(string fileName, string? folder = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_dealer.SendFrame(RemoteCommand.Serialize(CommandType.Load, new LoadFileData(fileName, folder)));
|
||||
|
||||
if (!_dealer.TryReceiveFrameBytes(TimeSpan.FromSeconds(300), out var bytes))
|
||||
throw new Exception($"Unable to receive {fileName}");
|
||||
|
||||
return new MemoryStream(bytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to load fil0e '{fileName}': {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Azaion.CommonSecurity.DTO.Commands;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Azaion.CommonSecurity.Services;
|
||||
|
||||
public interface IResourceLoader
|
||||
{
|
||||
MemoryStream LoadFile(string fileName, string? folder = null);
|
||||
}
|
||||
|
||||
public class ResourceLoader([FromKeyedServices(SecurityConstants.EXTERNAL_INFERENCE_PATH)] IExternalClient externalClient) : IResourceLoader
|
||||
{
|
||||
public MemoryStream LoadFile(string fileName, string? folder = null)
|
||||
{
|
||||
externalClient.Send(RemoteCommand.Create(CommandType.Load, new LoadFileData(fileName, folder)));
|
||||
var bytes = externalClient.GetBytes();
|
||||
if (bytes == null)
|
||||
throw new Exception($"Unable to receive {fileName}");
|
||||
|
||||
return new MemoryStream(bytes);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using MessagePack;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
|
||||
namespace Azaion.CommonSecurity;
|
||||
|
||||
public static class ZeroMqExtensions
|
||||
{
|
||||
public static T? Get<T>(this DealerSocket dealer, Func<byte[], bool>? shouldInterceptFn = null, int retries = 24, int tryTimeoutSeconds = 5, CancellationToken ct = default) where T : class
|
||||
{
|
||||
var tryNum = 0;
|
||||
while (!ct.IsCancellationRequested && tryNum++ < retries)
|
||||
{
|
||||
if (!dealer.TryReceiveFrameBytes(TimeSpan.FromSeconds(tryTimeoutSeconds), out var bytes))
|
||||
continue;
|
||||
|
||||
if (shouldInterceptFn != null && shouldInterceptFn(bytes))
|
||||
return null;
|
||||
|
||||
return MessagePackSerializer.Deserialize<T>(bytes);
|
||||
}
|
||||
|
||||
if (!ct.IsCancellationRequested)
|
||||
throw new Exception($"Unable to get {typeof(T).Name} after {tryNum} retries, {tryTimeoutSeconds} seconds each");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user