mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 08:36:29 +00:00
separate load functionality from inference client to loader client. Call loader client from inference to get the model.
remove dummy dlls, remove resource loader from c#. TODO: Load dlls separately by Loader UI and loader client WIP
This commit is contained in:
+3
-1
@@ -19,4 +19,6 @@ dist-azaion
|
|||||||
Azaion*.exe
|
Azaion*.exe
|
||||||
Azaion*.bin
|
Azaion*.bin
|
||||||
|
|
||||||
azaion\.*\.big
|
azaion\.*\.big
|
||||||
|
_internal
|
||||||
|
*.spec
|
||||||
@@ -8,6 +8,8 @@ namespace Azaion.Common.DTO.Config;
|
|||||||
|
|
||||||
public class AppConfig
|
public class AppConfig
|
||||||
{
|
{
|
||||||
|
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!;
|
||||||
@@ -76,6 +78,7 @@ public class ConfigUpdater : IConfigUpdater
|
|||||||
//Save only user's config
|
//Save only user's config
|
||||||
var publicConfig = new
|
var publicConfig = new
|
||||||
{
|
{
|
||||||
|
config.LoaderClientConfig,
|
||||||
config.InferenceClientConfig,
|
config.InferenceClientConfig,
|
||||||
config.GpsDeniedClientConfig,
|
config.GpsDeniedClientConfig,
|
||||||
config.DirectoriesConfig,
|
config.DirectoriesConfig,
|
||||||
|
|||||||
@@ -17,17 +17,14 @@ namespace Azaion.Common.Services;
|
|||||||
|
|
||||||
public interface IInferenceClient : IDisposable
|
public interface IInferenceClient : IDisposable
|
||||||
{
|
{
|
||||||
event EventHandler<RemoteCommand> BytesReceived;
|
|
||||||
event EventHandler<RemoteCommand>? InferenceDataReceived;
|
event EventHandler<RemoteCommand>? InferenceDataReceived;
|
||||||
event EventHandler<RemoteCommand>? AIAvailabilityReceived;
|
event EventHandler<RemoteCommand>? AIAvailabilityReceived;
|
||||||
void Send(RemoteCommand create);
|
void Send(RemoteCommand create);
|
||||||
void Stop();
|
void Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class InferenceClient : IInferenceClient, IResourceLoader
|
public class InferenceClient(IOptions<InferenceClientConfig> inferenceConfig, IOptions<LoaderClientConfig> loaderConfig) : IInferenceClient
|
||||||
{
|
{
|
||||||
private CancellationTokenSource _waitFileCancelSource = new();
|
|
||||||
|
|
||||||
public event EventHandler<RemoteCommand>? BytesReceived;
|
public event EventHandler<RemoteCommand>? BytesReceived;
|
||||||
public event EventHandler<RemoteCommand>? InferenceDataReceived;
|
public event EventHandler<RemoteCommand>? InferenceDataReceived;
|
||||||
public event EventHandler<RemoteCommand>? AIAvailabilityReceived;
|
public event EventHandler<RemoteCommand>? AIAvailabilityReceived;
|
||||||
@@ -35,15 +32,10 @@ public class InferenceClient : IInferenceClient, IResourceLoader
|
|||||||
private readonly DealerSocket _dealer = new();
|
private readonly DealerSocket _dealer = new();
|
||||||
private readonly NetMQPoller _poller = new();
|
private readonly NetMQPoller _poller = new();
|
||||||
private readonly Guid _clientId = Guid.NewGuid();
|
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<InferenceClientConfig> config, CancellationToken ct)
|
private void Start()
|
||||||
{
|
|
||||||
_inferenceClientConfig = config.Value;
|
|
||||||
Start(ct);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Start(CancellationToken ct = default)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -51,7 +43,7 @@ public class InferenceClient : IInferenceClient, IResourceLoader
|
|||||||
process.StartInfo = new ProcessStartInfo
|
process.StartInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = SecurityConstants.EXTERNAL_INFERENCE_PATH,
|
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,
|
//RedirectStandardOutput = true,
|
||||||
//RedirectStandardError = true,
|
//RedirectStandardError = true,
|
||||||
//CreateNoWindow = true
|
//CreateNoWindow = true
|
||||||
@@ -70,9 +62,9 @@ public class InferenceClient : IInferenceClient, IResourceLoader
|
|||||||
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
|
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
|
||||||
_dealer.Connect($"tcp://{_inferenceClientConfig.ZeroMqHost}:{_inferenceClientConfig.ZeroMqPort}");
|
_dealer.Connect($"tcp://{_inferenceClientConfig.ZeroMqHost}:{_inferenceClientConfig.ZeroMqPort}");
|
||||||
|
|
||||||
_dealer.ReceiveReady += (_, e) => ProcessClientCommand(e.Socket, ct);
|
_dealer.ReceiveReady += (_, e) => ProcessClientCommand(e.Socket);
|
||||||
_poller.Add(_dealer);
|
_poller.Add(_dealer);
|
||||||
_ = Task.Run(() => _poller.RunAsync(), ct);
|
_ = Task.Run(() => _poller.RunAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessClientCommand(NetMQSocket socket, CancellationToken ct = default)
|
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));
|
_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()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_waitFileCancelSource.Dispose();
|
|
||||||
_poller.Stop();
|
_poller.Stop();
|
||||||
_poller.Dispose();
|
_poller.Dispose();
|
||||||
|
|
||||||
|
|||||||
@@ -79,8 +79,5 @@ public class InferenceService : IInferenceService
|
|||||||
await combinedTokenSource.Token.AsTask();
|
await combinedTokenSource.Token.AsTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopInference()
|
public void StopInference() => _client.Stop();
|
||||||
{
|
|
||||||
_client.Send(RemoteCommand.Create(CommandType.StopInference));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,8 @@ public class RemoteCommand(CommandType commandType, byte[]? data = null, string?
|
|||||||
|
|
||||||
public static RemoteCommand Create<T>(CommandType commandType, T data, string? message = null) where T : class =>
|
public static RemoteCommand Create<T>(CommandType commandType, T data, string? message = null) where T : class =>
|
||||||
new(commandType, MessagePackSerializer.Serialize(data), message);
|
new(commandType, MessagePackSerializer.Serialize(data), message);
|
||||||
|
|
||||||
|
public override string ToString() => $"({CommandType.ToString().ToUpper()}: Data: {Data?.Length ?? 0} bytes. Message: {Message})";
|
||||||
}
|
}
|
||||||
|
|
||||||
[MessagePackObject]
|
[MessagePackObject]
|
||||||
@@ -35,6 +37,7 @@ public class LoadFileData(string filename, string? folder = null )
|
|||||||
public enum CommandType
|
public enum CommandType
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
|
Ok = 3,
|
||||||
Login = 10,
|
Login = 10,
|
||||||
Load = 20,
|
Load = 20,
|
||||||
DataBytes = 25,
|
DataBytes = 25,
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ public class SecurityConstants
|
|||||||
{
|
{
|
||||||
public const string CONFIG_PATH = "config.json";
|
public const string CONFIG_PATH = "config.json";
|
||||||
|
|
||||||
public const string DUMMY_DIR = "dummy";
|
|
||||||
|
|
||||||
private const string DEFAULT_API_URL = "https://api.azaion.com";
|
private const string DEFAULT_API_URL = "https://api.azaion.com";
|
||||||
|
|
||||||
#region ExternalClientsConfig
|
#region ExternalClientsConfig
|
||||||
|
|||||||
@@ -27,8 +27,14 @@ public class LoaderClient(LoaderClientConfig config, ILogger logger, Cancellatio
|
|||||||
CreateNoWindow = true
|
CreateNoWindow = true
|
||||||
};
|
};
|
||||||
|
|
||||||
process.OutputDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
|
process.OutputDataReceived += (_, e) =>
|
||||||
process.ErrorDataReceived += (_, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
|
{
|
||||||
|
if (e.Data != null) Console.WriteLine(e.Data);
|
||||||
|
};
|
||||||
|
process.ErrorDataReceived += (_, e) =>
|
||||||
|
{
|
||||||
|
if (e.Data != null) Console.WriteLine(e.Data);
|
||||||
|
};
|
||||||
process.Start();
|
process.Start();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -44,28 +50,39 @@ public class LoaderClient(LoaderClientConfig config, ILogger logger, Cancellatio
|
|||||||
_dealer.Connect($"tcp://{config.ZeroMqHost}:{config.ZeroMqPort}");
|
_dealer.Connect($"tcp://{config.ZeroMqHost}:{config.ZeroMqPort}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Send(RemoteCommand command) =>
|
public void Login(ApiCredentials credentials)
|
||||||
_dealer.SendFrame(MessagePackSerializer.Serialize(command));
|
{
|
||||||
|
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)
|
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
|
try
|
||||||
{
|
{
|
||||||
Send(RemoteCommand.Create(CommandType.Load, new LoadFileData(filename, folder)));
|
_dealer.SendFrame(MessagePackSerializer.Serialize(command));
|
||||||
|
|
||||||
var retries = 10;
|
|
||||||
var tryNum = 0;
|
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;
|
continue;
|
||||||
var resultCommand = MessagePackSerializer.Deserialize<RemoteCommand>(bytes, cancellationToken: ct);
|
var res = MessagePackSerializer.Deserialize<RemoteCommand>(bytes, cancellationToken: ct);
|
||||||
if (resultCommand.Data?.Length == 0)
|
if (res.CommandType == CommandType.Error)
|
||||||
throw new Exception($"Can't load {filename}. Returns 0 bytes");
|
throw new Exception(res.Message);
|
||||||
return new MemoryStream(resultCommand.Data!);
|
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)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,6 @@ echo install azaion-inference
|
|||||||
venv\Scripts\pyinstaller --name=azaion-inference ^
|
venv\Scripts\pyinstaller --name=azaion-inference ^
|
||||||
--collect-submodules cv2 ^
|
--collect-submodules cv2 ^
|
||||||
--add-data "venv\Lib\site-packages\cv2;cv2" ^
|
--add-data "venv\Lib\site-packages\cv2;cv2" ^
|
||||||
--collect-all requests ^
|
|
||||||
--collect-all psutil ^
|
--collect-all psutil ^
|
||||||
--collect-all msgpack ^
|
--collect-all msgpack ^
|
||||||
--collect-all zmq ^
|
--collect-all zmq ^
|
||||||
@@ -36,14 +35,6 @@ venv\Scripts\pyinstaller --name=azaion-inference ^
|
|||||||
--collect-all jwt ^
|
--collect-all jwt ^
|
||||||
--hidden-import constants ^
|
--hidden-import constants ^
|
||||||
--hidden-import annotation ^
|
--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 remote_command ^
|
||||||
--hidden-import ai_config ^
|
--hidden-import ai_config ^
|
||||||
--hidden-import tensorrt_engine ^
|
--hidden-import tensorrt_engine ^
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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
|
||||||
|
})
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
from remote_command cimport RemoteCommand
|
from remote_command cimport RemoteCommand
|
||||||
from annotation cimport Annotation, Detection
|
from annotation cimport Annotation, Detection
|
||||||
from ai_config cimport AIRecognitionConfig
|
from ai_config cimport AIRecognitionConfig
|
||||||
from api_client cimport ApiClient
|
from loader_client cimport LoaderClient
|
||||||
from inference_engine cimport InferenceEngine
|
from inference_engine cimport InferenceEngine
|
||||||
|
|
||||||
cdef class Inference:
|
cdef class Inference:
|
||||||
cdef ApiClient api_client
|
cdef LoaderClient loader_client
|
||||||
cdef InferenceEngine engine
|
cdef InferenceEngine engine
|
||||||
cdef object on_annotation
|
cdef object on_annotation
|
||||||
cdef Annotation _previous_annotation
|
cdef Annotation _previous_annotation
|
||||||
|
|||||||
@@ -1,29 +1,54 @@
|
|||||||
import json
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
cimport constants
|
cimport constants
|
||||||
from remote_command cimport RemoteCommand
|
from remote_command cimport RemoteCommand
|
||||||
from annotation cimport Detection, Annotation
|
from annotation cimport Detection, Annotation
|
||||||
from ai_config cimport AIRecognitionConfig
|
from ai_config cimport AIRecognitionConfig
|
||||||
from hardware_service cimport HardwareService
|
import pynvml
|
||||||
from security cimport Security
|
|
||||||
|
|
||||||
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
|
from tensorrt_engine import TensorRTEngine
|
||||||
else:
|
else:
|
||||||
from onnx_engine import OnnxEngine
|
from onnx_engine import OnnxEngine
|
||||||
|
|
||||||
|
|
||||||
cdef class Inference:
|
cdef class Inference:
|
||||||
def __init__(self, api_client, on_annotation):
|
def __init__(self, loader_client, on_annotation):
|
||||||
self.api_client = api_client
|
self.loader_client = loader_client
|
||||||
self.on_annotation = on_annotation
|
self.on_annotation = on_annotation
|
||||||
self.stop_signal = False
|
self.stop_signal = False
|
||||||
self.model_input = None
|
self.model_input = None
|
||||||
@@ -33,27 +58,26 @@ cdef class Inference:
|
|||||||
self.is_building_engine = False
|
self.is_building_engine = False
|
||||||
|
|
||||||
cdef build_tensor_engine(self, object updater_callback):
|
cdef build_tensor_engine(self, object updater_callback):
|
||||||
is_nvidia = HardwareService.has_nvidia_gpu()
|
if not tensor_gpu_index == -1:
|
||||||
if not is_nvidia:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
engine_filename = TensorRTEngine.get_engine_filename(0)
|
engine_filename = TensorRTEngine.get_engine_filename(0)
|
||||||
key = Security.get_model_encryption_key()
|
|
||||||
models_dir = constants.MODELS_FOLDER
|
models_dir = constants.MODELS_FOLDER
|
||||||
|
|
||||||
self.is_building_engine = True
|
self.is_building_engine = True
|
||||||
updater_callback('downloading')
|
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')
|
print('tensor rt engine is here, no need to build')
|
||||||
self.is_building_engine = False
|
self.is_building_engine = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# time.sleep(8) # prevent simultaneously loading dll and models
|
# time.sleep(8) # prevent simultaneously loading dll and models
|
||||||
updater_callback('converting')
|
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)
|
model_bytes = TensorRTEngine.convert_from_onnx(onnx_model)
|
||||||
updater_callback('uploading')
|
updater_callback('uploading')
|
||||||
self.api_client.upload_big_small_resource(model_bytes, <str> engine_filename, models_dir, key)
|
self.loader_client.upload_big_small_resource(model_bytes, <str> engine_filename, models_dir)
|
||||||
print(f'uploaded {engine_filename} to CDN and API')
|
print(f'uploaded {engine_filename} to CDN and API')
|
||||||
self.is_building_engine = False
|
self.is_building_engine = False
|
||||||
|
|
||||||
@@ -61,17 +85,16 @@ cdef class Inference:
|
|||||||
if self.engine is not None:
|
if self.engine is not None:
|
||||||
return
|
return
|
||||||
|
|
||||||
is_nvidia = HardwareService.has_nvidia_gpu()
|
|
||||||
key = Security.get_model_encryption_key()
|
|
||||||
models_dir = constants.MODELS_FOLDER
|
models_dir = constants.MODELS_FOLDER
|
||||||
if is_nvidia:
|
if tensor_gpu_index > -1:
|
||||||
while self.is_building_engine:
|
while self.is_building_engine:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
engine_filename = TensorRTEngine.get_engine_filename(0)
|
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)
|
self.engine = TensorRTEngine(model_bytes)
|
||||||
else:
|
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.engine = OnnxEngine(model_bytes)
|
||||||
|
|
||||||
self.model_height, self.model_width = self.engine.get_input_shape()
|
self.model_height, self.model_width = self.engine.get_input_shape()
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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()
|
||||||
@@ -4,10 +4,9 @@ from queue import Queue
|
|||||||
cimport constants
|
cimport constants
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from annotation cimport Annotation
|
from annotation cimport Annotation
|
||||||
from inference cimport Inference
|
from inference cimport Inference
|
||||||
|
from loader_client cimport LoaderClient
|
||||||
from remote_command cimport RemoteCommand, CommandType
|
from remote_command cimport RemoteCommand, CommandType
|
||||||
from remote_command_handler cimport RemoteCommandHandler
|
from remote_command_handler cimport RemoteCommandHandler
|
||||||
|
|
||||||
@@ -17,14 +16,16 @@ cdef class CommandProcessor:
|
|||||||
cdef object inference_queue
|
cdef object inference_queue
|
||||||
cdef bint running
|
cdef bint running
|
||||||
cdef Inference inference
|
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.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
|
||||||
|
self.loader_client = LoaderClient(loader_zmq_host, loader_zmq_port)
|
||||||
#TODO: replace api_client to azaion_loader.exe call
|
#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):
|
def start(self):
|
||||||
while self.running:
|
while self.running:
|
||||||
@@ -41,14 +42,12 @@ cdef class CommandProcessor:
|
|||||||
|
|
||||||
cdef on_command(self, RemoteCommand command):
|
cdef on_command(self, RemoteCommand command):
|
||||||
try:
|
try:
|
||||||
if command.command_type == CommandType.LOGIN:
|
if command.command_type == CommandType.INFERENCE:
|
||||||
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:
|
|
||||||
self.inference_queue.put(command)
|
self.inference_queue.put(command)
|
||||||
elif command.command_type == CommandType.AI_AVAILABILITY_CHECK:
|
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:
|
elif command.command_type == CommandType.STOP_INFERENCE:
|
||||||
self.inference.stop()
|
self.inference.stop()
|
||||||
elif command.command_type == CommandType.EXIT:
|
elif command.command_type == CommandType.EXIT:
|
||||||
@@ -59,25 +58,6 @@ cdef class CommandProcessor:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error handling client: {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 on_annotation(self, RemoteCommand cmd, Annotation annotation):
|
||||||
cdef RemoteCommand response = RemoteCommand(CommandType.INFERENCE_DATA, annotation.serialize())
|
cdef RemoteCommand response = RemoteCommand(CommandType.INFERENCE_DATA, annotation.serialize())
|
||||||
self.remote_handler.send(cmd.client_id, response.serialize())
|
self.remote_handler.send(cmd.client_id, response.serialize())
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
cdef enum CommandType:
|
cdef enum CommandType:
|
||||||
|
OK = 3
|
||||||
LOGIN = 10
|
LOGIN = 10
|
||||||
LOAD = 20
|
LOAD = 20
|
||||||
|
LOAD_BIG_SMALL = 22
|
||||||
|
UPLOAD_BIG_SMALL = 24
|
||||||
DATA_BYTES = 25
|
DATA_BYTES = 25
|
||||||
INFERENCE = 30
|
INFERENCE = 30
|
||||||
INFERENCE_DATA = 35
|
INFERENCE_DATA = 35
|
||||||
|
|||||||
@@ -8,8 +8,11 @@ cdef class RemoteCommand:
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
command_type_names = {
|
command_type_names = {
|
||||||
|
3: "OK",
|
||||||
10: "LOGIN",
|
10: "LOGIN",
|
||||||
20: "LOAD",
|
20: "LOAD",
|
||||||
|
22: "LOAD_BIG_SMALL",
|
||||||
|
24: "UPLOAD_BIG_SMALL",
|
||||||
25: "DATA_BYTES",
|
25: "DATA_BYTES",
|
||||||
30: "INFERENCE",
|
30: "INFERENCE",
|
||||||
35: "INFERENCE_DATA",
|
35: "INFERENCE_DATA",
|
||||||
|
|||||||
@@ -5,15 +5,10 @@ import numpy as np
|
|||||||
extensions = [
|
extensions = [
|
||||||
Extension('constants', ['constants.pyx']),
|
Extension('constants', ['constants.pyx']),
|
||||||
Extension('annotation', ['annotation.pyx']),
|
Extension('annotation', ['annotation.pyx']),
|
||||||
Extension('credentials', ['credentials.pyx']),
|
|
||||||
Extension('file_data', ['file_data.pyx']),
|
Extension('file_data', ['file_data.pyx']),
|
||||||
Extension('hardware_service', ['hardware_service.pyx'], extra_compile_args=["-g"], extra_link_args=["-g"]),
|
Extension('loader_client', ['loader_client.pyx']),
|
||||||
Extension('security', ['security.pyx']),
|
|
||||||
Extension('remote_command', ['remote_command.pyx']),
|
Extension('remote_command', ['remote_command.pyx']),
|
||||||
Extension('remote_command_handler', ['remote_command_handler.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('ai_config', ['ai_config.pyx']),
|
||||||
Extension('tensorrt_engine', ['tensorrt_engine.pyx'], include_dirs=[np.get_include()]),
|
Extension('tensorrt_engine', ['tensorrt_engine.pyx'], include_dirs=[np.get_include()]),
|
||||||
Extension('onnx_engine', ['onnx_engine.pyx'], include_dirs=[np.get_include()]),
|
Extension('onnx_engine', ['onnx_engine.pyx'], include_dirs=[np.get_include()]),
|
||||||
|
|||||||
@@ -2,18 +2,16 @@ from main import CommandProcessor
|
|||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
def start(zmq_port, api_url):
|
if __name__ == '__main__':
|
||||||
processor = CommandProcessor(zmq_port, api_url)
|
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:
|
try:
|
||||||
processor.start()
|
processor.start()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
processor.stop()
|
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)
|
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ 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_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)
|
||||||
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)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import requests
|
|||||||
cimport constants
|
cimport constants
|
||||||
import yaml
|
import yaml
|
||||||
from requests import HTTPError
|
from requests import HTTPError
|
||||||
|
from credentials cimport Credentials
|
||||||
from cdn_manager cimport CDNManager, CDNCredentials
|
from cdn_manager cimport CDNManager, CDNCredentials
|
||||||
from hardware_service cimport HardwareService
|
from hardware_service cimport HardwareService
|
||||||
from security cimport Security
|
from security cimport Security
|
||||||
@@ -98,8 +98,10 @@ cdef class ApiClient:
|
|||||||
constants.log(f"Upload fail: {e}")
|
constants.log(f"Upload fail: {e}")
|
||||||
|
|
||||||
cdef load_bytes(self, str filename, str folder):
|
cdef load_bytes(self, str filename, str folder):
|
||||||
hardware_service = HardwareService()
|
cdef str hardware = HardwareService.get_hardware_info()
|
||||||
cdef str hardware = hardware_service.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:
|
if self.token is None:
|
||||||
self.login()
|
self.login()
|
||||||
url = f"{self.api_url}/resources/get/{folder}"
|
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)
|
response = requests.post(url, data=payload, headers=headers, stream=True)
|
||||||
|
|
||||||
if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
|
if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
|
||||||
print('500!')
|
raise Exception(f'Internal API error! {response.text}')
|
||||||
|
if response.status_code == HTTPStatus.CONFLICT:
|
||||||
hw_hash = Security.get_hw_hash(hardware)
|
res = response.json()
|
||||||
key = Security.get_api_encryption_key(self.credentials, hw_hash)
|
err_code = res['ErrorCode']
|
||||||
|
err_msg = res['Message']
|
||||||
|
raise Exception(f"Error {err_code}: {err_msg}")
|
||||||
|
|
||||||
resp_bytes = response.raw.read()
|
resp_bytes = response.raw.read()
|
||||||
data = Security.decrypt_to(resp_bytes, key)
|
data = Security.decrypt_to(resp_bytes, key)
|
||||||
@@ -142,28 +146,32 @@ cdef class ApiClient:
|
|||||||
else:
|
else:
|
||||||
return None
|
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 big_part = f'{resource_name}.big'
|
||||||
cdef str small_part = f'{resource_name}.small'
|
cdef str small_part = f'{resource_name}.small'
|
||||||
|
|
||||||
encrypted_bytes_small = self.load_bytes(small_part, folder)
|
encrypted_bytes_small = self.load_bytes(small_part, folder)
|
||||||
|
|
||||||
|
key = Security.get_resource_encryption_key()
|
||||||
|
|
||||||
print(f'checking on existence for {folder}\\{big_part}')
|
print(f'checking on existence for {folder}\\{big_part}')
|
||||||
if os.path.exists(os.path.join(<str> folder, big_part)):
|
if os.path.exists(os.path.join(<str> folder, big_part)):
|
||||||
with open(path.join(<str> folder, big_part), 'rb') as binary_file:
|
with open(path.join(<str> folder, big_part), 'rb') as binary_file:
|
||||||
local_bytes_big = binary_file.read()
|
local_bytes_big = binary_file.read()
|
||||||
print(f'local file {folder}\\{big_part} is found!')
|
print(f'local file {folder}\\{big_part} is found!')
|
||||||
try:
|
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:
|
except Exception as ex:
|
||||||
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, key)
|
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 big_part_name = f'{resource_name}.big'
|
||||||
cdef str small_part_name = f'{resource_name}.small'
|
cdef str small_part_name = f'{resource_name}.small'
|
||||||
|
key = Security.get_resource_encryption_key()
|
||||||
|
|
||||||
resource_encrypted = Security.encrypt_to(<bytes>resource, key)
|
resource_encrypted = Security.encrypt_to(<bytes>resource, key)
|
||||||
part_small_size = min(constants.SMALL_SIZE_KB * 1024, int(0.3 * len(resource_encrypted)))
|
part_small_size = min(constants.SMALL_SIZE_KB * 1024, int(0.3 * len(resource_encrypted)))
|
||||||
|
|||||||
@@ -21,4 +21,22 @@ venv\Scripts\python setup.py build_ext --inplace
|
|||||||
|
|
||||||
echo install azaion-loader
|
echo install azaion-loader
|
||||||
venv\Scripts\pyinstaller --name=azaion-loader ^
|
venv\Scripts\pyinstaller --name=azaion-loader ^
|
||||||
--collect-all boto3 ^
|
--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
|
||||||
@@ -13,3 +13,6 @@ cdef class Credentials:
|
|||||||
unpacked.get("Email"),
|
unpacked.get("Email"),
|
||||||
unpacked.get("Password"))
|
unpacked.get("Password"))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.email}: {self.password}'
|
||||||
|
|
||||||
|
|||||||
@@ -4,3 +4,13 @@ cdef class FileData:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
cdef from_msgpack(bytes data)
|
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)
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
from msgpack import unpackb
|
from msgpack import unpackb, packb
|
||||||
|
|
||||||
cdef class FileData:
|
cdef class FileData:
|
||||||
|
|
||||||
def __init__(self, str folder, str filename):
|
def __init__(self, str folder, str filename):
|
||||||
self.folder = folder
|
self.folder = folder
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
@@ -12,3 +11,30 @@ cdef class FileData:
|
|||||||
return FileData(
|
return FileData(
|
||||||
unpacked.get("Folder"),
|
unpacked.get("Folder"),
|
||||||
unpacked.get("Filename"))
|
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
|
||||||
|
})
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
cdef class HardwareService:
|
cdef class HardwareService:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
cdef has_nvidia_gpu()
|
cdef str get_hardware_info()
|
||||||
cdef str get_hardware_info(self)
|
|
||||||
@@ -1,34 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import pynvml
|
|
||||||
|
|
||||||
|
|
||||||
cdef class HardwareService:
|
cdef class HardwareService:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
cdef has_nvidia_gpu():
|
cdef str get_hardware_info():
|
||||||
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):
|
|
||||||
if os.name == 'nt': # windows
|
if os.name == 'nt': # windows
|
||||||
os_command = (
|
os_command = (
|
||||||
"powershell -Command \""
|
"powershell -Command \""
|
||||||
|
|||||||
+25
-18
@@ -4,7 +4,7 @@ import traceback
|
|||||||
from credentials cimport Credentials
|
from credentials cimport Credentials
|
||||||
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 file_data cimport FileData
|
from file_data cimport FileData, UploadFileData
|
||||||
from api_client cimport ApiClient
|
from api_client cimport ApiClient
|
||||||
|
|
||||||
cdef class CommandProcessor:
|
cdef class CommandProcessor:
|
||||||
@@ -12,6 +12,7 @@ cdef class CommandProcessor:
|
|||||||
cdef ApiClient api_client
|
cdef ApiClient api_client
|
||||||
cdef bint running
|
cdef bint running
|
||||||
cdef object shutdown_event
|
cdef object shutdown_event
|
||||||
|
cdef RemoteCommand ok_response
|
||||||
|
|
||||||
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.api_client = ApiClient(api_url)
|
||||||
@@ -19,6 +20,7 @@ cdef class CommandProcessor:
|
|||||||
self.remote_handler = RemoteCommandHandler(zmq_port, self.on_command)
|
self.remote_handler = RemoteCommandHandler(zmq_port, self.on_command)
|
||||||
self.remote_handler.start()
|
self.remote_handler.start()
|
||||||
self.running = True
|
self.running = True
|
||||||
|
self.ok_response = RemoteCommand(CommandType.OK)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
while self.running:
|
while self.running:
|
||||||
@@ -29,31 +31,36 @@ cdef class CommandProcessor:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
print('EXIT!')
|
print('EXIT!')
|
||||||
|
|
||||||
|
|
||||||
cdef on_command(self, RemoteCommand command):
|
cdef on_command(self, RemoteCommand command):
|
||||||
try:
|
try:
|
||||||
if command.command_type == CommandType.LOGIN:
|
if command.command_type == CommandType.EXIT:
|
||||||
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:
|
|
||||||
self.load_file(command)
|
|
||||||
elif command.command_type == CommandType.EXIT:
|
|
||||||
t = Thread(target=self.stop) # non-block worker:
|
t = Thread(target=self.stop) # non-block worker:
|
||||||
t.start()
|
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:
|
else:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error handling client: {e}")
|
print(f"Error handling client: {e}")
|
||||||
|
err_command = RemoteCommand(CommandType.ERROR, None, str(e))
|
||||||
cdef load_file(self, RemoteCommand command):
|
self.remote_handler.send(command.client_id, err_command.serialize())
|
||||||
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):
|
def stop(self):
|
||||||
self.shutdown_event.set()
|
self.shutdown_event.set()
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
cdef enum CommandType:
|
cdef enum CommandType:
|
||||||
|
OK = 3
|
||||||
LOGIN = 10
|
LOGIN = 10
|
||||||
LOAD = 20
|
LOAD = 20
|
||||||
|
LOAD_BIG_SMALL = 22
|
||||||
|
UPLOAD_BIG_SMALL = 24
|
||||||
DATA_BYTES = 25
|
DATA_BYTES = 25
|
||||||
INFERENCE = 30
|
INFERENCE = 30
|
||||||
INFERENCE_DATA = 35
|
INFERENCE_DATA = 35
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
import msgpack
|
import msgpack
|
||||||
|
|
||||||
cdef class RemoteCommand:
|
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.command_type = command_type
|
||||||
self.data = data
|
self.data = data
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
command_type_names = {
|
command_type_names = {
|
||||||
|
3: "OK",
|
||||||
10: "LOGIN",
|
10: "LOGIN",
|
||||||
20: "LOAD",
|
20: "LOAD",
|
||||||
|
22: "LOAD_BIG_SMALL",
|
||||||
|
24: "UPLOAD_BIG_SMALL",
|
||||||
25: "DATA_BYTES",
|
25: "DATA_BYTES",
|
||||||
30: "INFERENCE",
|
30: "INFERENCE",
|
||||||
35: "INFERENCE_DATA",
|
35: "INFERENCE_DATA",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import zmq
|
|||||||
from threading import Thread, Event
|
from threading import Thread, Event
|
||||||
from remote_command cimport RemoteCommand
|
from remote_command cimport RemoteCommand
|
||||||
cimport constants
|
cimport constants
|
||||||
import yaml
|
|
||||||
|
|
||||||
cdef class RemoteCommandHandler:
|
cdef class RemoteCommandHandler:
|
||||||
def __init__(self, int zmq_port, object on_command):
|
def __init__(self, int zmq_port, object on_command):
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ zmq
|
|||||||
requests
|
requests
|
||||||
pyyaml
|
pyyaml
|
||||||
boto3
|
boto3
|
||||||
|
cryptography==44.0.2
|
||||||
@@ -14,7 +14,7 @@ cdef class Security:
|
|||||||
cdef get_api_encryption_key(Credentials credentials, str hardware_hash)
|
cdef get_api_encryption_key(Credentials credentials, str hardware_hash)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
cdef get_model_encryption_key()
|
cdef get_resource_encryption_key()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
cdef calc_hash(str key)
|
cdef calc_hash(str key)
|
||||||
@@ -56,7 +56,7 @@ cdef class Security:
|
|||||||
return Security.calc_hash(key)
|
return Security.calc_hash(key)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
cdef get_model_encryption_key():
|
cdef get_resource_encryption_key():
|
||||||
cdef str key = '-#%@AzaionKey@%#---234sdfklgvhjbnn'
|
cdef str key = '-#%@AzaionKey@%#---234sdfklgvhjbnn'
|
||||||
return Security.calc_hash(key)
|
return Security.calc_hash(key)
|
||||||
|
|
||||||
|
|||||||
@@ -13,16 +13,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Common", "Azaion.Com
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Dataset", "Azaion.Dataset\Azaion.Dataset.csproj", "{01A5CA37-A62E-4EF3-8678-D72CD9525677}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.Dataset", "Azaion.Dataset\Azaion.Dataset.csproj", "{01A5CA37-A62E-4EF3-8678-D72CD9525677}"
|
||||||
EndProject
|
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}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azaion.CommonSecurity", "Azaion.CommonSecurity\Azaion.CommonSecurity.csproj", "{E0C7176D-2E91-4928-B3C1-55CC91C8F77D}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{CF141A48-8002-4006-81CF-6B85AE5B0B5F}"
|
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.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.Build.0 = Release|Any CPU
|
||||||
{01A5CA37-A62E-4EF3-8678-D72CD9525677}.Release|Any CPU.Deploy.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.ActiveCfg = Debug|Any CPU
|
||||||
{E0C7176D-2E91-4928-B3C1-55CC91C8F77D}.Debug|Any CPU.Build.0 = 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
|
{E0C7176D-2E91-4928-B3C1-55CC91C8F77D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -95,8 +77,6 @@ Global
|
|||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{32C4747F-F700-44FD-B4ED-21B4A66B5FAB} = {C307BE2E-FFCC-4BD7-AD89-C82D40B65D03}
|
|
||||||
{A2E3D3AE-5DB7-4342-BE20-88A9D1B0C05E} = {C307BE2E-FFCC-4BD7-AD89-C82D40B65D03}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {788BD4AD-E4EC-43A1-85A0-AEC644BD8D48}
|
SolutionGuid = {788BD4AD-E4EC-43A1-85A0-AEC644BD8D48}
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ public partial class App
|
|||||||
loaderClient.StartClient();
|
loaderClient.StartClient();
|
||||||
#endif
|
#endif
|
||||||
loaderClient.Connect(); //Client app should be already started by LoaderUI
|
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);
|
var azaionApi = new AzaionApi(new HttpClient { BaseAddress = new Uri(initConfig.InferenceClientConfig.ApiUrl) }, _cache, credentials);
|
||||||
|
|
||||||
_host = Host.CreateDefaultBuilder()
|
_host = Host.CreateDefaultBuilder()
|
||||||
@@ -167,7 +167,7 @@ public partial class App
|
|||||||
services.AddSingleton<ISatelliteDownloader, SatelliteDownloader>();
|
services.AddSingleton<ISatelliteDownloader, SatelliteDownloader>();
|
||||||
|
|
||||||
services.AddHttpClient();
|
services.AddHttpClient();
|
||||||
services.AddSingleton(azaionApi);
|
services.AddSingleton<IAzaionApi>(azaionApi);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
services.AddSingleton<IConfigUpdater, ConfigUpdater>();
|
services.AddSingleton<IConfigUpdater, ConfigUpdater>();
|
||||||
@@ -201,9 +201,9 @@ public partial class App
|
|||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
Annotation.InitializeDirs(_host.Services.GetRequiredService<IOptions<DirectoriesConfig>>().Value);
|
Annotation.InitializeDirs(_host.Services.GetRequiredService<IOptions<DirectoriesConfig>>().Value);
|
||||||
var datasetExplorer = _host.Services.GetRequiredService<DatasetExplorer>();
|
_host.Services.GetRequiredService<DatasetExplorer>();
|
||||||
datasetExplorer.Show();
|
// datasetExplorer.Show();
|
||||||
datasetExplorer.Hide();
|
// datasetExplorer.Hide();
|
||||||
|
|
||||||
_mediator = _host.Services.GetRequiredService<IMediator>();
|
_mediator = _host.Services.GetRequiredService<IMediator>();
|
||||||
|
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
<Window x:Class="Azaion.Annotator.Annotator"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Title="Azaion Annotator" Height="800" Width="1100"
|
|
||||||
WindowState="Maximized"
|
|
||||||
>
|
|
||||||
<Grid Background="Black"
|
|
||||||
ShowGridLines="False">
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="*"></RowDefinition>
|
|
||||||
<RowDefinition Height="*"></RowDefinition>
|
|
||||||
<RowDefinition Height="*"></RowDefinition>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="0"
|
|
||||||
Padding="20 20"
|
|
||||||
Foreground="Brown"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
FontSize="30"
|
|
||||||
TextAlignment="Center">
|
|
||||||
Під час запуску виникла помилка!
|
|
||||||
<LineBreak /><LineBreak />
|
|
||||||
Error happened during the launch!
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="1"
|
|
||||||
Padding="10"
|
|
||||||
TextAlignment="Center"
|
|
||||||
Foreground="Red"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
FontSize="20"
|
|
||||||
Name="TbError"
|
|
||||||
Text="{Binding ErrorMessage}"/>
|
|
||||||
<TextBlock
|
|
||||||
Grid.Row="2"
|
|
||||||
TextAlignment="Center"
|
|
||||||
Foreground="Brown"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
FontSize="20">
|
|
||||||
Будь ласка перевірте правильність email чи паролю! <LineBreak />
|
|
||||||
Також зауважте, що запуск можливий лише з одного конкретного компьютера, копіювання заборонене! <LineBreak/> <LineBreak/>
|
|
||||||
Для подальшого вирішення проблеми ви можете зв'язатися з нами: hi@azaion.com
|
|
||||||
<LineBreak /><LineBreak /><LineBreak />
|
|
||||||
Please check your email or password! <LineBreak />
|
|
||||||
The program is restricted to start only from particular hardware, copying is forbidden! <LineBreak/> <LineBreak/>
|
|
||||||
For the further guidance, please feel free to contact us: hi@azaion.com
|
|
||||||
</TextBlock>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace Azaion.Annotator;
|
|
||||||
|
|
||||||
public partial class Annotator
|
|
||||||
{
|
|
||||||
public Annotator()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using Azaion.Common.Events;
|
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace Azaion.Annotator;
|
|
||||||
|
|
||||||
public class AnnotatorEventHandler(Annotator annotator) : INotificationHandler<LoadErrorEvent>
|
|
||||||
{
|
|
||||||
public Task Handle(LoadErrorEvent notification, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
annotator.Dispatcher.Invoke(() => annotator.TbError.Text = notification.Error);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
using Azaion.Common.DTO;
|
|
||||||
|
|
||||||
namespace Azaion.Annotator;
|
|
||||||
|
|
||||||
public class AnnotatorModule : IAzaionModule
|
|
||||||
{
|
|
||||||
public string Name => "Анотатор";
|
|
||||||
|
|
||||||
public string SvgIcon =>
|
|
||||||
@"<?xml version=""1.0"" encoding=""utf-8""?>
|
|
||||||
<!-- Generator: Adobe Illustrator 28.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<svg version=""1.1"" id=""Layer_1"" xmlns=""http://www.w3.org/2000/svg"" xmlns:xlink=""http://www.w3.org/1999/xlink"" x=""0px"" y=""0px""
|
|
||||||
viewBox=""0 0 800 800"" style=""enable-background:new 0 0 800 800;"" xml:space=""preserve"">
|
|
||||||
<style type=""text/css"">
|
|
||||||
.st0{fill:#FFFFFF;}
|
|
||||||
.st1{opacity:0.75;fill:#006932;stroke:#000000;stroke-width:20;stroke-miterlimit:10;}
|
|
||||||
.st2{opacity:0.75;fill:#6C0A0B;stroke:#000000;stroke-width:20;stroke-miterlimit:10;}
|
|
||||||
.st3{fill:#FFFFFF;stroke:#434444;stroke-width:8;stroke-miterlimit:10;}
|
|
||||||
</style>
|
|
||||||
<g id=""SVGRepo_bgCarrier"">
|
|
||||||
</g>
|
|
||||||
<g id=""SVGRepo_tracerCarrier"">
|
|
||||||
</g>
|
|
||||||
<g id=""SVGRepo_iconCarrier"">
|
|
||||||
<g transform=""translate(1)"">
|
|
||||||
<g>
|
|
||||||
<polygon class=""st0"" points=""465.7,93.3 545.7,93.3 545.7,13.3 465.7,13.3""/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<rect x=""43.3"" y=""53.3"" class=""st1"" width=""609.7"" height=""301""/>
|
|
||||||
<rect x=""443.2"" y=""400"" class=""st2"" width=""285.8"" height=""363""/>
|
|
||||||
<g>
|
|
||||||
<rect x=""19"" y=""325"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""17.5"" y=""166"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""17.5"" y=""27"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""325.5"" y=""329"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""624.5"" y=""325"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""626.5"" y=""168"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""626.5"" y=""27"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""323.5"" y=""27"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""419.8"" y=""377.3"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""698.7"" y=""378.3"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""418.5"" y=""733.2"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""698.7"" y=""736.5"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""415.8"" y=""555.7"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
<rect x=""701.2"" y=""551.7"" class=""st3"" width=""53"" height=""53""/>
|
|
||||||
</g>
|
|
||||||
</svg>";
|
|
||||||
|
|
||||||
public Type MainWindowType => typeof(Annotator);
|
|
||||||
|
|
||||||
public WindowEnum WindowEnum => WindowEnum.Annotator;
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
)]
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<UseWPF>true</UseWPF>
|
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="libc.translation" Version="7.1.1" />
|
|
||||||
<PackageReference Include="LibVLCSharp" Version="3.9.1" />
|
|
||||||
<PackageReference Include="LibVLCSharp.WPF" Version="3.9.1" />
|
|
||||||
<PackageReference Include="MediatR" Version="12.5.0" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.5" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
||||||
<PackageReference Include="RangeTree" Version="3.0.1" />
|
|
||||||
<PackageReference Include="SkiaSharp" Version="2.88.9" />
|
|
||||||
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.21" />
|
|
||||||
<PackageReference Include="WindowsAPICodePack" Version="7.0.4" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\Azaion.Common\Azaion.Common.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="DTO\" />
|
|
||||||
<Folder Include="Extensions\" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace Azaion.Annotator.DTO;
|
|
||||||
|
|
||||||
public class FormState;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace Azaion.Annotator.Extensions;
|
|
||||||
|
|
||||||
public class VLCFrameExtractor;
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<Window x:Class="Azaion.Annotator.HelpWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Height="700" Width="800"
|
|
||||||
ResizeMode="NoResize"
|
|
||||||
Topmost="True"
|
|
||||||
WindowStartupLocation="CenterScreen">
|
|
||||||
</Window>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace Azaion.Annotator;
|
|
||||||
|
|
||||||
public partial class HelpWindow;
|
|
||||||
@@ -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)
|
|
||||||
)]
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0-windows</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<UseWPF>true</UseWPF>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Page Update="DatasetExplorer.xaml">
|
|
||||||
<Generator>MSBuild:Compile</Generator>
|
|
||||||
<XamlRuntime>Wpf</XamlRuntime>
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</Page>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.5" />
|
|
||||||
<PackageReference Include="ScottPlot.WPF" Version="5.0.46" />
|
|
||||||
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\Azaion.Common\Azaion.Common.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
using System.Windows.Media.Imaging;
|
|
||||||
|
|
||||||
namespace Azaion.Dataset;
|
|
||||||
|
|
||||||
public static class BitmapExtensions
|
|
||||||
{
|
|
||||||
public static async Task<BitmapImage> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<Window x:Class="Azaion.Dataset.DatasetExplorer"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
Title="Переглядач анотацій" Height="900" Width="1200"
|
|
||||||
WindowState="Maximized">
|
|
||||||
|
|
||||||
<TextBlock Padding="20 80"
|
|
||||||
Background="Black"
|
|
||||||
Foreground="Brown"
|
|
||||||
TextWrapping="Wrap"
|
|
||||||
FontSize="32"
|
|
||||||
TextAlignment="Center">
|
|
||||||
Будь ласка перевірте правильність email чи паролю! <LineBreak />
|
|
||||||
Також зауважте, що запуск можливий лише з одного конкретного компьютера, копіювання заборонене! <LineBreak/> <LineBreak/>
|
|
||||||
Для подальшого вирішення проблеми ви можете зв'язатися з нами: hi@azaion.com
|
|
||||||
<LineBreak /><LineBreak /><LineBreak />
|
|
||||||
Please check your email or password! <LineBreak />
|
|
||||||
The program is restricted to start only from particular hardware, copying is forbidden! <LineBreak/> <LineBreak/>
|
|
||||||
For the further guidance, please feel free to contact us: hi@azaion.com
|
|
||||||
</TextBlock>
|
|
||||||
</Window>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace Azaion.Dataset;
|
|
||||||
|
|
||||||
public partial class DatasetExplorer;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using Azaion.Common.DTO;
|
|
||||||
|
|
||||||
namespace Azaion.Dataset;
|
|
||||||
|
|
||||||
public class DatasetExplorerModule : IAzaionModule
|
|
||||||
{
|
|
||||||
public string Name => "Переглядач";
|
|
||||||
|
|
||||||
public string SvgIcon =>
|
|
||||||
@"<?xml version=""1.0"" encoding=""utf-8""?>
|
|
||||||
<svg width=""800px"" height=""800px"" viewBox=""0 0 24 24"" xmlns=""http://www.w3.org/2000/svg"" fill=""none"" stroke=""#000000"" stroke-width=""1"" stroke-linecap=""round"" stroke-linejoin=""miter"">
|
|
||||||
<rect x=""2"" y=""2"" width=""8"" height=""8"" rx=""0"" fill=""#059cf7"" opacity=""0.8""></rect>
|
|
||||||
<rect x=""2"" y=""14"" width=""8"" height=""8"" rx=""0"" fill=""#059cf7"" opacity=""0.8""></rect>
|
|
||||||
<rect x=""14"" y=""2"" width=""8"" height=""8"" rx=""0"" fill=""#059cf7"" opacity=""0.8""></rect>
|
|
||||||
<rect x=""14"" y=""14"" width=""8"" height=""8"" rx=""0"" fill=""#059cf7"" opacity=""0.8""></rect>
|
|
||||||
<rect x=""2"" y=""2"" width=""8"" height=""8"" rx=""0""></rect>
|
|
||||||
<rect x=""2"" y=""14"" width=""8"" height=""8"" rx=""0""></rect>
|
|
||||||
<rect x=""14"" y=""2"" width=""8"" height=""8"" rx=""0""></rect>
|
|
||||||
<rect x=""14"" y=""14"" width=""8"" height=""8"" rx=""0""></rect>
|
|
||||||
</svg>";
|
|
||||||
|
|
||||||
public Type MainWindowType => typeof(DatasetExplorer);
|
|
||||||
|
|
||||||
public WindowEnum WindowEnum => WindowEnum.DatasetExplorer;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
Reference in New Issue
Block a user