add azaion loader

This commit is contained in:
Alex Bezdieniezhnykh
2025-06-01 19:16:49 +03:00
parent 2584b4f125
commit 500db31142
54 changed files with 629 additions and 291 deletions
+4 -11
View File
@@ -12,19 +12,12 @@
<PackageReference Include="libc.translation" Version="7.1.1" /> <PackageReference Include="libc.translation" Version="7.1.1" />
<PackageReference Include="LibVLCSharp" Version="3.9.1" /> <PackageReference Include="LibVLCSharp" Version="3.9.1" />
<PackageReference Include="LibVLCSharp.WPF" Version="3.9.1" /> <PackageReference Include="LibVLCSharp.WPF" Version="3.9.1" />
<PackageReference Include="MediatR" Version="12.4.1" /> <PackageReference Include="MediatR" Version="12.5.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.3" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="RangeTree" Version="3.0.1" /> <PackageReference Include="RangeTree" Version="3.0.1" />
<PackageReference Include="Serilog" Version="4.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="SkiaSharp" Version="2.88.9" /> <PackageReference Include="SkiaSharp" Version="2.88.9" />
<PackageReference Include="System.Data.SQLite" Version="1.0.119" /> <PackageReference Include="System.Data.SQLite" Version="1.0.119" />
<PackageReference Include="System.Data.SQLite.EF6" Version="1.0.119" /> <PackageReference Include="System.Data.SQLite.EF6" Version="1.0.119" />
+5 -5
View File
@@ -9,12 +9,12 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CsvHelper" Version="33.0.1" /> <PackageReference Include="CsvHelper" Version="33.0.1" />
<PackageReference Include="linq2db.SQLite" Version="5.4.1" /> <PackageReference Include="linq2db.SQLite" Version="5.4.1" />
<PackageReference Include="MediatR" Version="12.4.1" /> <PackageReference Include="MediatR" Version="12.5.0" />
<PackageReference Include="MessagePack" Version="3.1.0" /> <PackageReference Include="MessagePack" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Polly" Version="8.5.2" /> <PackageReference Include="Polly" Version="8.5.2" />
<PackageReference Include="RabbitMQ.Stream.Client" Version="1.8.9" /> <PackageReference Include="RabbitMQ.Stream.Client" Version="1.8.9" />
+1 -1
View File
@@ -129,7 +129,7 @@ public class GpsMatcherClient : IGpsMatcherClient
if (response != "OK") if (response != "OK")
{ {
_logger.LogError(response); _logger.LogError(response);
await _mediator.Publish(new SetStatusTextEvent(response, true)); await _mediator.Publish(new SetStatusTextEvent(response ?? "", true));
} }
} }
@@ -7,15 +7,17 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" /> <PackageReference Include="LazyCache.AspNetCore" Version="2.4.0" />
<PackageReference Include="MediatR" Version="12.4.1" /> <PackageReference Include="MediatR" Version="12.5.0" />
<PackageReference Include="MessagePack" Version="3.1.0" /> <PackageReference Include="MessagePack" Version="3.1.0" />
<PackageReference Include="MessagePack.Annotations" 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.DependencyInjection.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="9.0.5" />
<PackageReference Include="NetMQ" Version="4.0.1.13" /> <PackageReference Include="NetMQ" Version="4.0.1.13" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
</ItemGroup> </ItemGroup>
+7 -4
View File
@@ -1,13 +1,16 @@
using MessagePack; using CommandLine;
using MessagePack;
namespace Azaion.CommonSecurity.DTO; namespace Azaion.CommonSecurity.DTO;
[MessagePackObject] [MessagePackObject]
public class ApiCredentials(string email, string password) : EventArgs public class ApiCredentials : EventArgs
{ {
[Key(nameof(Email))] [Key(nameof(Email))]
public string Email { get; set; } = email; [Option('e', "email", Required = true, HelpText = "User Email")]
public string Email { get; set; } = null!;
[Key(nameof(Password))] [Key(nameof(Password))]
public string Password { get; set; } = password; [Option('p', "pass", Required = true, HelpText = "User Password")]
public string Password { get; set; } = null!;
} }
@@ -4,8 +4,11 @@ public abstract class ExternalClientConfig
{ {
public string ZeroMqHost { get; set; } = ""; public string ZeroMqHost { get; set; } = "";
public int ZeroMqPort { get; set; } public int ZeroMqPort { get; set; }
public double OneTryTimeoutSeconds { get; set; } }
public int RetryCount {get;set;}
public class LoaderClientConfig : ExternalClientConfig
{
public string ApiUrl { get; set; } = null!;
} }
public class InferenceClientConfig : ExternalClientConfig public class InferenceClientConfig : ExternalClientConfig
@@ -1,7 +1,8 @@
namespace Azaion.CommonSecurity.DTO; namespace Azaion.CommonSecurity.DTO;
public class SecureAppConfig public class InitConfig
{ {
public LoaderClientConfig LoaderClientConfig { get; set; } = null!;
public InferenceClientConfig InferenceClientConfig { get; set; } = null!; public InferenceClientConfig InferenceClientConfig { get; set; } = null!;
public GpsDeniedClientConfig GpsDeniedClientConfig { get; set; } = null!; public GpsDeniedClientConfig GpsDeniedClientConfig { get; set; } = null!;
public DirectoriesConfig DirectoriesConfig { get; set; } = null!; public DirectoriesConfig DirectoriesConfig { get; set; } = null!;
+15 -9
View File
@@ -8,8 +8,14 @@ public class SecurityConstants
public const string DUMMY_DIR = "dummy"; public const string DUMMY_DIR = "dummy";
private const string DEFAULT_API_URL = "https://api.azaion.com";
#region ExternalClientsConfig #region ExternalClientsConfig
private const string DEFAULT_ZMQ_LOADER_HOST = "127.0.0.1";
private const int DEFAULT_ZMQ_LOADER_PORT = 5025;
public const string EXTERNAL_LOADER_PATH = "azaion-loader.exe";
public const string EXTERNAL_INFERENCE_PATH = "azaion-inference.exe"; public const string EXTERNAL_INFERENCE_PATH = "azaion-inference.exe";
public const string EXTERNAL_GPS_DENIED_FOLDER = "gps-denied"; public const string EXTERNAL_GPS_DENIED_FOLDER = "gps-denied";
public static readonly string ExternalGpsDeniedPath = Path.Combine(EXTERNAL_GPS_DENIED_FOLDER, "image-matcher.exe"); public static readonly string ExternalGpsDeniedPath = Path.Combine(EXTERNAL_GPS_DENIED_FOLDER, "image-matcher.exe");
@@ -20,9 +26,6 @@ public class SecurityConstants
public const string DEFAULT_ZMQ_GPS_DENIED_HOST = "127.0.0.1"; public const string DEFAULT_ZMQ_GPS_DENIED_HOST = "127.0.0.1";
public const int DEFAULT_ZMQ_GPS_DENIED_PORT = 5227; public const int DEFAULT_ZMQ_GPS_DENIED_PORT = 5227;
public const int DEFAULT_RETRY_COUNT = 25;
public const int DEFAULT_TIMEOUT_SECONDS = 5;
# region Cache keys # region Cache keys
public const string CURRENT_USER_CACHE_KEY = "CurrentUser"; public const string CURRENT_USER_CACHE_KEY = "CurrentUser";
@@ -30,21 +33,24 @@ public class SecurityConstants
# endregion # endregion
public static readonly SecureAppConfig DefaultSecureAppConfig = new() public static readonly InitConfig DefaultInitConfig = new()
{ {
LoaderClientConfig = new LoaderClientConfig
{
ZeroMqHost = DEFAULT_ZMQ_LOADER_HOST,
ZeroMqPort = DEFAULT_ZMQ_LOADER_PORT,
ApiUrl = DEFAULT_API_URL
},
InferenceClientConfig = new InferenceClientConfig InferenceClientConfig = new InferenceClientConfig
{ {
ZeroMqHost = DEFAULT_ZMQ_INFERENCE_HOST, ZeroMqHost = DEFAULT_ZMQ_INFERENCE_HOST,
ZeroMqPort = DEFAULT_ZMQ_INFERENCE_PORT, ZeroMqPort = DEFAULT_ZMQ_INFERENCE_PORT,
OneTryTimeoutSeconds = DEFAULT_TIMEOUT_SECONDS, ApiUrl = DEFAULT_API_URL
RetryCount = DEFAULT_RETRY_COUNT
}, },
GpsDeniedClientConfig = new GpsDeniedClientConfig GpsDeniedClientConfig = new GpsDeniedClientConfig
{ {
ZeroMqHost = DEFAULT_ZMQ_GPS_DENIED_HOST, ZeroMqHost = DEFAULT_ZMQ_GPS_DENIED_HOST,
ZeroMqPort = DEFAULT_ZMQ_GPS_DENIED_PORT, ZeroMqPort = DEFAULT_ZMQ_GPS_DENIED_PORT
OneTryTimeoutSeconds = DEFAULT_TIMEOUT_SECONDS,
RetryCount = DEFAULT_RETRY_COUNT,
}, },
DirectoriesConfig = new DirectoriesConfig DirectoriesConfig = new DirectoriesConfig
{ {
+53 -28
View File
@@ -1,30 +1,30 @@
using System.Diagnostics; using System.Diagnostics;
using System.Text;
using Azaion.CommonSecurity.DTO; using Azaion.CommonSecurity.DTO;
using MediatR; using Azaion.CommonSecurity.DTO.Commands;
using Microsoft.Extensions.Logging; using MessagePack;
using Microsoft.Extensions.Options; using NetMQ;
using NetMQ.Sockets;
using Serilog;
using Exception = System.Exception;
namespace Azaion.CommonSecurity.Services; namespace Azaion.CommonSecurity.Services;
public class LoaderClient public class LoaderClient(LoaderClientConfig config, ILogger logger, CancellationToken ct = default)
{ {
private readonly IMediator _mediator; private readonly DealerSocket _dealer = new();
private readonly ILogger<LoaderClient> _logger; private readonly Guid _clientId = Guid.NewGuid();
public LoaderClient(IMediator mediator, IOptions<LoaderClientConfig> config, ILogger<LoaderClient> logger) public void StartClient()
{ {
_mediator = mediator;
_logger = logger;
try try
{ {
using var process = new Process(); using var process = new Process();
process.StartInfo = new ProcessStartInfo process.StartInfo = new ProcessStartInfo
{ {
FileName = SecurityConstants.LoaderPath, FileName = SecurityConstants.EXTERNAL_LOADER_PATH,
Arguments = $"--port {config.Value.ZeroMqPort} --api {config.Value.ApiUrl}", Arguments = $"--port {config.ZeroMqPort} --api {config.ApiUrl}",
//RedirectStandardOutput = true, CreateNoWindow = true
//RedirectStandardError = true,
//CreateNoWindow = true
}; };
process.OutputDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); }; process.OutputDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
@@ -33,19 +33,44 @@ public class LoaderClient
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e); logger.Error(e.Message);
//throw; throw;
} }
}
_requestAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqPort}";
_requestSocket.Connect(_requestAddress); public void Connect()
{
_subscriberAddress = $"tcp://{gpsConfig.Value.ZeroMqHost}:{gpsConfig.Value.ZeroMqReceiverPort}"; _dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
_subscriberSocket.Connect(_subscriberAddress); _dealer.Connect($"tcp://{config.ZeroMqHost}:{config.ZeroMqPort}");
_subscriberSocket.Subscribe(""); }
_subscriberSocket.ReceiveReady += async (sender, e) => await ProcessClientCommand(sender, e);
public void Send(RemoteCommand command) =>
_poller.Add(_subscriberSocket); _dealer.SendFrame(MessagePackSerializer.Serialize(command));
_poller.RunAsync();
public MemoryStream LoadFile(string filename, string folder)
{
try
{
Send(RemoteCommand.Create(CommandType.Load, new LoadFileData(filename, folder)));
var retries = 10;
var tryNum = 0;
while (!ct.IsCancellationRequested && tryNum++ < retries)
{
if (!_dealer.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(150), out var bytes))
continue;
var resultCommand = MessagePackSerializer.Deserialize<RemoteCommand>(bytes, cancellationToken: ct);
if (resultCommand.Data?.Length == 0)
throw new Exception($"Can't load {filename}. Returns 0 bytes");
return new MemoryStream(resultCommand.Data!);
}
throw new Exception($"Can't load file {filename} after {retries} retries");
}
catch (Exception e)
{
logger.Error(e, e.Message);
throw;
}
} }
} }
+2 -2
View File
@@ -16,8 +16,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.5" />
<PackageReference Include="ScottPlot.WPF" Version="5.0.46" /> <PackageReference Include="ScottPlot.WPF" Version="5.0.46" />
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.0" /> <PackageReference Include="VirtualizingWrapPanel" Version="2.1.0" />
</ItemGroup> </ItemGroup>
-2
View File
@@ -26,8 +26,6 @@ tmp_ret = collect_all('pycuda')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('pynvml') tmp_ret = collect_all('pynvml')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('boto3')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('jwt') tmp_ret = collect_all('jwt')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
-1
View File
@@ -33,7 +33,6 @@ venv\Scripts\pyinstaller --name=azaion-inference ^
--collect-all tensorrt ^ --collect-all tensorrt ^
--collect-all pycuda ^ --collect-all pycuda ^
--collect-all pynvml ^ --collect-all pynvml ^
--collect-all boto3 ^
--collect-all jwt ^ --collect-all jwt ^
--hidden-import constants ^ --hidden-import constants ^
--hidden-import annotation ^ --hidden-import annotation ^
-5
View File
@@ -2,12 +2,7 @@ import time
cdef str CONFIG_FILE = "config.yaml" # Port for the zmq cdef str CONFIG_FILE = "config.yaml" # Port for the zmq
cdef int QUEUE_MAXSIZE = 1000 # Maximum size of the command queue
cdef str COMMANDS_QUEUE = "azaion-commands"
cdef str ANNOTATIONS_QUEUE = "azaion-annotations"
cdef str QUEUE_CONFIG_FILENAME = "secured-config.json" cdef str QUEUE_CONFIG_FILENAME = "secured-config.json"
cdef str AI_ONNX_MODEL_FILE = "azaion.onnx" cdef str AI_ONNX_MODEL_FILE = "azaion.onnx"
cdef str CDN_CONFIG = "cdn.yaml" cdef str CDN_CONFIG = "cdn.yaml"
+2 -5
View File
@@ -6,27 +6,24 @@ from threading import Thread
import yaml import yaml
from api_client cimport ApiClient
from annotation cimport Annotation from annotation cimport Annotation
from inference cimport Inference from inference cimport Inference
from remote_command cimport RemoteCommand, CommandType from remote_command cimport RemoteCommand, CommandType
from remote_command_handler cimport RemoteCommandHandler from remote_command_handler cimport RemoteCommandHandler
from credentials cimport Credentials
from file_data cimport FileData
cdef class CommandProcessor: cdef class CommandProcessor:
cdef ApiClient api_client
cdef RemoteCommandHandler remote_handler cdef RemoteCommandHandler remote_handler
cdef object inference_queue cdef object inference_queue
cdef bint running cdef bint running
cdef Inference inference cdef Inference inference
def __init__(self, int zmq_port, str api_url): def __init__(self, int zmq_port, str api_url):
self.api_client = ApiClient(api_url)
self.remote_handler = RemoteCommandHandler(zmq_port, self.on_command) self.remote_handler = RemoteCommandHandler(zmq_port, self.on_command)
self.inference_queue = Queue(maxsize=constants.QUEUE_MAXSIZE) self.inference_queue = Queue(maxsize=constants.QUEUE_MAXSIZE)
self.remote_handler.start() self.remote_handler.start()
self.running = True self.running = True
#TODO: replace api_client to azaion_loader.exe call
self.inference = Inference(self.api_client, self.on_annotation) self.inference = Inference(self.api_client, self.on_annotation)
def start(self): def start(self):
@@ -16,5 +16,6 @@ cdef class ApiClient:
cdef load_bytes(self, str filename, str folder) cdef load_bytes(self, str filename, str folder)
cdef upload_file(self, str filename, bytes resource, str folder) cdef upload_file(self, str filename, bytes resource, str folder)
cdef load_big_file_cdn(self, str folder, str big_part)
cdef load_big_small_resource(self, str resource_name, str folder, str key) cdef load_big_small_resource(self, str resource_name, str folder, str key)
cdef upload_big_small_resource(self, bytes resource, str resource_name, str folder, str key) cdef upload_big_small_resource(self, bytes resource, str resource_name, str folder, str key)
@@ -159,7 +159,7 @@ cdef class ApiClient:
print('Local file doesnt match with api file, old version') print('Local file doesnt match with api file, old version')
remote_bytes_big = self.load_big_file_cdn(folder, big_part) remote_bytes_big = self.load_big_file_cdn(folder, big_part)
return Security.decrypt_to(encrypted_bytes_small + remote_bytes_big) return Security.decrypt_to(encrypted_bytes_small + remote_bytes_big, key)
cdef upload_big_small_resource(self, bytes resource, str resource_name, str folder, str key): cdef upload_big_small_resource(self, bytes resource, str resource_name, str folder, str key):
cdef str big_part_name = f'{resource_name}.big' cdef str big_part_name = f'{resource_name}.big'
+24
View File
@@ -0,0 +1,24 @@
echo Build Cython app
set CURRENT_DIR=%cd%
REM Change to the parent directory of the current location
cd /d %~dp0
echo remove dist folder:
if exist dist rmdir dist /s /q
if exist build rmdir build /s /q
echo install python and dependencies
if not exist venv (
python -m venv venv
)
venv\Scripts\python -m pip install --upgrade pip
venv\Scripts\pip install -r requirements.txt
venv\Scripts\pip install --upgrade pyinstaller pyinstaller-hooks-contrib
venv\Scripts\python setup.py build_ext --inplace
echo install azaion-loader
venv\Scripts\pyinstaller --name=azaion-loader ^
--collect-all boto3 ^
+17
View File
@@ -0,0 +1,17 @@
cdef str CONFIG_FILE # Port for the zmq
cdef int QUEUE_MAXSIZE # Maximum size of the command queue
cdef str COMMANDS_QUEUE # Name of the commands queue in rabbit
cdef str ANNOTATIONS_QUEUE # Name of the annotations queue in rabbit
cdef str QUEUE_CONFIG_FILENAME # queue config filename to load from api
cdef str AI_ONNX_MODEL_FILE
cdef str CDN_CONFIG
cdef str MODELS_FOLDER
cdef int SMALL_SIZE_KB
cdef log(str log_message, bytes client_id=*)
+16
View File
@@ -0,0 +1,16 @@
import time
cdef str CONFIG_FILE = "config.yaml" # Port for the zmq
cdef str QUEUE_CONFIG_FILENAME = "secured-config.json"
cdef str AI_ONNX_MODEL_FILE = "azaion.onnx"
cdef str CDN_CONFIG = "cdn.yaml"
cdef str MODELS_FOLDER = "models"
cdef int SMALL_SIZE_KB = 3
cdef log(str log_message, bytes client_id=None):
local_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
client_str = '' if client_id is None else f' {client_id}'
print(f'[{local_time}{client_str}]: {log_message}')
+61
View File
@@ -0,0 +1,61 @@
import threading
from threading import Thread
import traceback
from credentials cimport Credentials
from remote_command cimport RemoteCommand, CommandType
from remote_command_handler cimport RemoteCommandHandler
from file_data cimport FileData
from api_client cimport ApiClient
cdef class CommandProcessor:
cdef RemoteCommandHandler remote_handler
cdef ApiClient api_client
cdef bint running
cdef object shutdown_event
def __init__(self, int zmq_port, str api_url):
self.api_client = ApiClient(api_url)
self.shutdown_event = threading.Event()
self.remote_handler = RemoteCommandHandler(zmq_port, self.on_command)
self.remote_handler.start()
self.running = True
def start(self):
while self.running:
try:
while not self.shutdown_event.is_set():
self.shutdown_event.wait(timeout=1.0)
except Exception as e:
traceback.print_exc()
print('EXIT!')
cdef on_command(self, RemoteCommand command):
try:
if command.command_type == CommandType.LOGIN:
self.api_client.set_credentials(Credentials.from_msgpack(command.data))
elif command.command_type == CommandType.LOAD:
self.load_file(command)
elif command.command_type == CommandType.EXIT:
t = Thread(target=self.stop) # non-block worker:
t.start()
else:
pass
except Exception as e:
print(f"Error handling client: {e}")
cdef load_file(self, RemoteCommand command):
cdef RemoteCommand response
cdef FileData file_data
cdef bytes file_bytes
try:
file_data = FileData.from_msgpack(command.data)
file_bytes = self.api_client.load_bytes(file_data.filename, file_data.folder)
response = RemoteCommand(CommandType.DATA_BYTES, file_bytes)
except Exception as e:
response = RemoteCommand(CommandType.DATA_BYTES, None, str(e))
self.remote_handler.send(command.client_id, response.serialize())
def stop(self):
self.shutdown_event.set()
self.remote_handler.stop()
self.running = False
+22
View File
@@ -0,0 +1,22 @@
cdef enum CommandType:
LOGIN = 10
LOAD = 20
DATA_BYTES = 25
INFERENCE = 30
INFERENCE_DATA = 35
STOP_INFERENCE = 40
AI_AVAILABILITY_CHECK = 80
AI_AVAILABILITY_RESULT = 85
ERROR = 90
EXIT = 100
cdef class RemoteCommand:
cdef public bytes client_id
cdef CommandType command_type
cdef str message
cdef bytes data
@staticmethod
cdef from_msgpack(bytes data)
cdef bytes serialize(self)
+35
View File
@@ -0,0 +1,35 @@
import msgpack
cdef class RemoteCommand:
def __init__(self, CommandType command_type, bytes data, str message=None):
self.command_type = command_type
self.data = data
self.message = message
def __str__(self):
command_type_names = {
10: "LOGIN",
20: "LOAD",
25: "DATA_BYTES",
30: "INFERENCE",
35: "INFERENCE_DATA",
40: "STOP_INFERENCE",
80: "AI_AVAILABILITY_CHECK",
85: "AI_AVAILABILITY_RESULT",
90: "ERROR",
100: "EXIT"
}
data_str = f'{len(self.data)} bytes' if self.data else ''
return f'{command_type_names[self.command_type]} ({data_str})'
@staticmethod
cdef from_msgpack(bytes data):
unpacked = msgpack.unpackb(data, strict_map_key=False)
return RemoteCommand(unpacked.get("CommandType"), unpacked.get("Data"), unpacked.get("Message"))
cdef bytes serialize(self):
return msgpack.packb({
"CommandType": self.command_type,
"Data": self.data,
"Message": self.message
})
+16
View File
@@ -0,0 +1,16 @@
cdef class RemoteCommandHandler:
cdef object _context
cdef object _router
cdef object _dealer
cdef object _control
cdef object _shutdown_event
cdef object _on_command
cdef object _proxy_thread
cdef object _workers
cdef start(self)
cdef _proxy_loop(self)
cdef _worker_loop(self)
cdef send(self, bytes client_id, bytes data)
cdef stop(self)
+94
View File
@@ -0,0 +1,94 @@
import time
import zmq
from threading import Thread, Event
from remote_command cimport RemoteCommand
cimport constants
import yaml
cdef class RemoteCommandHandler:
def __init__(self, int zmq_port, object on_command):
self._on_command = on_command
self._context = zmq.Context.instance()
self._router = self._context.socket(zmq.ROUTER)
self._router.setsockopt(zmq.LINGER, 0)
self._router.bind(f'tcp://*:{zmq_port}')
self._dealer = self._context.socket(zmq.DEALER)
self._dealer.setsockopt(zmq.LINGER, 0)
self._dealer.bind("inproc://backend")
self._control = self._context.socket(zmq.PAIR)
self._control.bind("inproc://control")
self._shutdown_event = Event()
self._proxy_thread = Thread(target=self._proxy_loop, daemon=True)
self._workers = []
for _ in range(4): # 4 worker threads
worker = Thread(target=self._worker_loop, daemon=True)
self._workers.append(worker)
print(f'Listening to commands on port {zmq_port}...')
cdef start(self):
self._proxy_thread.start()
for worker in self._workers:
worker.start()
cdef _proxy_loop(self):
try:
zmq.proxy_steerable(self._router, self._dealer, control=self._control)
except zmq.error.ZMQError as e:
if self._shutdown_event.is_set():
print("Shutdown, exit proxy loop.")
else:
raise
cdef _worker_loop(self):
worker_socket = self._context.socket(zmq.DEALER)
worker_socket.setsockopt(zmq.LINGER, 0)
worker_socket.connect("inproc://backend")
poller = zmq.Poller()
poller.register(worker_socket, zmq.POLLIN)
try:
while not self._shutdown_event.is_set():
try:
socks = dict(poller.poll(500))
if worker_socket in socks:
client_id, message = worker_socket.recv_multipart()
cmd = RemoteCommand.from_msgpack(<bytes> message)
cmd.client_id = client_id
constants.log(<str>f'{cmd}', client_id)
self._on_command(cmd)
except Exception as e:
if not self._shutdown_event.is_set():
constants.log(f"Worker error: {e}")
import traceback
traceback.print_exc()
finally:
worker_socket.close()
cdef send(self, bytes client_id, bytes data):
self._router.send_multipart([client_id, data])
# with self._context.socket(zmq.DEALER) as socket:
# socket.connect("inproc://backend")
# socket.send_multipart([client_id, data])
# # constants.log(<str>f'Sent {len(data)} bytes.', client_id)
cdef stop(self):
self._shutdown_event.set()
try:
self._control.send(b"TERMINATE", flags=zmq.DONTWAIT)
except zmq.error.ZMQError:
pass
self._router.close(linger=0)
self._dealer.close(linger=0)
self._control.close(linger=0)
self._proxy_thread.join(timeout=2)
while any(w.is_alive() for w in self._workers):
time.sleep(0.1)
self._context.term()
+9
View File
@@ -0,0 +1,9 @@
pyinstaller
Cython
psutil
msgpack
pyjwt
zmq
requests
pyyaml
boto3
+31
View File
@@ -0,0 +1,31 @@
from setuptools import setup, Extension
from Cython.Build import cythonize
extensions = [
Extension('constants', ['constants.pyx']),
Extension('credentials', ['credentials.pyx']),
Extension('file_data', ['file_data.pyx']),
Extension('hardware_service', ['hardware_service.pyx'], extra_compile_args=["-g"], extra_link_args=["-g"]),
Extension('security', ['security.pyx']),
Extension('remote_command', ['remote_command.pyx']),
Extension('remote_command_handler', ['remote_command_handler.pyx']),
Extension('user', ['user.pyx']),
Extension('cdn_manager', ['cdn_manager.pyx']),
Extension('api_client', ['api_client.pyx']),
Extension('main', ['main.pyx']),
]
setup(
name="azaion.loader",
ext_modules=cythonize(
extensions,
compiler_directives={
"language_level": 3,
"emit_code_comments" : False,
"binding": True,
'boundscheck': False,
'wraparound': False
}
),
zip_safe=False
)
+15
View File
@@ -0,0 +1,15 @@
from main import CommandProcessor
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-p", "--port", type=str, default="5025", help="zero mq port")
parser.add_argument("-a", "--api", type=str, default="https://api.azaion.com", help="api url")
args = parser.parse_args()
processor = CommandProcessor(int(args.port), args.api)
try:
processor.start()
except KeyboardInterrupt:
processor.stop()
+7
View File
@@ -0,0 +1,7 @@
<Application x:Class="Azaion.LoaderUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
+26
View File
@@ -0,0 +1,26 @@
using System.Windows;
using Serilog;
namespace Azaion.LoaderUI;
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Start();
}
private void Start()
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File(
path: "Logs/log.txt",
rollingInterval: RollingInterval.Day)
.CreateLogger();
}
}
+10
View File
@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
+26
View File
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Azaion.CommonSecurity\Azaion.CommonSecurity.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
</ItemGroup>
</Project>
@@ -1,4 +1,4 @@
<Window x:Class="Azaion.Suite.Login" <Window x:Class="Azaion.LoaderUI.Login"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -1,10 +1,9 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using Azaion.Common.DTO;
using Azaion.CommonSecurity.DTO; using Azaion.CommonSecurity.DTO;
namespace Azaion.Suite; namespace Azaion.LoaderUI;
public partial class Login public partial class Login
{ {
@@ -15,20 +14,17 @@ public partial class Login
InitializeComponent(); InitializeComponent();
} }
public event EventHandler<ApiCredentials>? CredentialsEntered;
private void LoginClick(object sender, RoutedEventArgs e) private void LoginClick(object sender, RoutedEventArgs e)
{ {
LoginBtn.Cursor = Cursors.Wait; LoginBtn.Cursor = Cursors.Wait;
Cursor = Cursors.Wait; Cursor = Cursors.Wait;
CredentialsEntered?.Invoke(this, new ApiCredentials(TbEmail.Text, TbPassword.Password));
MainSuiteOpened = true; MainSuiteOpened = true;
Close(); Close();
} }
private void CloseClick(object sender, RoutedEventArgs e) => Close(); private void CloseClick(object sender, RoutedEventArgs e) => Close();
private void MainMouseMove(object sender, MouseEventArgs e) private void MainMouseMove(object sende0r, MouseEventArgs e)
{ {
if (e.OriginalSource is Button || e.OriginalSource is TextBox) if (e.OriginalSource is Button || e.OriginalSource is TextBox)
return; return;
+94 -122
View File
@@ -1,6 +1,5 @@
using System.IO; using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Reflection;
using System.Text; using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
@@ -17,12 +16,12 @@ using Azaion.CommonSecurity.DTO;
using Azaion.CommonSecurity.DTO.Commands; using Azaion.CommonSecurity.DTO.Commands;
using Azaion.CommonSecurity.Services; using Azaion.CommonSecurity.Services;
using Azaion.Dataset; using Azaion.Dataset;
using CommandLine;
using LibVLCSharp.Shared; using LibVLCSharp.Shared;
using MediatR; using MediatR;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Serilog; using Serilog;
@@ -32,131 +31,24 @@ namespace Azaion.Suite;
public partial class App public partial class App
{ {
private IHost _host = null!;
private ILogger<App> _logger = null!;
private IMediator _mediator = null!; private IMediator _mediator = null!;
private FormState _formState = null!; private FormState _formState = null!;
private IHost _host = null!;
private IInferenceClient _inferenceClient = null!;
private Stream _securedConfig = null!;
private Stream _systemConfig = null!;
private static readonly Guid KeyPressTaskId = Guid.NewGuid(); private static readonly Guid KeyPressTaskId = Guid.NewGuid();
private string _loadErrors = "";
private readonly ICache _cache = new MemoryCache(); private readonly ICache _cache = new MemoryCache();
private IAzaionApi _azaionApi = null!; private readonly CancellationTokenSource _mainCTokenSource = new();
private CancellationTokenSource _mainCTokenSource = new();
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{ {
_logger.LogError(e.Exception, e.Exception.Message); Log.Logger.Error(e.Exception, "Unhandled exception");
e.Handled = true; e.Handled = true;
} }
protected override void OnStartup(StartupEventArgs e) protected override void OnStartup(StartupEventArgs e)
{ {
base.OnStartup(e); base.OnStartup(e);
StartLogin();
}
private readonly List<string> _encryptedResources =
[
"Azaion.Annotator",
"Azaion.Dataset"
];
private static SecureAppConfig ReadSecureAppConfig()
{
try
{
if (!File.Exists(SecurityConstants.CONFIG_PATH))
throw new FileNotFoundException(SecurityConstants.CONFIG_PATH);
var configStr = File.ReadAllText(SecurityConstants.CONFIG_PATH);
var config = JsonConvert.DeserializeObject<SecureAppConfig>(configStr);
return config ?? SecurityConstants.DefaultSecureAppConfig;
}
catch (Exception e)
{
Console.WriteLine(e);
return SecurityConstants.DefaultSecureAppConfig;
}
}
private void StartLogin()
{
new ConfigUpdater().CheckConfig();
var secureAppConfig = ReadSecureAppConfig();
var apiDir = secureAppConfig.DirectoriesConfig.ApiResourcesDirectory;
_inferenceClient = new InferenceClient(new OptionsWrapper<InferenceClientConfig>(secureAppConfig.InferenceClientConfig), _mainCTokenSource.Token);
var login = new Login();
var loader = (IResourceLoader)_inferenceClient;
login.CredentialsEntered += (_, credentials) =>
{
_inferenceClient.Send(RemoteCommand.Create(CommandType.Login, credentials));
_azaionApi = new AzaionApi(new HttpClient { BaseAddress = new Uri(secureAppConfig.InferenceClientConfig.ApiUrl) }, _cache, credentials);
try
{
_securedConfig = loader.LoadFile("config.secured.json", apiDir);
_systemConfig = loader.LoadFile("config.system.json", apiDir);
}
catch (Exception e)
{
Console.WriteLine(e);
_securedConfig = new MemoryStream("{}"u8.ToArray());
var systemConfig = new
{
AnnotationConfig = Constants.DefaultAnnotationConfig,
AIRecognitionConfig = Constants.DefaultAIRecognitionConfig,
ThumbnailConfig = Constants.DefaultThumbnailConfig,
};
_systemConfig = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(systemConfig)));
}
AppDomain.CurrentDomain.AssemblyResolve += (_, a) =>
{
var assemblyName = a.Name.Split(',').First();
if (_encryptedResources.Contains(assemblyName))
{
try
{
var stream = loader.LoadFile($"{assemblyName}.dll", apiDir);
return Assembly.Load(stream.ToArray());
}
catch (Exception e)
{
Log.Logger.Error(e, $"Failed to load assembly {assemblyName}");
_loadErrors += $"{e.Message}{Environment.NewLine}{Environment.NewLine}";
var currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
var dllPath = Path.Combine(currentLocation, SecurityConstants.DUMMY_DIR, $"{assemblyName}.dll");
return Assembly.LoadFile(dllPath);
}
}
var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.GetName().Name == assemblyName);
return loadedAssembly;
};
StartMain();
_host.Start();
EventManager.RegisterClassHandler(typeof(UIElement), UIElement.PreviewKeyDownEvent, new RoutedEventHandler(GlobalKeyHandler));
_host.Services.GetRequiredService<MainSuite>().Show();
};
login.Closed += (sender, args) =>
{
if (!login.MainSuiteOpened)
_inferenceClient.Dispose();
};
login.ShowDialog();
}
private void StartMain()
{
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext() .Enrich.FromLogContext()
.MinimumLevel.Information() .MinimumLevel.Information()
@@ -166,12 +58,86 @@ public partial class App
rollingInterval: RollingInterval.Day) rollingInterval: RollingInterval.Day)
.CreateLogger(); .CreateLogger();
Parser.Default.ParseArguments<ApiCredentials>(e.Args)
.WithParsed(Start)
.WithNotParsed(ErrorHandling);
}
private void ErrorHandling(IEnumerable<Error> obj)
{
Log.Fatal($"Error happened: {string.Join(",", obj.Select(x => x.Tag))}");
}
private static InitConfig ReadInitConfig()
{
try
{
if (!File.Exists(SecurityConstants.CONFIG_PATH))
throw new FileNotFoundException(SecurityConstants.CONFIG_PATH);
var configStr = File.ReadAllText(SecurityConstants.CONFIG_PATH);
var config = JsonConvert.DeserializeObject<InitConfig>(configStr);
return config ?? SecurityConstants.DefaultInitConfig;
}
catch (Exception e)
{
Console.WriteLine(e);
return SecurityConstants.DefaultInitConfig;
}
}
private Stream GetSystemConfig(LoaderClient loaderClient, string apiDir)
{
try
{
return loaderClient.LoadFile("config.system.json", apiDir);
}
catch (Exception e)
{
Log.Logger.Error(e, e.Message);
return new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new
{
AnnotationConfig = Constants.DefaultAnnotationConfig,
AIRecognitionConfig = Constants.DefaultAIRecognitionConfig,
ThumbnailConfig = Constants.DefaultThumbnailConfig,
})));
}
}
private Stream GetSecuredConfig(LoaderClient loaderClient, string apiDir)
{
try
{
return loaderClient.LoadFile("config.secured.json", apiDir);
}
catch (Exception e)
{
Log.Logger.Error(e, e.Message);
throw;
}
}
private void Start(ApiCredentials credentials)
{
new ConfigUpdater().CheckConfig();
var initConfig = ReadInitConfig();
var apiDir = initConfig.DirectoriesConfig.ApiResourcesDirectory;
var loaderClient = new LoaderClient(initConfig.LoaderClientConfig, Log.Logger, _mainCTokenSource.Token);
#if DEBUG
loaderClient.StartClient();
#endif
loaderClient.Connect(); //Client app should be already started by LoaderUI
loaderClient.Send(RemoteCommand.Create(CommandType.Login, credentials));
var azaionApi = new AzaionApi(new HttpClient { BaseAddress = new Uri(initConfig.InferenceClientConfig.ApiUrl) }, _cache, credentials);
_host = Host.CreateDefaultBuilder() _host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, config) => config .ConfigureAppConfiguration((_, config) => config
.AddCommandLine(Environment.GetCommandLineArgs()) .AddCommandLine(Environment.GetCommandLineArgs())
.AddJsonFile(SecurityConstants.CONFIG_PATH, optional: true, reloadOnChange: true) .AddJsonFile(SecurityConstants.CONFIG_PATH, optional: true, reloadOnChange: true)
.AddJsonStream(_securedConfig) .AddJsonStream(GetSystemConfig(loaderClient, apiDir))
.AddJsonStream(_systemConfig)) .AddJsonStream(GetSecuredConfig(loaderClient, apiDir)))
.UseSerilog() .UseSerilog()
.ConfigureServices((context, services) => .ConfigureServices((context, services) =>
{ {
@@ -188,15 +154,20 @@ public partial class App
#region External Services #region External Services
services.ConfigureSection<LoaderClientConfig>(context.Configuration);
services.AddSingleton(loaderClient);
services.ConfigureSection<InferenceClientConfig>(context.Configuration); services.ConfigureSection<InferenceClientConfig>(context.Configuration);
services.ConfigureSection<GpsDeniedClientConfig>(context.Configuration); services.AddSingleton<IInferenceClient, InferenceClient>();
services.AddSingleton<IInferenceClient>(_inferenceClient);
services.AddSingleton<IGpsMatcherClient, GpsMatcherClient>();
services.AddSingleton<IInferenceService, InferenceService>(); services.AddSingleton<IInferenceService, InferenceService>();
services.ConfigureSection<GpsDeniedClientConfig>(context.Configuration);
services.AddSingleton<IGpsMatcherClient, GpsMatcherClient>();
services.AddSingleton<IGpsMatcherService, GpsMatcherService>(); services.AddSingleton<IGpsMatcherService, GpsMatcherService>();
services.AddSingleton<ISatelliteDownloader, SatelliteDownloader>(); services.AddSingleton<ISatelliteDownloader, SatelliteDownloader>();
services.AddHttpClient(); services.AddHttpClient();
services.AddSingleton(_azaionApi); services.AddSingleton(azaionApi);
#endregion #endregion
services.AddSingleton<IConfigUpdater, ConfigUpdater>(); services.AddSingleton<IConfigUpdater, ConfigUpdater>();
@@ -235,12 +206,13 @@ public partial class App
datasetExplorer.Hide(); datasetExplorer.Hide();
_mediator = _host.Services.GetRequiredService<IMediator>(); _mediator = _host.Services.GetRequiredService<IMediator>();
if (!string.IsNullOrEmpty(_loadErrors))
_mediator.Publish(new LoadErrorEvent(_loadErrors));
_logger = _host.Services.GetRequiredService<ILogger<App>>();
_formState = _host.Services.GetRequiredService<FormState>(); _formState = _host.Services.GetRequiredService<FormState>();
DispatcherUnhandledException += OnDispatcherUnhandledException; DispatcherUnhandledException += OnDispatcherUnhandledException;
_host.Start();
EventManager.RegisterClassHandler(typeof(UIElement), UIElement.PreviewKeyDownEvent, new RoutedEventHandler(GlobalKeyHandler));
_host.Services.GetRequiredService<MainSuite>().Show();
} }
private void GlobalKeyHandler(object sender, RoutedEventArgs e) private void GlobalKeyHandler(object sender, RoutedEventArgs e)
+9 -23
View File
@@ -12,19 +12,21 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="GMap.NET.WinPresentation" Version="2.1.7" /> <PackageReference Include="GMap.NET.WinPresentation" Version="2.1.7" />
<PackageReference Include="LibVLCSharp" Version="3.9.1" /> <PackageReference Include="LibVLCSharp" Version="3.9.1" />
<PackageReference Include="LibVLCSharp.WPF" Version="3.9.1" /> <PackageReference Include="LibVLCSharp.WPF" Version="3.9.1" />
<PackageReference Include="MediatR" Version="12.4.1" /> <PackageReference Include="MediatR" Version="12.5.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="RabbitMQ.Stream.Client" Version="1.8.9" /> <PackageReference Include="RabbitMQ.Stream.Client" Version="1.8.9" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" /> <PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.4" /> <PackageReference Include="Serilog.Extensions.Logging" Version="9.0.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="SharpVectors" Version="1.8.4.2" /> <PackageReference Include="SharpVectors" Version="1.8.4.2" />
<PackageReference Include="SharpVectors.Wpf" Version="1.8.4.2" /> <PackageReference Include="SharpVectors.Wpf" Version="1.8.4.2" />
<PackageReference Include="System.Data.SQLite" Version="1.0.119" /> <PackageReference Include="System.Data.SQLite" Version="1.0.119" />
@@ -51,20 +53,4 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="Login.xaml">
<Generator>MSBuild:Compile</Generator>
<XamlRuntime>Wpf</XamlRuntime>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="Build">
<MakeDir Directories="$(TargetDir)dummy" />
<Copy SourceFiles="$(TargetDir)Azaion.Annotator.dll" DestinationFolder="$(TargetDir)dummy" />
<Copy SourceFiles="$(TargetDir)Azaion.Dataset.dll" DestinationFolder="$(TargetDir)dummy" />
<Exec Command="postbuild.cmd $(ConfigurationName) stage" />
</Target>
</Project> </Project>
+6 -5
View File
@@ -1,17 +1,18 @@
{ {
"LoaderClientConfig": {
"ZeroMqHost": "127.0.0.1",
"ZeroMqPort": 5025,
"ApiUrl": "https://api.azaion.com"
},
"InferenceClientConfig": { "InferenceClientConfig": {
"ZeroMqHost": "127.0.0.1", "ZeroMqHost": "127.0.0.1",
"ZeroMqPort": 5127, "ZeroMqPort": 5127,
"RetryCount": 25,
"TimeoutSeconds": 5,
"ApiUrl": "https://api.azaion.com" "ApiUrl": "https://api.azaion.com"
}, },
"GpsDeniedClientConfig": { "GpsDeniedClientConfig": {
"ZeroMqHost": "127.0.0.1", "ZeroMqHost": "127.0.0.1",
"ZeroMqPort": 5555, "ZeroMqPort": 5555,
"ZeroMqReceiverPort": 5556, "ZeroMqReceiverPort": 5556
"RetryCount": 25,
"TimeoutSeconds": 5
}, },
"DirectoriesConfig": { "DirectoriesConfig": {
"ApiResourcesDirectory": "stage", "ApiResourcesDirectory": "stage",
-13
View File
@@ -1,13 +0,0 @@
@echo off
set CONFIG=%1
set RESOURCES_FOLDER=%2
set FILE1_TO_UPLOAD=%cd%\..\Azaion.Annotator\bin\%CONFIG%\net8.0-windows\Azaion.Annotator.dll
call upload-file %FILE1_TO_UPLOAD% %RESOURCES_FOLDER%
set FILE2_TO_UPLOAD=%cd%\..\Azaion.Dataset\bin\%CONFIG%\net8.0-windows\Azaion.Dataset.dll
call upload-file %FILE2_TO_UPLOAD% %RESOURCES_FOLDER%
set SUITE_FOLDER=%cd%\bin\%CONFIG%\net8.0-windows\
-28
View File
@@ -1,28 +0,0 @@
setlocal enabledelayedexpansion
set API_URL=https://api.azaion.com
set SOURCE_FILE=%1
set DESTINATION=%2
set "SOURCE_FILE=%SOURCE_FILE:\=/%"
set EMAIL=uploader@azaion.com
set PASSWORD=Az@1on_10Upl0@der
for /f "tokens=*" %%i in ('curl -s -X POST -H "Content-Type: application/json" ^
-d "{\"email\":\"%EMAIL%\",\"password\":\"%PASSWORD%\"}" %API_URL%/login') do set RESPONSE=%%i
for /f "tokens=2 delims=:" %%a in ('echo %RESPONSE% ^| findstr /i "token"') do (
set "TOKEN=%%a"
set "TOKEN=!TOKEN:~1,-1!"
set "TOKEN=!TOKEN:~0,-2!"
)
set UPLOAD_URL=%API_URL%/resources/%DESTINATION%
echo Uploading %SOURCE_FILE% to %UPLOAD_URL%...
curl --location %UPLOAD_URL% ^
-H "Authorization: Bearer %TOKEN%" ^
-H "Content-Type: multipart/form-data" ^
--form "data=@%SOURCE_FILE%"
echo Upload complete!
+1 -1
View File
@@ -13,7 +13,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" /> <PackageReference Include="FluentAssertions" Version="8.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.72" /> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.8.0" /> <PackageReference Include="xunit" Version="2.8.0" />
+4 -11
View File
@@ -11,19 +11,12 @@
<PackageReference Include="libc.translation" Version="7.1.1" /> <PackageReference Include="libc.translation" Version="7.1.1" />
<PackageReference Include="LibVLCSharp" Version="3.9.1" /> <PackageReference Include="LibVLCSharp" Version="3.9.1" />
<PackageReference Include="LibVLCSharp.WPF" Version="3.9.1" /> <PackageReference Include="LibVLCSharp.WPF" Version="3.9.1" />
<PackageReference Include="MediatR" Version="12.4.1" /> <PackageReference Include="MediatR" Version="12.5.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.3" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="RangeTree" Version="3.0.1" /> <PackageReference Include="RangeTree" Version="3.0.1" />
<PackageReference Include="Serilog" Version="4.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="SkiaSharp" Version="2.88.9" /> <PackageReference Include="SkiaSharp" Version="2.88.9" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.21" /> <PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.21" />
<PackageReference Include="WindowsAPICodePack" Version="7.0.4" /> <PackageReference Include="WindowsAPICodePack" Version="7.0.4" />
+1 -1
View File
@@ -16,7 +16,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
<PackageReference Include="ScottPlot.WPF" Version="5.0.46" /> <PackageReference Include="ScottPlot.WPF" Version="5.0.46" />
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.0" /> <PackageReference Include="VirtualizingWrapPanel" Version="2.1.0" />
</ItemGroup> </ItemGroup>
+2 -3
View File
@@ -16,9 +16,8 @@ xcopy Azaion.Suite\bin\Release\net8.0-windows\win-x64\publish dist\ /s /e /q
del dist\config.json del dist\config.json
move dist\config.production.json dist\config.json move dist\config.production.json dist\config.json
mkdir dist-azaion\dummy robocopy "dist" "dist-azaion" "Azaion.Annotator.dll" "Azaion.Dataset.dll" "Azaion.Common.dll" "Azaion.CommonSecurity.dll" /MOV
robocopy "dist" "dist-azaion\dummy" "Azaion.Annotator.dll" "Azaion.Dataset.dll" /MOV robocopy "dist" "dist-azaion" "Azaion.Suite.deps.json" "Azaion.Suite.dll" "Azaion.Suite.exe" "Azaion.Suite.runtimeconfig.json" "config.json" "logo.png" /MOV
robocopy "dist" "dist-azaion" "Azaion.Common.dll" "Azaion.CommonSecurity.dll" "Azaion.Suite.deps.json" "Azaion.Suite.dll" "Azaion.Suite.exe" "Azaion.Suite.runtimeconfig.json" "config.json" "logo.png" /MOV
if exist dist\libvlc\win-x86 rmdir dist\libvlc\win-x86 /s /q if exist dist\libvlc\win-x86 rmdir dist\libvlc\win-x86 /s /q
robocopy "dist" "dist-dlls" /E /MOVE robocopy "dist" "dist-dlls" /E /MOVE