diff --git a/.gitignore b/.gitignore index 528133f..788d8a9 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,6 @@ dist-azaion Azaion*.exe Azaion*.bin -azaion\.*\.big \ No newline at end of file +azaion\.*\.big +_internal +*.spec \ No newline at end of file diff --git a/Azaion.Common/DTO/Config/AppConfig.cs b/Azaion.Common/DTO/Config/AppConfig.cs index 7d94ab3..74f8298 100644 --- a/Azaion.Common/DTO/Config/AppConfig.cs +++ b/Azaion.Common/DTO/Config/AppConfig.cs @@ -8,6 +8,8 @@ namespace Azaion.Common.DTO.Config; public class AppConfig { + public LoaderClientConfig LoaderClientConfig { get; set; } = null!; + public InferenceClientConfig InferenceClientConfig { get; set; } = null!; public GpsDeniedClientConfig GpsDeniedClientConfig { get; set; } = null!; @@ -76,6 +78,7 @@ public class ConfigUpdater : IConfigUpdater //Save only user's config var publicConfig = new { + config.LoaderClientConfig, config.InferenceClientConfig, config.GpsDeniedClientConfig, config.DirectoriesConfig, diff --git a/Azaion.Common/Services/InferenceClient.cs b/Azaion.Common/Services/InferenceClient.cs index 0d6771d..1e30f6f 100644 --- a/Azaion.Common/Services/InferenceClient.cs +++ b/Azaion.Common/Services/InferenceClient.cs @@ -17,17 +17,14 @@ namespace Azaion.Common.Services; public interface IInferenceClient : IDisposable { - event EventHandler BytesReceived; event EventHandler? InferenceDataReceived; event EventHandler? AIAvailabilityReceived; void Send(RemoteCommand create); void Stop(); } -public class InferenceClient : IInferenceClient, IResourceLoader +public class InferenceClient(IOptions inferenceConfig, IOptions loaderConfig) : IInferenceClient { - private CancellationTokenSource _waitFileCancelSource = new(); - public event EventHandler? BytesReceived; public event EventHandler? InferenceDataReceived; public event EventHandler? AIAvailabilityReceived; @@ -35,15 +32,10 @@ public class InferenceClient : IInferenceClient, IResourceLoader private readonly DealerSocket _dealer = new(); private readonly NetMQPoller _poller = new(); private readonly Guid _clientId = Guid.NewGuid(); - private readonly InferenceClientConfig _inferenceClientConfig; + private readonly InferenceClientConfig _inferenceClientConfig = inferenceConfig.Value; + private readonly LoaderClientConfig _loaderClientConfig = loaderConfig.Value; - public InferenceClient(IOptions config, CancellationToken ct) - { - _inferenceClientConfig = config.Value; - Start(ct); - } - - private void Start(CancellationToken ct = default) + private void Start() { try { @@ -51,7 +43,7 @@ public class InferenceClient : IInferenceClient, IResourceLoader process.StartInfo = new ProcessStartInfo { FileName = SecurityConstants.EXTERNAL_INFERENCE_PATH, - Arguments = $"--port {_inferenceClientConfig.ZeroMqPort} --api {_inferenceClientConfig.ApiUrl}", + Arguments = $"--port {_inferenceClientConfig.ZeroMqPort} --loader-port {_loaderClientConfig.ZeroMqPort} --api {_inferenceClientConfig.ApiUrl}", //RedirectStandardOutput = true, //RedirectStandardError = true, //CreateNoWindow = true @@ -70,9 +62,9 @@ public class InferenceClient : IInferenceClient, IResourceLoader _dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N")); _dealer.Connect($"tcp://{_inferenceClientConfig.ZeroMqHost}:{_inferenceClientConfig.ZeroMqPort}"); - _dealer.ReceiveReady += (_, e) => ProcessClientCommand(e.Socket, ct); + _dealer.ReceiveReady += (_, e) => ProcessClientCommand(e.Socket); _poller.Add(_dealer); - _ = Task.Run(() => _poller.RunAsync(), ct); + _ = Task.Run(() => _poller.RunAsync()); } private void ProcessClientCommand(NetMQSocket socket, CancellationToken ct = default) @@ -99,46 +91,14 @@ public class InferenceClient : IInferenceClient, IResourceLoader } } - public void Stop() - { + public void Stop() => + Send(RemoteCommand.Create(CommandType.StopInference)); - } - - public void Send(RemoteCommand command) - { + public void Send(RemoteCommand command) => _dealer.SendFrame(MessagePackSerializer.Serialize(command)); - } - - public MemoryStream LoadFile(string fileName, string? folder = null, TimeSpan? timeout = null) - { - //TODO: Bad solution, look for better implementation - byte[] bytes = []; - Exception? exception = null; - _waitFileCancelSource = new CancellationTokenSource(); - Send(RemoteCommand.Create(CommandType.Load, new LoadFileData(fileName, folder))); - BytesReceived += OnBytesReceived; - - void OnBytesReceived(object? sender, RemoteCommand command) - { - if (command.Data is null) - { - exception = new BusinessException(command.Message ?? "File is empty"); - _waitFileCancelSource.Cancel(); - } - - bytes = command.Data!; - _waitFileCancelSource.Cancel(); - } - _waitFileCancelSource.Token.WaitForCancel(timeout ?? TimeSpan.FromSeconds(15)); - BytesReceived -= OnBytesReceived; - if (exception != null) - throw exception; - return new MemoryStream(bytes); - } public void Dispose() { - _waitFileCancelSource.Dispose(); _poller.Stop(); _poller.Dispose(); diff --git a/Azaion.Common/Services/InferenceService.cs b/Azaion.Common/Services/InferenceService.cs index 119353e..9dee7e6 100644 --- a/Azaion.Common/Services/InferenceService.cs +++ b/Azaion.Common/Services/InferenceService.cs @@ -79,8 +79,5 @@ public class InferenceService : IInferenceService await combinedTokenSource.Token.AsTask(); } - public void StopInference() - { - _client.Send(RemoteCommand.Create(CommandType.StopInference)); - } + public void StopInference() => _client.Stop(); } \ No newline at end of file diff --git a/Azaion.CommonSecurity/DTO/Commands/RemoteCommand.cs b/Azaion.CommonSecurity/DTO/Commands/RemoteCommand.cs index fbeecf6..923cdf1 100644 --- a/Azaion.CommonSecurity/DTO/Commands/RemoteCommand.cs +++ b/Azaion.CommonSecurity/DTO/Commands/RemoteCommand.cs @@ -19,6 +19,8 @@ public class RemoteCommand(CommandType commandType, byte[]? data = null, string? public static RemoteCommand Create(CommandType commandType, T data, string? message = null) where T : class => new(commandType, MessagePackSerializer.Serialize(data), message); + + public override string ToString() => $"({CommandType.ToString().ToUpper()}: Data: {Data?.Length ?? 0} bytes. Message: {Message})"; } [MessagePackObject] @@ -35,6 +37,7 @@ public class LoadFileData(string filename, string? folder = null ) public enum CommandType { None = 0, + Ok = 3, Login = 10, Load = 20, DataBytes = 25, diff --git a/Azaion.CommonSecurity/SecurityConstants.cs b/Azaion.CommonSecurity/SecurityConstants.cs index 9beea40..85deddb 100644 --- a/Azaion.CommonSecurity/SecurityConstants.cs +++ b/Azaion.CommonSecurity/SecurityConstants.cs @@ -6,8 +6,6 @@ public class SecurityConstants { public const string CONFIG_PATH = "config.json"; - public const string DUMMY_DIR = "dummy"; - private const string DEFAULT_API_URL = "https://api.azaion.com"; #region ExternalClientsConfig diff --git a/Azaion.CommonSecurity/Services/LoaderClient.cs b/Azaion.CommonSecurity/Services/LoaderClient.cs index 62c1d7b..05f6226 100644 --- a/Azaion.CommonSecurity/Services/LoaderClient.cs +++ b/Azaion.CommonSecurity/Services/LoaderClient.cs @@ -27,8 +27,14 @@ public class LoaderClient(LoaderClientConfig config, ILogger logger, Cancellatio 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.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) @@ -44,28 +50,39 @@ public class LoaderClient(LoaderClientConfig config, ILogger logger, Cancellatio _dealer.Connect($"tcp://{config.ZeroMqHost}:{config.ZeroMqPort}"); } - public void Send(RemoteCommand command) => - _dealer.SendFrame(MessagePackSerializer.Serialize(command)); + public void Login(ApiCredentials credentials) + { + var result = SendCommand(RemoteCommand.Create(CommandType.Login, credentials)); + if (result.CommandType != CommandType.Ok) + throw new Exception(result.Message); + } public MemoryStream LoadFile(string filename, string folder) + { + var result = SendCommand(RemoteCommand.Create(CommandType.Load, new LoadFileData(filename, folder))); + if (result.Data?.Length == 0) + throw new Exception($"Can't load {filename}. Returns 0 bytes"); + return new MemoryStream(result.Data!); + } + + private RemoteCommand SendCommand(RemoteCommand command, int retryCount = 15, int retryDelayMs = 400) { try { - Send(RemoteCommand.Create(CommandType.Load, new LoadFileData(filename, folder))); + _dealer.SendFrame(MessagePackSerializer.Serialize(command)); - var retries = 10; var tryNum = 0; - while (!ct.IsCancellationRequested && tryNum++ < retries) + while (!ct.IsCancellationRequested && tryNum++ < retryCount) { - if (!_dealer.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(150), out var bytes)) + if (!_dealer.TryReceiveFrameBytes(TimeSpan.FromMilliseconds(retryDelayMs), out var bytes)) continue; - var resultCommand = MessagePackSerializer.Deserialize(bytes, cancellationToken: ct); - if (resultCommand.Data?.Length == 0) - throw new Exception($"Can't load {filename}. Returns 0 bytes"); - return new MemoryStream(resultCommand.Data!); + var res = MessagePackSerializer.Deserialize(bytes, cancellationToken: ct); + if (res.CommandType == CommandType.Error) + throw new Exception(res.Message); + return res; } - throw new Exception($"Can't load file {filename} after {retries} retries"); + throw new Exception($"Sent {command} {retryCount} times. No response from client."); } catch (Exception e) { diff --git a/Azaion.CommonSecurity/Services/ResourceLoader.cs b/Azaion.CommonSecurity/Services/ResourceLoader.cs deleted file mode 100644 index 773ac40..0000000 --- a/Azaion.CommonSecurity/Services/ResourceLoader.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Azaion.CommonSecurity.DTO.Commands; - -namespace Azaion.CommonSecurity.Services; - -public interface IResourceLoader -{ - MemoryStream LoadFile(string fileName, string? folder, TimeSpan? timeout = null); -} diff --git a/Azaion.Inference/build_inference.cmd b/Azaion.Inference/build_inference.cmd index 5835a10..01b08de 100644 --- a/Azaion.Inference/build_inference.cmd +++ b/Azaion.Inference/build_inference.cmd @@ -23,7 +23,6 @@ echo install azaion-inference venv\Scripts\pyinstaller --name=azaion-inference ^ --collect-submodules cv2 ^ --add-data "venv\Lib\site-packages\cv2;cv2" ^ ---collect-all requests ^ --collect-all psutil ^ --collect-all msgpack ^ --collect-all zmq ^ @@ -36,14 +35,6 @@ venv\Scripts\pyinstaller --name=azaion-inference ^ --collect-all jwt ^ --hidden-import constants ^ --hidden-import annotation ^ ---hidden-import credentials ^ ---hidden-import file_data ^ ---hidden-import user ^ ---hidden-import security ^ ---hidden-import secure_model ^ ---hidden-import cdn_manager ^ ---hidden-import api_client ^ ---hidden-import hardware_service ^ --hidden-import remote_command ^ --hidden-import ai_config ^ --hidden-import tensorrt_engine ^ diff --git a/Azaion.Inference/file_data.pxd b/Azaion.Inference/file_data.pxd new file mode 100644 index 0000000..11604e5 --- /dev/null +++ b/Azaion.Inference/file_data.pxd @@ -0,0 +1,16 @@ +cdef class FileData: + cdef public str folder + cdef public str filename + + @staticmethod + cdef from_msgpack(bytes data) + + cdef bytes serialize(self) + +cdef class UploadFileData(FileData): + cdef public bytes resource + + @staticmethod + cdef from_msgpack(bytes data) + + cdef bytes serialize(self) diff --git a/Azaion.Inference/file_data.pyx b/Azaion.Inference/file_data.pyx new file mode 100644 index 0000000..73bf2b2 --- /dev/null +++ b/Azaion.Inference/file_data.pyx @@ -0,0 +1,40 @@ +from msgpack import unpackb, packb + +cdef class FileData: + def __init__(self, str folder, str filename): + self.folder = folder + self.filename = filename + + @staticmethod + cdef from_msgpack(bytes data): + unpacked = unpackb(data, strict_map_key=False) + return FileData( + unpacked.get("Folder"), + unpacked.get("Filename")) + + cdef bytes serialize(self): + return packb({ + "Folder": self.folder, + "Filename": self.filename + }) + + +cdef class UploadFileData(FileData): + def __init__(self, bytes resource, str folder, str filename): + super().__init__(folder, filename) + self.resource = resource + + @staticmethod + cdef from_msgpack(bytes data): + unpacked = unpackb(data, strict_map_key=False) + return UploadFileData( + unpacked.get("Resource"), + unpacked.get("Folder"), + unpacked.get("Filename")) + + cdef bytes serialize(self): + return packb({ + "Resource": self.resource, + "Folder": self.folder, + "Filename": self.filename + }) \ No newline at end of file diff --git a/Azaion.Inference/inference.pxd b/Azaion.Inference/inference.pxd index 7cb602f..2a090ab 100644 --- a/Azaion.Inference/inference.pxd +++ b/Azaion.Inference/inference.pxd @@ -1,11 +1,11 @@ from remote_command cimport RemoteCommand from annotation cimport Annotation, Detection from ai_config cimport AIRecognitionConfig -from api_client cimport ApiClient +from loader_client cimport LoaderClient from inference_engine cimport InferenceEngine cdef class Inference: - cdef ApiClient api_client + cdef LoaderClient loader_client cdef InferenceEngine engine cdef object on_annotation cdef Annotation _previous_annotation diff --git a/Azaion.Inference/inference.pyx b/Azaion.Inference/inference.pyx index ccae2c6..acf369d 100644 --- a/Azaion.Inference/inference.pyx +++ b/Azaion.Inference/inference.pyx @@ -1,29 +1,54 @@ -import json import mimetypes -import os -import subprocess -import sys import time - import cv2 import numpy as np - cimport constants from remote_command cimport RemoteCommand from annotation cimport Detection, Annotation from ai_config cimport AIRecognitionConfig -from hardware_service cimport HardwareService -from security cimport Security +import pynvml -if HardwareService.has_nvidia_gpu(): +cdef int tensor_gpu_index + +cdef int check_tensor_gpu_index(): + try: + pynvml.nvmlInit() + deviceCount = pynvml.nvmlDeviceGetCount() + + if deviceCount == 0: + print('No NVIDIA GPUs found.') + return -1 + + for i in range(deviceCount): + handle = pynvml.nvmlDeviceGetHandleByIndex(i) + major, minor = pynvml.nvmlDeviceGetCudaComputeCapability(handle) + + if major > 6 or (major == 6 and minor >= 1): + print('found NVIDIA GPU!') + return i + + print('NVIDIA GPU doesnt support TensorRT!') + return -1 + + except pynvml.NVMLError: + return -1 + finally: + try: + pynvml.nvmlShutdown() + except: + print('Failed to shutdown pynvml cause probably no NVidia GPU') + pass + +tensor_gpu_index = check_tensor_gpu_index() +if tensor_gpu_index > -1: from tensorrt_engine import TensorRTEngine else: from onnx_engine import OnnxEngine cdef class Inference: - def __init__(self, api_client, on_annotation): - self.api_client = api_client + def __init__(self, loader_client, on_annotation): + self.loader_client = loader_client self.on_annotation = on_annotation self.stop_signal = False self.model_input = None @@ -33,27 +58,26 @@ cdef class Inference: self.is_building_engine = False cdef build_tensor_engine(self, object updater_callback): - is_nvidia = HardwareService.has_nvidia_gpu() - if not is_nvidia: + if not tensor_gpu_index == -1: return engine_filename = TensorRTEngine.get_engine_filename(0) - key = Security.get_model_encryption_key() models_dir = constants.MODELS_FOLDER self.is_building_engine = True updater_callback('downloading') - if self.api_client.load_big_small_resource(engine_filename, models_dir, key): + + if self.loader_client.load_big_small_resource(engine_filename, models_dir): print('tensor rt engine is here, no need to build') self.is_building_engine = False return # time.sleep(8) # prevent simultaneously loading dll and models updater_callback('converting') - onnx_model = self.api_client.load_big_small_resource(constants.AI_ONNX_MODEL_FILE, models_dir, key) + onnx_model = self.loader_client.load_big_small_resource(constants.AI_ONNX_MODEL_FILE, models_dir) model_bytes = TensorRTEngine.convert_from_onnx(onnx_model) updater_callback('uploading') - self.api_client.upload_big_small_resource(model_bytes, engine_filename, models_dir, key) + self.loader_client.upload_big_small_resource(model_bytes, engine_filename, models_dir) print(f'uploaded {engine_filename} to CDN and API') self.is_building_engine = False @@ -61,17 +85,16 @@ cdef class Inference: if self.engine is not None: return - is_nvidia = HardwareService.has_nvidia_gpu() - key = Security.get_model_encryption_key() models_dir = constants.MODELS_FOLDER - if is_nvidia: + if tensor_gpu_index > -1: while self.is_building_engine: time.sleep(1) engine_filename = TensorRTEngine.get_engine_filename(0) - model_bytes = self.api_client.load_big_small_resource(engine_filename, models_dir, key) + + model_bytes = self.loader_client.load_big_small_resource(engine_filename, models_dir) self.engine = TensorRTEngine(model_bytes) else: - model_bytes = self.api_client.load_big_small_resource(constants.AI_ONNX_MODEL_FILE, models_dir, key) + model_bytes = self.loader_client.load_big_small_resource(constants.AI_ONNX_MODEL_FILE, models_dir) self.engine = OnnxEngine(model_bytes) self.model_height, self.model_width = self.engine.get_input_shape() diff --git a/Azaion.Inference/loader_client.pxd b/Azaion.Inference/loader_client.pxd new file mode 100644 index 0000000..fe959fa --- /dev/null +++ b/Azaion.Inference/loader_client.pxd @@ -0,0 +1,13 @@ +from remote_command cimport RemoteCommand + +cdef class LoaderClient: + cdef object _context + cdef object _socket + + cdef RemoteCommand _send_receive_command(self, RemoteCommand command) + + cdef load_big_small_resource(self, str filename, str directory) + + cdef upload_big_small_resource(self, bytes content, str filename, str directory) + + cdef close(self) \ No newline at end of file diff --git a/Azaion.Inference/loader_client.pyx b/Azaion.Inference/loader_client.pyx new file mode 100644 index 0000000..df297ea --- /dev/null +++ b/Azaion.Inference/loader_client.pyx @@ -0,0 +1,39 @@ +import zmq +from remote_command cimport RemoteCommand, CommandType +from file_data cimport FileData, UploadFileData + +cdef class LoaderClient: + def __init__(self, str zmq_host, int zmq_port): + self._context = zmq.Context().instance() + self._socket = self._context.socket(zmq.DEALER) + self._socket.connect(f'tcp://{zmq_host}:{zmq_port}') + + cdef RemoteCommand _send_receive_command(self, RemoteCommand command): + self._socket.send(command.serialize()) + return RemoteCommand.from_msgpack(self._socket.recv()) + + cdef load_big_small_resource(self, str filename, str directory): + cdef FileData file_data = FileData(folder=directory, filename=filename) + cdef RemoteCommand response = self._send_receive_command(RemoteCommand(CommandType.LOAD_BIG_SMALL, data=file_data.serialize())) + if response.command_type == CommandType.DATA_BYTES: + return response.data + elif response.command_type == CommandType.ERROR: + raise Exception(f"Error from server: {response.message}") + else: + raise Exception(f"Unexpected response command type: {response.command_type}") + + cdef upload_big_small_resource(self, bytes content, str filename, str directory): + cdef UploadFileData upload_file_data = UploadFileData(content, filename, directory) + cdef RemoteCommand upload_resp = self._send_receive_command(RemoteCommand(CommandType.UPLOAD_BIG_SMALL, data=upload_file_data.serialize())) + if upload_resp.command_type == CommandType.OK: + return + elif upload_resp.command_type == CommandType.ERROR: + raise Exception(f"Error from server: {upload_resp.message}") + else: + raise Exception(f"Unexpected response command type: {upload_resp.command_type}") + + cdef close(self): + if self._socket and not self._socket.closed: + self._socket.close() + if self._context and not self._context.closed: + self._context.term() \ No newline at end of file diff --git a/Azaion.Inference/main.pyx b/Azaion.Inference/main.pyx index ecaa8ea..f816108 100644 --- a/Azaion.Inference/main.pyx +++ b/Azaion.Inference/main.pyx @@ -4,10 +4,9 @@ from queue import Queue cimport constants from threading import Thread -import yaml - from annotation cimport Annotation from inference cimport Inference +from loader_client cimport LoaderClient from remote_command cimport RemoteCommand, CommandType from remote_command_handler cimport RemoteCommandHandler @@ -17,14 +16,16 @@ cdef class CommandProcessor: cdef object inference_queue cdef bint running cdef Inference inference + cdef LoaderClient loader_client - def __init__(self, int zmq_port, str api_url): + def __init__(self, int zmq_port, str loader_zmq_host, int loader_zmq_port, str api_url): self.remote_handler = RemoteCommandHandler(zmq_port, self.on_command) self.inference_queue = Queue(maxsize=constants.QUEUE_MAXSIZE) self.remote_handler.start() self.running = True + self.loader_client = LoaderClient(loader_zmq_host, loader_zmq_port) #TODO: replace api_client to azaion_loader.exe call - self.inference = Inference(self.api_client, self.on_annotation) + self.inference = Inference(self.loader_client, self.on_annotation) def start(self): while self.running: @@ -41,14 +42,12 @@ cdef class CommandProcessor: 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.INFERENCE: + if command.command_type == CommandType.INFERENCE: self.inference_queue.put(command) elif command.command_type == CommandType.AI_AVAILABILITY_CHECK: - self.build_tensor_engine(command.client_id) + self.inference.build_tensor_engine(lambda status: self.remote_handler.send( + command.client_id, RemoteCommand(CommandType.AI_AVAILABILITY_RESULT, None, status).serialize())) + self.remote_handler.send(command.client_id, RemoteCommand(CommandType.AI_AVAILABILITY_RESULT, None, 'enabled').serialize()) elif command.command_type == CommandType.STOP_INFERENCE: self.inference.stop() elif command.command_type == CommandType.EXIT: @@ -59,25 +58,6 @@ cdef class CommandProcessor: except Exception as e: print(f"Error handling client: {e}") - cdef build_tensor_engine(self, client_id): - self.inference.build_tensor_engine(lambda status: self.build_tensor_status_updater(client_id, status)) - self.remote_handler.send(client_id, RemoteCommand(CommandType.AI_AVAILABILITY_RESULT, None, 'enabled').serialize()) - - cdef build_tensor_status_updater(self, bytes client_id, str status): - self.remote_handler.send(client_id, RemoteCommand(CommandType.AI_AVAILABILITY_RESULT, None, status).serialize()) - - 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()) - cdef on_annotation(self, RemoteCommand cmd, Annotation annotation): cdef RemoteCommand response = RemoteCommand(CommandType.INFERENCE_DATA, annotation.serialize()) self.remote_handler.send(cmd.client_id, response.serialize()) diff --git a/Azaion.Inference/remote_command.pxd b/Azaion.Inference/remote_command.pxd index 5f5b708..0d02840 100644 --- a/Azaion.Inference/remote_command.pxd +++ b/Azaion.Inference/remote_command.pxd @@ -1,6 +1,9 @@ cdef enum CommandType: + OK = 3 LOGIN = 10 LOAD = 20 + LOAD_BIG_SMALL = 22 + UPLOAD_BIG_SMALL = 24 DATA_BYTES = 25 INFERENCE = 30 INFERENCE_DATA = 35 diff --git a/Azaion.Inference/remote_command.pyx b/Azaion.Inference/remote_command.pyx index 333b34a..cea1f41 100644 --- a/Azaion.Inference/remote_command.pyx +++ b/Azaion.Inference/remote_command.pyx @@ -8,8 +8,11 @@ cdef class RemoteCommand: def __str__(self): command_type_names = { + 3: "OK", 10: "LOGIN", 20: "LOAD", + 22: "LOAD_BIG_SMALL", + 24: "UPLOAD_BIG_SMALL", 25: "DATA_BYTES", 30: "INFERENCE", 35: "INFERENCE_DATA", diff --git a/Azaion.Inference/setup.py b/Azaion.Inference/setup.py index ee669b0..62ee10d 100644 --- a/Azaion.Inference/setup.py +++ b/Azaion.Inference/setup.py @@ -5,15 +5,10 @@ import numpy as np extensions = [ Extension('constants', ['constants.pyx']), Extension('annotation', ['annotation.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('loader_client', ['loader_client.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('ai_config', ['ai_config.pyx']), Extension('tensorrt_engine', ['tensorrt_engine.pyx'], include_dirs=[np.get_include()]), Extension('onnx_engine', ['onnx_engine.pyx'], include_dirs=[np.get_include()]), diff --git a/Azaion.Inference/start.py b/Azaion.Inference/start.py index 5f3ede1..0f4cd57 100644 --- a/Azaion.Inference/start.py +++ b/Azaion.Inference/start.py @@ -2,18 +2,16 @@ from main import CommandProcessor import argparse -def start(zmq_port, api_url): - processor = CommandProcessor(zmq_port, api_url) +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", type=int, default=5127, help="zero mq port") + parser.add_argument("-lh", "--loader-host", type=str, default="127.0.0.1", help="zero mq loader app port") + parser.add_argument("-lp", "--loader-port", type=int, default=5025, help="zero mq loader app port") + parser.add_argument("-a", "--api", type=str, default="https://api.azaion.com", help="api url") + args = parser.parse_args() + + processor = CommandProcessor(args.port, args.loader_host, args.loader_port, args.api) try: processor.start() except KeyboardInterrupt: processor.stop() - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument("-p", "--port", type=str, default="5127", help="zero mq port") - parser.add_argument("-a", "--api", type=str, default="https://api.azaion.com", help="api url") - args = parser.parse_args() - - start(int(args.port), args.api) diff --git a/Azaion.Loader/api_client.pxd b/Azaion.Loader/api_client.pxd index 9e77294..468ac46 100644 --- a/Azaion.Loader/api_client.pxd +++ b/Azaion.Loader/api_client.pxd @@ -17,5 +17,5 @@ cdef class ApiClient: cdef load_bytes(self, str filename, 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 upload_big_small_resource(self, bytes resource, str resource_name, str folder, str key) + cdef load_big_small_resource(self, str resource_name, str folder) + cdef upload_big_small_resource(self, bytes resource, str resource_name, str folder) diff --git a/Azaion.Loader/api_client.pyx b/Azaion.Loader/api_client.pyx index 2fe3162..9c9ba2a 100644 --- a/Azaion.Loader/api_client.pyx +++ b/Azaion.Loader/api_client.pyx @@ -8,7 +8,7 @@ import requests cimport constants import yaml from requests import HTTPError - +from credentials cimport Credentials from cdn_manager cimport CDNManager, CDNCredentials from hardware_service cimport HardwareService from security cimport Security @@ -98,8 +98,10 @@ cdef class ApiClient: constants.log(f"Upload fail: {e}") cdef load_bytes(self, str filename, str folder): - hardware_service = HardwareService() - cdef str hardware = hardware_service.get_hardware_info() + cdef str hardware = HardwareService.get_hardware_info() + hw_hash = Security.get_hw_hash(hardware) + key = Security.get_api_encryption_key(self.credentials, hw_hash) + if self.token is None: self.login() url = f"{self.api_url}/resources/get/{folder}" @@ -124,10 +126,12 @@ cdef class ApiClient: response = requests.post(url, data=payload, headers=headers, stream=True) if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR: - print('500!') - - hw_hash = Security.get_hw_hash(hardware) - key = Security.get_api_encryption_key(self.credentials, hw_hash) + raise Exception(f'Internal API error! {response.text}') + if response.status_code == HTTPStatus.CONFLICT: + res = response.json() + err_code = res['ErrorCode'] + err_msg = res['Message'] + raise Exception(f"Error {err_code}: {err_msg}") resp_bytes = response.raw.read() data = Security.decrypt_to(resp_bytes, key) @@ -142,28 +146,32 @@ cdef class ApiClient: else: return None - cdef load_big_small_resource(self, str resource_name, str folder, str key): + cdef load_big_small_resource(self, str resource_name, str folder): cdef str big_part = f'{resource_name}.big' cdef str small_part = f'{resource_name}.small' encrypted_bytes_small = self.load_bytes(small_part, folder) + key = Security.get_resource_encryption_key() + print(f'checking on existence for {folder}\\{big_part}') if os.path.exists(os.path.join( folder, big_part)): with open(path.join( folder, big_part), 'rb') as binary_file: local_bytes_big = binary_file.read() print(f'local file {folder}\\{big_part} is found!') try: - return Security.decrypt_to(encrypted_bytes_small + local_bytes_big, key) + resource = Security.decrypt_to(encrypted_bytes_small + local_bytes_big, key) + return resource except Exception as ex: print('Local file doesnt match with api file, old version') remote_bytes_big = self.load_big_file_cdn(folder, big_part) 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): cdef str big_part_name = f'{resource_name}.big' cdef str small_part_name = f'{resource_name}.small' + key = Security.get_resource_encryption_key() resource_encrypted = Security.encrypt_to(resource, key) part_small_size = min(constants.SMALL_SIZE_KB * 1024, int(0.3 * len(resource_encrypted))) diff --git a/Azaion.Loader/build_loader.cmd b/Azaion.Loader/build_loader.cmd index 0ed743b..36e006b 100644 --- a/Azaion.Loader/build_loader.cmd +++ b/Azaion.Loader/build_loader.cmd @@ -21,4 +21,22 @@ venv\Scripts\python setup.py build_ext --inplace echo install azaion-loader venv\Scripts\pyinstaller --name=azaion-loader ^ ---collect-all boto3 ^ \ No newline at end of file +--collect-all requests ^ +--collect-all boto3 ^ +--collect-all msgpack ^ +--collect-all zmq ^ +--collect-all jwt ^ +--collect-all boto3 ^ +--collect-all cryptography ^ +--collect-all yaml ^ +--hidden-import constants ^ +--hidden-import file_data ^ +--hidden-import remote_command ^ +--hidden-import remote_command_handler ^ +--hidden-import user ^ +--hidden-import security ^ +--hidden-import cdn_manager ^ +--hidden-import credentials ^ +--hidden-import api_client ^ +--hidden-import hardware_service ^ +start.py \ No newline at end of file diff --git a/Azaion.Loader/credentials.pyx b/Azaion.Loader/credentials.pyx index ae0e17e..bb07e5f 100644 --- a/Azaion.Loader/credentials.pyx +++ b/Azaion.Loader/credentials.pyx @@ -13,3 +13,6 @@ cdef class Credentials: unpacked.get("Email"), unpacked.get("Password")) + def __str__(self): + return f'{self.email}: {self.password}' + diff --git a/Azaion.Loader/file_data.pxd b/Azaion.Loader/file_data.pxd index ad5223e..ee7f359 100644 --- a/Azaion.Loader/file_data.pxd +++ b/Azaion.Loader/file_data.pxd @@ -4,3 +4,13 @@ cdef class FileData: @staticmethod cdef from_msgpack(bytes data) + + cdef bytes serialize(self) + +cdef class UploadFileData(FileData): + cdef public bytes resource + + @staticmethod + cdef from_msgpack(bytes data) + + cdef bytes serialize(self) \ No newline at end of file diff --git a/Azaion.Loader/file_data.pyx b/Azaion.Loader/file_data.pyx index 1b13de6..73bf2b2 100644 --- a/Azaion.Loader/file_data.pyx +++ b/Azaion.Loader/file_data.pyx @@ -1,7 +1,6 @@ -from msgpack import unpackb +from msgpack import unpackb, packb cdef class FileData: - def __init__(self, str folder, str filename): self.folder = folder self.filename = filename @@ -12,3 +11,30 @@ cdef class FileData: return FileData( unpacked.get("Folder"), unpacked.get("Filename")) + + cdef bytes serialize(self): + return packb({ + "Folder": self.folder, + "Filename": self.filename + }) + + +cdef class UploadFileData(FileData): + def __init__(self, bytes resource, str folder, str filename): + super().__init__(folder, filename) + self.resource = resource + + @staticmethod + cdef from_msgpack(bytes data): + unpacked = unpackb(data, strict_map_key=False) + return UploadFileData( + unpacked.get("Resource"), + unpacked.get("Folder"), + unpacked.get("Filename")) + + cdef bytes serialize(self): + return packb({ + "Resource": self.resource, + "Folder": self.folder, + "Filename": self.filename + }) \ No newline at end of file diff --git a/Azaion.Loader/hardware_service.pxd b/Azaion.Loader/hardware_service.pxd index dcbb929..9cf2ffe 100644 --- a/Azaion.Loader/hardware_service.pxd +++ b/Azaion.Loader/hardware_service.pxd @@ -1,5 +1,4 @@ cdef class HardwareService: @staticmethod - cdef has_nvidia_gpu() - cdef str get_hardware_info(self) \ No newline at end of file + cdef str get_hardware_info() \ No newline at end of file diff --git a/Azaion.Loader/hardware_service.pyx b/Azaion.Loader/hardware_service.pyx index 11e122e..5907ed1 100644 --- a/Azaion.Loader/hardware_service.pyx +++ b/Azaion.Loader/hardware_service.pyx @@ -1,34 +1,9 @@ import os import subprocess -import pynvml - - cdef class HardwareService: @staticmethod - cdef has_nvidia_gpu(): - try: - pynvml.nvmlInit() - device_count = pynvml.nvmlDeviceGetCount() - - if device_count > 0: - print(f"Found NVIDIA GPU(s).") - return True - else: - print("No NVIDIA GPUs found by NVML.") - return False - - except pynvml.NVMLError as error: - print(f"Failed to find NVIDIA GPU") - return False - finally: - try: - pynvml.nvmlShutdown() - except: - print('Failed to shutdown pynvml cause probably no NVidia GPU') - pass - - cdef str get_hardware_info(self): + cdef str get_hardware_info(): if os.name == 'nt': # windows os_command = ( "powershell -Command \"" diff --git a/Azaion.Loader/main.pyx b/Azaion.Loader/main.pyx index b891954..d2085dd 100644 --- a/Azaion.Loader/main.pyx +++ b/Azaion.Loader/main.pyx @@ -4,7 +4,7 @@ import traceback from credentials cimport Credentials from remote_command cimport RemoteCommand, CommandType from remote_command_handler cimport RemoteCommandHandler -from file_data cimport FileData +from file_data cimport FileData, UploadFileData from api_client cimport ApiClient cdef class CommandProcessor: @@ -12,6 +12,7 @@ cdef class CommandProcessor: cdef ApiClient api_client cdef bint running cdef object shutdown_event + cdef RemoteCommand ok_response def __init__(self, int zmq_port, str api_url): self.api_client = ApiClient(api_url) @@ -19,6 +20,7 @@ cdef class CommandProcessor: self.remote_handler = RemoteCommandHandler(zmq_port, self.on_command) self.remote_handler.start() self.running = True + self.ok_response = RemoteCommand(CommandType.OK) def start(self): while self.running: @@ -29,31 +31,36 @@ cdef class CommandProcessor: 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: + if command.command_type == CommandType.EXIT: + self.remote_handler.send(command.client_id, self.ok_response.serialize()) t = Thread(target=self.stop) # non-block worker: t.start() + return + + if command.command_type == CommandType.LOGIN: + self.api_client.set_credentials(Credentials.from_msgpack(command.data)) + self.remote_handler.send(command.client_id, self.ok_response.serialize()) + elif command.command_type == CommandType.LOAD: + file_data = FileData.from_msgpack(command.data) + file_bytes = self.api_client.load_bytes(file_data.filename, file_data.folder) + self.remote_handler.send(command.client_id, RemoteCommand(CommandType.DATA_BYTES, file_bytes).serialize()) + elif command.command_type == CommandType.LOAD_BIG_SMALL: + data = FileData.from_msgpack(command.data) + file_bytes = self.api_client.load_big_small_resource(data.filename, data.folder) + self.remote_handler.send(command.client_id, RemoteCommand(CommandType.DATA_BYTES, file_bytes).serialize()) + elif command.command_type == CommandType.UPLOAD_BIG_SMALL: + data = UploadFileData.from_msgpack(command.data) + file_bytes = self.api_client.upload_big_small_resource(data.resource, data.filename, data.folder) + self.remote_handler.send(command.client_id, RemoteCommand(CommandType.OK).serialize()) 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()) + err_command = RemoteCommand(CommandType.ERROR, None, str(e)) + self.remote_handler.send(command.client_id, err_command.serialize()) def stop(self): self.shutdown_event.set() diff --git a/Azaion.Loader/remote_command.pxd b/Azaion.Loader/remote_command.pxd index 5f5b708..0d02840 100644 --- a/Azaion.Loader/remote_command.pxd +++ b/Azaion.Loader/remote_command.pxd @@ -1,6 +1,9 @@ cdef enum CommandType: + OK = 3 LOGIN = 10 LOAD = 20 + LOAD_BIG_SMALL = 22 + UPLOAD_BIG_SMALL = 24 DATA_BYTES = 25 INFERENCE = 30 INFERENCE_DATA = 35 diff --git a/Azaion.Loader/remote_command.pyx b/Azaion.Loader/remote_command.pyx index 333b34a..18a41ec 100644 --- a/Azaion.Loader/remote_command.pyx +++ b/Azaion.Loader/remote_command.pyx @@ -1,15 +1,18 @@ import msgpack cdef class RemoteCommand: - def __init__(self, CommandType command_type, bytes data, str message=None): + def __init__(self, CommandType command_type, bytes data=None, str message=None): self.command_type = command_type self.data = data self.message = message def __str__(self): command_type_names = { + 3: "OK", 10: "LOGIN", 20: "LOAD", + 22: "LOAD_BIG_SMALL", + 24: "UPLOAD_BIG_SMALL", 25: "DATA_BYTES", 30: "INFERENCE", 35: "INFERENCE_DATA", diff --git a/Azaion.Loader/remote_command_handler.pyx b/Azaion.Loader/remote_command_handler.pyx index 86877ed..32a0118 100644 --- a/Azaion.Loader/remote_command_handler.pyx +++ b/Azaion.Loader/remote_command_handler.pyx @@ -3,7 +3,7 @@ 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): diff --git a/Azaion.Loader/requirements.txt b/Azaion.Loader/requirements.txt index ea86d64..ef65f13 100644 --- a/Azaion.Loader/requirements.txt +++ b/Azaion.Loader/requirements.txt @@ -7,3 +7,4 @@ zmq requests pyyaml boto3 +cryptography==44.0.2 \ No newline at end of file diff --git a/Azaion.Loader/security.pxd b/Azaion.Loader/security.pxd index ad1396f..e0e92ed 100644 --- a/Azaion.Loader/security.pxd +++ b/Azaion.Loader/security.pxd @@ -14,7 +14,7 @@ cdef class Security: cdef get_api_encryption_key(Credentials credentials, str hardware_hash) @staticmethod - cdef get_model_encryption_key() + cdef get_resource_encryption_key() @staticmethod cdef calc_hash(str key) \ No newline at end of file diff --git a/Azaion.Loader/security.pyx b/Azaion.Loader/security.pyx index 7bada8a..a7771c8 100644 --- a/Azaion.Loader/security.pyx +++ b/Azaion.Loader/security.pyx @@ -56,7 +56,7 @@ cdef class Security: return Security.calc_hash(key) @staticmethod - cdef get_model_encryption_key(): + cdef get_resource_encryption_key(): cdef str key = '-#%@AzaionKey@%#---234sdfklgvhjbnn' return Security.calc_hash(key) diff --git a/Azaion.Suite.sln b/Azaion.Suite.sln index 7dac5b3..179f670 100644 --- a/Azaion.Suite.sln +++ b/Azaion.Suite.sln @@ -13,16 +13,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Common", "Azaion.Com EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Dataset", "Azaion.Dataset\Azaion.Dataset.csproj", "{01A5CA37-A62E-4EF3-8678-D72CD9525677}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dummy", "Dummy", "{C307BE2E-FFCC-4BD7-AD89-C82D40B65D03}" - ProjectSection(SolutionItems) = preProject - Dummy\Azaion.Annotator.dll = Dummy\Azaion.Annotator.dll - Dummy\Azaion.Dataset.dll = Dummy\Azaion.Dataset.dll - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Annotator", "Dummy\Azaion.Annotator\Azaion.Annotator.csproj", "{32C4747F-F700-44FD-B4ED-21B4A66B5FAB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Dataset", "Dummy\Azaion.Dataset\Azaion.Dataset.csproj", "{A2E3D3AE-5DB7-4342-BE20-88A9D1B0C05E}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.CommonSecurity", "Azaion.CommonSecurity\Azaion.CommonSecurity.csproj", "{E0C7176D-2E91-4928-B3C1-55CC91C8F77D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{CF141A48-8002-4006-81CF-6B85AE5B0B5F}" @@ -73,14 +63,6 @@ Global {01A5CA37-A62E-4EF3-8678-D72CD9525677}.Release|Any CPU.ActiveCfg = Release|Any CPU {01A5CA37-A62E-4EF3-8678-D72CD9525677}.Release|Any CPU.Build.0 = Release|Any CPU {01A5CA37-A62E-4EF3-8678-D72CD9525677}.Release|Any CPU.Deploy.0 = Release|Any CPU - {32C4747F-F700-44FD-B4ED-21B4A66B5FAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {32C4747F-F700-44FD-B4ED-21B4A66B5FAB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32C4747F-F700-44FD-B4ED-21B4A66B5FAB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {32C4747F-F700-44FD-B4ED-21B4A66B5FAB}.Release|Any CPU.Build.0 = Release|Any CPU - {A2E3D3AE-5DB7-4342-BE20-88A9D1B0C05E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A2E3D3AE-5DB7-4342-BE20-88A9D1B0C05E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A2E3D3AE-5DB7-4342-BE20-88A9D1B0C05E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A2E3D3AE-5DB7-4342-BE20-88A9D1B0C05E}.Release|Any CPU.Build.0 = Release|Any CPU {E0C7176D-2E91-4928-B3C1-55CC91C8F77D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E0C7176D-2E91-4928-B3C1-55CC91C8F77D}.Debug|Any CPU.Build.0 = Debug|Any CPU {E0C7176D-2E91-4928-B3C1-55CC91C8F77D}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -95,8 +77,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {32C4747F-F700-44FD-B4ED-21B4A66B5FAB} = {C307BE2E-FFCC-4BD7-AD89-C82D40B65D03} - {A2E3D3AE-5DB7-4342-BE20-88A9D1B0C05E} = {C307BE2E-FFCC-4BD7-AD89-C82D40B65D03} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {788BD4AD-E4EC-43A1-85A0-AEC644BD8D48} diff --git a/Azaion.Suite/App.xaml.cs b/Azaion.Suite/App.xaml.cs index 5611f5b..794781f 100644 --- a/Azaion.Suite/App.xaml.cs +++ b/Azaion.Suite/App.xaml.cs @@ -128,8 +128,8 @@ public partial class App loaderClient.StartClient(); #endif loaderClient.Connect(); //Client app should be already started by LoaderUI + loaderClient.Login(credentials); - loaderClient.Send(RemoteCommand.Create(CommandType.Login, credentials)); var azaionApi = new AzaionApi(new HttpClient { BaseAddress = new Uri(initConfig.InferenceClientConfig.ApiUrl) }, _cache, credentials); _host = Host.CreateDefaultBuilder() @@ -167,7 +167,7 @@ public partial class App services.AddSingleton(); services.AddHttpClient(); - services.AddSingleton(azaionApi); + services.AddSingleton(azaionApi); #endregion services.AddSingleton(); @@ -201,9 +201,9 @@ public partial class App .Build(); Annotation.InitializeDirs(_host.Services.GetRequiredService>().Value); - var datasetExplorer = _host.Services.GetRequiredService(); - datasetExplorer.Show(); - datasetExplorer.Hide(); + _host.Services.GetRequiredService(); + // datasetExplorer.Show(); + // datasetExplorer.Hide(); _mediator = _host.Services.GetRequiredService(); diff --git a/Dummy/Azaion.Annotator/Annotator.xaml b/Dummy/Azaion.Annotator/Annotator.xaml deleted file mode 100644 index f139f98..0000000 --- a/Dummy/Azaion.Annotator/Annotator.xaml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - Під час запуску виникла помилка! - - Error happened during the launch! - - - - Будь ласка перевірте правильність email чи паролю! - Також зауважте, що запуск можливий лише з одного конкретного компьютера, копіювання заборонене! - Для подальшого вирішення проблеми ви можете зв'язатися з нами: hi@azaion.com - - Please check your email or password! - The program is restricted to start only from particular hardware, copying is forbidden! - For the further guidance, please feel free to contact us: hi@azaion.com - - - diff --git a/Dummy/Azaion.Annotator/Annotator.xaml.cs b/Dummy/Azaion.Annotator/Annotator.xaml.cs deleted file mode 100644 index 067b45b..0000000 --- a/Dummy/Azaion.Annotator/Annotator.xaml.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Azaion.Annotator; - -public partial class Annotator -{ - public Annotator() - { - InitializeComponent(); - } -} diff --git a/Dummy/Azaion.Annotator/AnnotatorEventHandler.cs b/Dummy/Azaion.Annotator/AnnotatorEventHandler.cs deleted file mode 100644 index f216f8a..0000000 --- a/Dummy/Azaion.Annotator/AnnotatorEventHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Azaion.Common.Events; -using MediatR; - -namespace Azaion.Annotator; - -public class AnnotatorEventHandler(Annotator annotator) : INotificationHandler -{ - public Task Handle(LoadErrorEvent notification, CancellationToken cancellationToken) - { - annotator.Dispatcher.Invoke(() => annotator.TbError.Text = notification.Error); - return Task.CompletedTask; - } -} diff --git a/Dummy/Azaion.Annotator/AnnotatorModule.cs b/Dummy/Azaion.Annotator/AnnotatorModule.cs deleted file mode 100644 index 2df179a..0000000 --- a/Dummy/Azaion.Annotator/AnnotatorModule.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Azaion.Common.DTO; - -namespace Azaion.Annotator; - -public class AnnotatorModule : IAzaionModule -{ - public string Name => "Анотатор"; - - public string SvgIcon => -@" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"; - - public Type MainWindowType => typeof(Annotator); - - public WindowEnum WindowEnum => WindowEnum.Annotator; -} \ No newline at end of file diff --git a/Dummy/Azaion.Annotator/AssemblyInfo.cs b/Dummy/Azaion.Annotator/AssemblyInfo.cs deleted file mode 100644 index 4a05c7d..0000000 --- a/Dummy/Azaion.Annotator/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -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) -)] \ No newline at end of file diff --git a/Dummy/Azaion.Annotator/Azaion.Annotator.csproj b/Dummy/Azaion.Annotator/Azaion.Annotator.csproj deleted file mode 100644 index daeb91e..0000000 --- a/Dummy/Azaion.Annotator/Azaion.Annotator.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - enable - enable - true - net8.0-windows - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Dummy/Azaion.Annotator/DTO/FormState.cs b/Dummy/Azaion.Annotator/DTO/FormState.cs deleted file mode 100644 index ae14d69..0000000 --- a/Dummy/Azaion.Annotator/DTO/FormState.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Azaion.Annotator.DTO; - -public class FormState; \ No newline at end of file diff --git a/Dummy/Azaion.Annotator/Extensions/VLCFrameExtractor.cs b/Dummy/Azaion.Annotator/Extensions/VLCFrameExtractor.cs deleted file mode 100644 index 7887ba9..0000000 --- a/Dummy/Azaion.Annotator/Extensions/VLCFrameExtractor.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Azaion.Annotator.Extensions; - -public class VLCFrameExtractor; \ No newline at end of file diff --git a/Dummy/Azaion.Annotator/HelpWindow.xaml b/Dummy/Azaion.Annotator/HelpWindow.xaml deleted file mode 100644 index a15a573..0000000 --- a/Dummy/Azaion.Annotator/HelpWindow.xaml +++ /dev/null @@ -1,11 +0,0 @@ - - diff --git a/Dummy/Azaion.Annotator/HelpWindow.xaml.cs b/Dummy/Azaion.Annotator/HelpWindow.xaml.cs deleted file mode 100644 index 4031078..0000000 --- a/Dummy/Azaion.Annotator/HelpWindow.xaml.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Azaion.Annotator; - -public partial class HelpWindow; \ No newline at end of file diff --git a/Dummy/Azaion.Dataset/AssemblyInfo.cs b/Dummy/Azaion.Dataset/AssemblyInfo.cs deleted file mode 100644 index 4a05c7d..0000000 --- a/Dummy/Azaion.Dataset/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -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) -)] \ No newline at end of file diff --git a/Dummy/Azaion.Dataset/Azaion.Dataset.csproj b/Dummy/Azaion.Dataset/Azaion.Dataset.csproj deleted file mode 100644 index 04f54b9..0000000 --- a/Dummy/Azaion.Dataset/Azaion.Dataset.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - net8.0-windows - enable - enable - true - - - - - MSBuild:Compile - Wpf - Designer - - - - - - - - - - - - - - diff --git a/Dummy/Azaion.Dataset/BitmapExtensions.cs b/Dummy/Azaion.Dataset/BitmapExtensions.cs deleted file mode 100644 index cb54e6a..0000000 --- a/Dummy/Azaion.Dataset/BitmapExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.IO; -using System.Windows.Media.Imaging; - -namespace Azaion.Dataset; - -public static class BitmapExtensions -{ - public static async Task OpenImage(this string imagePath) - { - var image = new BitmapImage(); - await using var stream = File.OpenRead(imagePath); - image.BeginInit(); - image.CacheOption = BitmapCacheOption.OnLoad; - image.StreamSource = stream; - image.EndInit(); - image.Freeze(); - return image; - } -} \ No newline at end of file diff --git a/Dummy/Azaion.Dataset/DatasetExplorer.xaml b/Dummy/Azaion.Dataset/DatasetExplorer.xaml deleted file mode 100644 index 01caff8..0000000 --- a/Dummy/Azaion.Dataset/DatasetExplorer.xaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - Будь ласка перевірте правильність email чи паролю! - Також зауважте, що запуск можливий лише з одного конкретного компьютера, копіювання заборонене! - Для подальшого вирішення проблеми ви можете зв'язатися з нами: hi@azaion.com - - Please check your email or password! - The program is restricted to start only from particular hardware, copying is forbidden! - For the further guidance, please feel free to contact us: hi@azaion.com - - diff --git a/Dummy/Azaion.Dataset/DatasetExplorer.xaml.cs b/Dummy/Azaion.Dataset/DatasetExplorer.xaml.cs deleted file mode 100644 index c7fc94e..0000000 --- a/Dummy/Azaion.Dataset/DatasetExplorer.xaml.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Azaion.Dataset; - -public partial class DatasetExplorer; \ No newline at end of file diff --git a/Dummy/Azaion.Dataset/DatasetExplorerModule.cs b/Dummy/Azaion.Dataset/DatasetExplorerModule.cs deleted file mode 100644 index c907ec5..0000000 --- a/Dummy/Azaion.Dataset/DatasetExplorerModule.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Azaion.Common.DTO; - -namespace Azaion.Dataset; - -public class DatasetExplorerModule : IAzaionModule -{ - public string Name => "Переглядач"; - - public string SvgIcon => - @" - - - - - - - - - -"; - - public Type MainWindowType => typeof(DatasetExplorer); - - public WindowEnum WindowEnum => WindowEnum.DatasetExplorer; -} \ No newline at end of file diff --git a/Dummy/Azaion.Dataset/GalleryManager.cs b/Dummy/Azaion.Dataset/GalleryManager.cs deleted file mode 100644 index 7aeb993..0000000 --- a/Dummy/Azaion.Dataset/GalleryManager.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Concurrent; -using System.Drawing; -using System.IO; -using Azaion.Annotator.Extensions; -using Azaion.Common; -using Azaion.Common.DTO; -using Azaion.Common.Extensions; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Newtonsoft.Json; -using Color = System.Drawing.Color; -using ParallelOptions = Azaion.Annotator.Extensions.ParallelOptions; -using Size = System.Windows.Size; -using System.Drawing.Imaging; -using System.Drawing.Drawing2D; -using Azaion.Common.DTO.Config; - -namespace Azaion.Dataset; - -public delegate void ThumbnailsUpdatedEventHandler(double thumbnailsPercentage); - -public class GalleryManager : IGalleryManager; - -public interface IGalleryManager; \ No newline at end of file