make python app load a bit eariler, making startup a bit faster

This commit is contained in:
Alex Bezdieniezhnykh
2025-02-13 18:13:15 +02:00
parent e329e5bb67
commit cfd5483a18
31 changed files with 183 additions and 124 deletions
+2 -2
View File
@@ -148,13 +148,13 @@
Grid.Column="0" Grid.Column="0"
Name="LvFiles" Name="LvFiles"
Background="Black" Background="Black"
SelectedItem="{Binding Path=SelectedVideo}" Foreground="#FFA4AFCC" SelectedItem="{Binding Path=SelectedVideo}" Foreground="#FFEEEEEE"
> >
<ListView.Resources> <ListView.Resources>
<Style TargetType="{x:Type ListViewItem}"> <Style TargetType="{x:Type ListViewItem}">
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{Binding HasAnnotations}" Value="true"> <DataTrigger Binding="{Binding HasAnnotations}" Value="true">
<Setter Property="Background" Value="Gray"/> <Setter Property="Background" Value="#FF505050"/>
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
<EventSetter Event="ContextMenuOpening" Handler="LvFilesContextOpening"></EventSetter> <EventSetter Event="ContextMenuOpening" Handler="LvFilesContextOpening"></EventSetter>
+3 -8
View File
@@ -41,7 +41,6 @@ public partial class Annotator
private readonly AnnotationService _annotationService; private readonly AnnotationService _annotationService;
private readonly IDbFactory _dbFactory; private readonly IDbFactory _dbFactory;
private readonly IInferenceService _inferenceService; private readonly IInferenceService _inferenceService;
private readonly IResourceLoader _resourceLoader;
private ObservableCollection<DetectionClass> AnnotationClasses { get; set; } = new(); private ObservableCollection<DetectionClass> AnnotationClasses { get; set; } = new();
private bool _suspendLayout; private bool _suspendLayout;
@@ -71,8 +70,7 @@ public partial class Annotator
ILogger<Annotator> logger, ILogger<Annotator> logger,
AnnotationService annotationService, AnnotationService annotationService,
IDbFactory dbFactory, IDbFactory dbFactory,
IInferenceService inferenceService, IInferenceService inferenceService)
IResourceLoader resourceLoader)
{ {
InitializeComponent(); InitializeComponent();
_appConfig = appConfig.Value; _appConfig = appConfig.Value;
@@ -86,7 +84,6 @@ public partial class Annotator
_annotationService = annotationService; _annotationService = annotationService;
_dbFactory = dbFactory; _dbFactory = dbFactory;
_inferenceService = inferenceService; _inferenceService = inferenceService;
_resourceLoader = resourceLoader;
Loaded += OnLoaded; Loaded += OnLoaded;
Closed += OnFormClosed; Closed += OnFormClosed;
@@ -401,7 +398,6 @@ public partial class Annotator
private void OnFormClosed(object? sender, EventArgs e) private void OnFormClosed(object? sender, EventArgs e)
{ {
_resourceLoader.StopPython();
MainCancellationSource.Cancel(); MainCancellationSource.Cancel();
DetectionCancellationSource.Cancel(); DetectionCancellationSource.Cancel();
_mediaPlayer.Stop(); _mediaPlayer.Stop();
@@ -454,6 +450,7 @@ public partial class Annotator
_appConfig.DirectoriesConfig.VideosDirectory = dlg.FileName; _appConfig.DirectoriesConfig.VideosDirectory = dlg.FileName;
TbFolder.Text = dlg.FileName; TbFolder.Text = dlg.FileName;
await Task.CompletedTask;
} }
private void TbFilter_OnTextChanged(object sender, TextChangedEventArgs e) private void TbFilter_OnTextChanged(object sender, TextChangedEventArgs e)
@@ -497,8 +494,6 @@ public partial class Annotator
LvFilesContextMenu.DataContext = listItem!.DataContext; LvFilesContextMenu.DataContext = listItem!.DataContext;
} }
private (TimeSpan Time, List<Detection> Detections)? _previousDetection;
public void AutoDetect(object sender, RoutedEventArgs e) public void AutoDetect(object sender, RoutedEventArgs e)
{ {
if (IsInferenceNow) if (IsInferenceNow)
@@ -560,7 +555,7 @@ public partial class Annotator
try try
{ {
var annotation = await _annotationService.SaveAnnotation(annotationImage); var annotation = await _annotationService.SaveAnnotation(annotationImage);
if (annotation.OriginalMediaName != _formState.CurrentMedia.FName) if (annotation.OriginalMediaName != _formState.CurrentMedia?.FName)
return; return;
AddAnnotation(annotation); AddAnnotation(annotation);
@@ -14,4 +14,5 @@ public class AIRecognitionConfig
[Key(nameof(TrackingIntersectionThreshold))] public double TrackingIntersectionThreshold { get; set; } [Key(nameof(TrackingIntersectionThreshold))] public double TrackingIntersectionThreshold { get; set; }
[Key(nameof(Data))] public byte[] Data { get; set; } [Key(nameof(Data))] public byte[] Data { get; set; }
[Key(nameof(Paths))] public List<string> Paths { get; set; }
} }
+3 -3
View File
@@ -26,9 +26,9 @@ public class PythonInferenceService(ILogger<PythonInferenceService> logger, IOpt
dealer.Options.Identity = Encoding.UTF8.GetBytes(clientId.ToString("N")); dealer.Options.Identity = Encoding.UTF8.GetBytes(clientId.ToString("N"));
dealer.Connect($"tcp://{SecurityConstants.ZMQ_HOST}:{SecurityConstants.ZMQ_PORT}"); dealer.Connect($"tcp://{SecurityConstants.ZMQ_HOST}:{SecurityConstants.ZMQ_PORT}");
var data = MessagePackSerializer.Serialize(aiConfigOptions.Value); var aiConfig = aiConfigOptions.Value;
var filename = JsonConvert.SerializeObject(mediaPaths); aiConfig.Paths = mediaPaths;
dealer.SendFrame(MessagePackSerializer.Serialize(new RemoteCommand(CommandType.Inference, filename, data))); dealer.SendFrame(RemoteCommand.Serialize(CommandType.Inference, aiConfig));
while (!ct.IsCancellationRequested) while (!ct.IsCancellationRequested)
{ {
+10 -1
View File
@@ -1,7 +1,16 @@
namespace Azaion.CommonSecurity.DTO; using MessagePack;
namespace Azaion.CommonSecurity.DTO;
[MessagePackObject]
public class ApiCredentials(string email, string password) : EventArgs public class ApiCredentials(string email, string password) : EventArgs
{ {
[Key(nameof(Email))]
public string Email { get; set; } = email; public string Email { get; set; } = email;
[Key(nameof(Password))]
public string Password { get; set; } = password; public string Password { get; set; } = password;
[Key(nameof(Folder))]
public string Folder { get; set; } = null!;
} }
@@ -3,22 +3,36 @@
namespace Azaion.CommonSecurity.DTO.Commands; namespace Azaion.CommonSecurity.DTO.Commands;
[MessagePackObject] [MessagePackObject]
public class RemoteCommand(CommandType commandType, string? filename = null, byte[]? data = null) public class RemoteCommand(CommandType commandType, byte[]? data = null)
{ {
[Key("CommandType")] [Key("CommandType")]
public CommandType CommandType { get; set; } = commandType; public CommandType CommandType { get; set; } = commandType;
[Key("Filename")]
public string? Filename { get; set; } = filename;
[Key("Data")] [Key("Data")]
public byte[]? Data { get; set; } = data; public byte[]? Data { get; set; } = data;
public static byte[] Serialize<T>(CommandType commandType, T data) where T : class
{
var dataBytes = MessagePackSerializer.Serialize(data);
return MessagePackSerializer.Serialize(new RemoteCommand(commandType, dataBytes ));
} }
}
[MessagePackObject]
public class LoadFileData(string filename, string? folder = null )
{
[Key(nameof(Folder))]
public string? Folder { get; set; } = folder;
[Key(nameof(Filename))]
public string Filename { get; set; } = filename;
}
public enum CommandType public enum CommandType
{ {
None = 0, None = 0,
GetUser = 10, Login = 10,
Load = 20, Load = 20,
Inference = 30, Inference = 30,
StopInference = 40, StopInference = 40,
+2 -2
View File
@@ -6,8 +6,8 @@ namespace Azaion.CommonSecurity.DTO;
[MessagePackObject] [MessagePackObject]
public class User public class User
{ {
[Key("i")]public string Id { get; set; } [Key("i")] public string Id { get; set; } = "";
[Key("e")]public string Email { get; set; } [Key("e")] public string Email { get; set; } = "";
[Key("r")]public RoleEnum Role { get; set; } [Key("r")]public RoleEnum Role { get; set; }
//For deserializing //For deserializing
@@ -10,7 +10,7 @@ namespace Azaion.CommonSecurity.Services;
public interface IResourceLoader public interface IResourceLoader
{ {
MemoryStream LoadFileFromPython(string fileName); MemoryStream LoadFileFromPython(string fileName, string? folder = null);
void StopPython(); void StopPython();
} }
@@ -25,29 +25,22 @@ public class PythonResourceLoader : IResourceLoader, IAuthProvider
private readonly DealerSocket _dealer = new(); private readonly DealerSocket _dealer = new();
private readonly Guid _clientId = Guid.NewGuid(); private readonly Guid _clientId = Guid.NewGuid();
public User CurrentUser { get; } public User CurrentUser { get; set; }
public PythonResourceLoader(ApiConfig apiConfig, ApiCredentials credentials, AzaionApiClient api) public PythonResourceLoader()
{ {
StartPython(apiConfig, credentials); StartPython();
_dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N")); _dealer.Options.Identity = Encoding.UTF8.GetBytes(_clientId.ToString("N"));
_dealer.Connect($"tcp://{SecurityConstants.ZMQ_HOST}:{SecurityConstants.ZMQ_PORT}"); _dealer.Connect($"tcp://{SecurityConstants.ZMQ_HOST}:{SecurityConstants.ZMQ_PORT}");
_dealer.SendFrame(MessagePackSerializer.Serialize(new RemoteCommand(CommandType.GetUser)));
var user = _dealer.Get<User>();
if (user == null)
throw new Exception("Can't get user from Auth provider");
CurrentUser = user;
} }
private void StartPython( ApiConfig apiConfig, ApiCredentials credentials) private void StartPython()
{ {
using var process = new Process(); using var process = new Process();
process.StartInfo = new ProcessStartInfo process.StartInfo = new ProcessStartInfo
{ {
FileName = SecurityConstants.AZAION_INFERENCE_PATH, FileName = SecurityConstants.AZAION_INFERENCE_PATH,
Arguments = $"-e {credentials.Email} -p {credentials.Password} -f {apiConfig.ResourcesFolder}", //Arguments = $"-e {credentials.Email} -p {credentials.Password} -f {apiConfig.ResourcesFolder}",
//UseShellExecute = false, //UseShellExecute = false,
//RedirectStandardOutput = true, //RedirectStandardOutput = true,
// RedirectStandardError = true, // RedirectStandardError = true,
@@ -59,17 +52,27 @@ public class PythonResourceLoader : IResourceLoader, IAuthProvider
process.Start(); process.Start();
} }
public void Login(ApiCredentials credentials)
{
_dealer.SendFrame(RemoteCommand.Serialize(CommandType.Login, credentials));
var user = _dealer.Get<User>();
if (user == null)
throw new Exception("Can't get user from Auth provider");
CurrentUser = user;
}
public void StopPython() public void StopPython()
{ {
_dealer.SendFrame(MessagePackSerializer.Serialize(new RemoteCommand(CommandType.Exit))); _dealer.SendFrame(MessagePackSerializer.Serialize(new RemoteCommand(CommandType.Exit)));
_dealer?.Close(); _dealer.Close();
} }
public MemoryStream LoadFileFromPython(string fileName) public MemoryStream LoadFileFromPython(string fileName, string? folder = null)
{ {
try try
{ {
_dealer.SendFrame(MessagePackSerializer.Serialize(new RemoteCommand(CommandType.Load, fileName))); _dealer.SendFrame(RemoteCommand.Serialize(CommandType.Load, new LoadFileData(fileName, folder)));
if (!_dealer.TryReceiveFrameBytes(TimeSpan.FromSeconds(3), out var bytes)) if (!_dealer.TryReceiveFrameBytes(TimeSpan.FromSeconds(3), out var bytes))
throw new Exception($"Unable to receive {fileName}"); throw new Exception($"Unable to receive {fileName}");
+5 -2
View File
@@ -1,7 +1,10 @@
from user cimport User from user cimport User
from credentials cimport Credentials
from file_data cimport FileData
cdef class ApiClient: cdef class ApiClient:
cdef str email, password, token, folder, token_file, api_url cdef public Credentials credentials
cdef str token, folder, api_url
cdef User user cdef User user
cdef get_encryption_key(self, str hardware_hash) cdef get_encryption_key(self, str hardware_hash)
@@ -9,7 +12,7 @@ cdef class ApiClient:
cdef set_token(self, str token) cdef set_token(self, str token)
cdef get_user(self) cdef get_user(self)
cdef load_bytes(self, str filename) cdef load_bytes(self, FileData file_data)
cdef load_ai_model(self) cdef load_ai_model(self)
cdef load_queue_config(self) cdef load_queue_config(self)
+11 -18
View File
@@ -9,33 +9,25 @@ from hardware_service cimport HardwareService, HardwareInfo
from security cimport Security from security cimport Security
from io import BytesIO from io import BytesIO
from user cimport User, RoleEnum from user cimport User, RoleEnum
from file_data cimport FileData
cdef class ApiClient: cdef class ApiClient:
"""Handles API authentication and downloading of the AI model.""" """Handles API authentication and downloading of the AI model."""
def __init__(self, str email, str password, str folder): def __init__(self):
self.email = email self.credentials = None
self.password = password
self.folder = folder
self.user = None self.user = None
if os.path.exists(<str>constants.TOKEN_FILE):
with open(<str>constants.TOKEN_FILE, "r") as file:
self.set_token(<str>file.read().strip())
else:
self.token = None self.token = None
cdef get_encryption_key(self, str hardware_hash): cdef get_encryption_key(self, str hardware_hash):
cdef str key = f'{self.email}-{self.password}-{hardware_hash}-#%@AzaionKey@%#---' cdef str key = f'{self.credentials.email}-{self.credentials.password}-{hardware_hash}-#%@AzaionKey@%#---'
return Security.calc_hash(key) return Security.calc_hash(key)
cdef login(self): cdef login(self):
response = requests.post(f"{constants.API_URL}/login", response = requests.post(f"{constants.API_URL}/login",
json={"email": self.email, "password": self.password}) json={"email": self.credentials.email, "password": self.credentials.password})
response.raise_for_status() response.raise_for_status()
token = response.json()["token"] token = response.json()["token"]
self.set_token(token) self.set_token(token)
with open(<str>constants.TOKEN_FILE, 'w') as file:
file.write(token)
cdef set_token(self, str token): cdef set_token(self, str token):
self.token = token self.token = token
@@ -68,14 +60,15 @@ cdef class ApiClient:
self.login() self.login()
return self.user return self.user
cdef load_bytes(self, str filename): cdef load_bytes(self, FileData file_data):
folder = file_data.folder or self.credentials.folder
hardware_service = HardwareService() hardware_service = HardwareService()
cdef HardwareInfo hardware = hardware_service.get_hardware_info() cdef HardwareInfo hardware = hardware_service.get_hardware_info()
if self.token is None: if self.token is None:
self.login() self.login()
url = f"{constants.API_URL}/resources/get/{self.folder}" url = f"{constants.API_URL}/resources/get/{folder}"
headers = { headers = {
"Authorization": f"Bearer {self.token}", "Authorization": f"Bearer {self.token}",
"Content-Type": "application/json" "Content-Type": "application/json"
@@ -83,9 +76,9 @@ cdef class ApiClient:
payload = json.dumps( payload = json.dumps(
{ {
"password": self.password, "password": self.credentials.password,
"hardware": hardware.to_json_object(), "hardware": hardware.to_json_object(),
"fileName": filename "fileName": file_data.filename
}, indent=4) }, indent=4)
response = requests.post(url, data=payload, headers=headers, stream=True) response = requests.post(url, data=payload, headers=headers, stream=True)
@@ -104,7 +97,7 @@ cdef class ApiClient:
stream = BytesIO(response.raw.read()) stream = BytesIO(response.raw.read())
data = Security.decrypt_to(stream, key) data = Security.decrypt_to(stream, key)
print(f'loaded file: {filename}, {len(data)} bytes') print(f'loaded file: {file_data.filename}, {len(data)} bytes')
return data return data
cdef load_ai_model(self): cdef load_ai_model(self):
+4 -2
View File
@@ -7,13 +7,15 @@ pyinstaller --onefile ^
--collect-all cryptography ^ --collect-all cryptography ^
--collect-all cv2 ^ --collect-all cv2 ^
--collect-all onnxruntime ^ --collect-all onnxruntime ^
--hidden-import constants ^
--hidden-import annotation ^
--hidden-import credentials ^
--hidden-import file_data ^
--hidden-import user ^ --hidden-import user ^
--hidden-import security ^ --hidden-import security ^
--hidden-import secure_model ^ --hidden-import secure_model ^
--hidden-import api_client ^ --hidden-import api_client ^
--hidden-import hardware_service ^ --hidden-import hardware_service ^
--hidden-import constants ^
--hidden-import annotation ^
--hidden-import remote_command ^ --hidden-import remote_command ^
--hidden-import ai_config ^ --hidden-import ai_config ^
--hidden-import inference ^ --hidden-import inference ^
-1
View File
@@ -5,7 +5,6 @@ cdef str COMMANDS_QUEUE # Name of the commands queue in rabbit
cdef str ANNOTATIONS_QUEUE # Name of the annotations queue in rabbit cdef str ANNOTATIONS_QUEUE # Name of the annotations queue in rabbit
cdef str API_URL # Base URL for the external API cdef str API_URL # Base URL for the external API
cdef str TOKEN_FILE # Name of the token file where temporary token would be stored
cdef str QUEUE_CONFIG_FILENAME # queue config filename to load from api cdef str QUEUE_CONFIG_FILENAME # queue config filename to load from api
cdef str AI_MODEL_FILE # AI Model file cdef str AI_MODEL_FILE # AI Model file
-1
View File
@@ -5,7 +5,6 @@ cdef str COMMANDS_QUEUE = "azaion-commands"
cdef str ANNOTATIONS_QUEUE = "azaion-annotations" cdef str ANNOTATIONS_QUEUE = "azaion-annotations"
cdef str API_URL = "https://api.azaion.com" # Base URL for the external API cdef str API_URL = "https://api.azaion.com" # Base URL for the external API
cdef str TOKEN_FILE = "token"
cdef str QUEUE_CONFIG_FILENAME = "secured-config.json" cdef str QUEUE_CONFIG_FILENAME = "secured-config.json"
cdef str AI_MODEL_FILE = "azaion.onnx" cdef str AI_MODEL_FILE = "azaion.onnx"
+7
View File
@@ -0,0 +1,7 @@
cdef class Credentials:
cdef public str email
cdef public str password
cdef public str folder
@staticmethod
cdef from_msgpack(bytes data)
+17
View File
@@ -0,0 +1,17 @@
from msgpack import unpackb
cdef class Credentials:
def __init__(self, str email, str password, str folder):
self.email = email
self.password = password
self.folder = folder
@staticmethod
cdef from_msgpack(bytes data):
unpacked = unpackb(data, strict_map_key=False)
return Credentials(
unpacked.get("Email"),
unpacked.get("Password"),
unpacked.get("Folder"))
+6
View File
@@ -0,0 +1,6 @@
cdef class FileData:
cdef public str folder
cdef public str filename
@staticmethod
cdef from_msgpack(bytes data)
+14
View File
@@ -0,0 +1,14 @@
from msgpack import unpackb
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"))
+15 -15
View File
@@ -9,16 +9,10 @@ from annotation cimport Annotation
from inference cimport Inference from inference cimport Inference
from remote_command cimport RemoteCommand, CommandType from remote_command cimport RemoteCommand, CommandType
from remote_command_handler cimport RemoteCommandHandler from remote_command_handler cimport RemoteCommandHandler
from credentials cimport Credentials
from file_data cimport FileData
from user cimport User from user cimport User
cdef class ParsedArguments:
cdef str email, password, folder;
def __init__(self, str email, str password, str folder):
self.email = email
self.password = password
self.folder = folder
cdef class CommandProcessor: cdef class CommandProcessor:
cdef ApiClient api_client cdef ApiClient api_client
cdef RemoteCommandHandler remote_handler cdef RemoteCommandHandler remote_handler
@@ -26,8 +20,8 @@ cdef class CommandProcessor:
cdef bint running cdef bint running
cdef Inference inference cdef Inference inference
def __init__(self, args: ParsedArguments): def __init__(self):
self.api_client = ApiClient(args.email, args.password, args.folder) self.api_client = ApiClient()
self.remote_handler = RemoteCommandHandler(self.on_command) self.remote_handler = RemoteCommandHandler(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()
@@ -49,11 +43,10 @@ cdef class CommandProcessor:
cdef on_command(self, RemoteCommand command): cdef on_command(self, RemoteCommand command):
try: try:
if command.command_type == CommandType.GET_USER: if command.command_type == CommandType.LOGIN:
self.get_user(command, self.api_client.get_user()) self.login(command)
elif command.command_type == CommandType.LOAD: elif command.command_type == CommandType.LOAD:
response = self.api_client.load_bytes(command.filename) self.load_file(command)
self.remote_handler.send(command.client_id, response)
elif command.command_type == CommandType.INFERENCE: elif command.command_type == CommandType.INFERENCE:
self.inference_queue.put(command) self.inference_queue.put(command)
elif command.command_type == CommandType.STOP_INFERENCE: elif command.command_type == CommandType.STOP_INFERENCE:
@@ -66,9 +59,16 @@ cdef class CommandProcessor:
except Exception as e: except Exception as e:
print(f"Error handling client: {e}") print(f"Error handling client: {e}")
cdef get_user(self, RemoteCommand command, User user): cdef login(self, RemoteCommand command):
cdef User user
self.api_client.credentials = Credentials.from_msgpack(command.data)
user = self.api_client.get_user()
self.remote_handler.send(command.client_id, user.serialize()) self.remote_handler.send(command.client_id, user.serialize())
cdef load_file(self, RemoteCommand command):
response = self.api_client.load_bytes(FileData.from_msgpack(command.data))
self.remote_handler.send(command.client_id, response)
cdef on_annotation(self, RemoteCommand cmd, Annotation annotation): cdef on_annotation(self, RemoteCommand cmd, Annotation annotation):
data = annotation.serialize() data = annotation.serialize()
self.remote_handler.send(cmd.client_id, data) self.remote_handler.send(cmd.client_id, data)
+1 -2
View File
@@ -1,5 +1,5 @@
cdef enum CommandType: cdef enum CommandType:
GET_USER = 10 LOGIN = 10
LOAD = 20 LOAD = 20
INFERENCE = 30 INFERENCE = 30
STOP_INFERENCE = 40 STOP_INFERENCE = 40
@@ -8,7 +8,6 @@ cdef enum CommandType:
cdef class RemoteCommand: cdef class RemoteCommand:
cdef public bytes client_id cdef public bytes client_id
cdef CommandType command_type cdef CommandType command_type
cdef str filename
cdef bytes data cdef bytes data
@staticmethod @staticmethod
+4 -5
View File
@@ -1,23 +1,22 @@
import msgpack import msgpack
cdef class RemoteCommand: cdef class RemoteCommand:
def __init__(self, CommandType command_type, str filename, bytes data): def __init__(self, CommandType command_type, bytes data):
self.command_type = command_type self.command_type = command_type
self.filename = filename
self.data = data self.data = data
def __str__(self): def __str__(self):
command_type_names = { command_type_names = {
10: "GET_USER", 10: "LOGIN",
20: "LOAD", 20: "LOAD",
30: "INFERENCE", 30: "INFERENCE",
40: "STOP_INFERENCE", 40: "STOP_INFERENCE",
100: "EXIT" 100: "EXIT"
} }
data_str = f'. Data: {len(self.data)} bytes' if self.data else '' data_str = f'. Data: {len(self.data)} bytes' if self.data else ''
return f'{command_type_names[self.command_type]}: {self.filename}{data_str}' return f'{command_type_names[self.command_type]}: {data_str}'
@staticmethod @staticmethod
cdef from_msgpack(bytes data): cdef from_msgpack(bytes data):
unpacked = msgpack.unpackb(data, strict_map_key=False) unpacked = msgpack.unpackb(data, strict_map_key=False)
return RemoteCommand(unpacked.get("CommandType"), unpacked.get("Filename"), unpacked.get("Data")) return RemoteCommand(unpacked.get("CommandType"), unpacked.get("Data"))
+2
View File
@@ -5,6 +5,8 @@ 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('security', ['security.pyx']), Extension('security', ['security.pyx']),
Extension('hardware_service', ['hardware_service.pyx'], extra_compile_args=["-g"], extra_link_args=["-g"]), Extension('hardware_service', ['hardware_service.pyx'], extra_compile_args=["-g"], extra_link_args=["-g"]),
Extension('remote_command', ['remote_command.pyx']), Extension('remote_command', ['remote_command.pyx']),
+5 -14
View File
@@ -1,22 +1,13 @@
import argparse from main import CommandProcessor
from main import ParsedArguments, CommandProcessor
def parse_arguments(): def start():
parser = argparse.ArgumentParser(description="Command Processor") processor = CommandProcessor()
parser.add_argument("-e", "--email", type=str, default="", help="Email")
parser.add_argument("-p", "--pw", type=str, default="", help="Password")
parser.add_argument("-f", "--folder", type=str, default="", help="Folder to API inner folder to download file from")
args = parser.parse_args()
return ParsedArguments(args.email, args.pw, args.folder)
def start(args: ParsedArguments):
processor = CommandProcessor(args)
try: try:
processor.start() processor.start()
except KeyboardInterrupt: except KeyboardInterrupt:
processor.stop() processor.stop()
if __name__ == '__main__': if __name__ == '__main__':
start(parse_arguments()) start()
+1 -1
View File
@@ -3,7 +3,7 @@ from PyInstaller.utils.hooks import collect_all
datas = [] datas = []
binaries = [] binaries = []
hiddenimports = ['user', 'security', 'secure_model', 'api_client', 'hardware_service', 'constants', 'annotation', 'remote_command', 'ai_config', 'inference', 'remote_command_handler'] hiddenimports = ['constants', 'annotation', 'credentials', 'file_data', 'user', 'security', 'secure_model', 'api_client', 'hardware_service', 'remote_command', 'ai_config', 'inference', 'remote_command_handler']
tmp_ret = collect_all('jwt') tmp_ret = collect_all('jwt')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('requests') tmp_ret = collect_all('requests')
+6
View File
@@ -32,22 +32,27 @@ Global
{8E0809AF-2920-4267-B14D-84BAB334A46F}.Debug|Any CPU.Build.0 = Debug|Any CPU {8E0809AF-2920-4267-B14D-84BAB334A46F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8E0809AF-2920-4267-B14D-84BAB334A46F}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E0809AF-2920-4267-B14D-84BAB334A46F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8E0809AF-2920-4267-B14D-84BAB334A46F}.Release|Any CPU.Build.0 = Release|Any CPU {8E0809AF-2920-4267-B14D-84BAB334A46F}.Release|Any CPU.Build.0 = Release|Any CPU
{8E0809AF-2920-4267-B14D-84BAB334A46F}.Release|Any CPU.Deploy.0 = Release|Any CPU
{85359558-FB59-4542-A597-FD9E1B04C8E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {85359558-FB59-4542-A597-FD9E1B04C8E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85359558-FB59-4542-A597-FD9E1B04C8E7}.Debug|Any CPU.Build.0 = Debug|Any CPU {85359558-FB59-4542-A597-FD9E1B04C8E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85359558-FB59-4542-A597-FD9E1B04C8E7}.Release|Any CPU.ActiveCfg = Release|Any CPU {85359558-FB59-4542-A597-FD9E1B04C8E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85359558-FB59-4542-A597-FD9E1B04C8E7}.Release|Any CPU.Build.0 = Release|Any CPU {85359558-FB59-4542-A597-FD9E1B04C8E7}.Release|Any CPU.Build.0 = Release|Any CPU
{85359558-FB59-4542-A597-FD9E1B04C8E7}.Release|Any CPU.Deploy.0 = Release|Any CPU
{BA77500E-8B66-4F31-81B0-E831FC12EDFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BA77500E-8B66-4F31-81B0-E831FC12EDFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA77500E-8B66-4F31-81B0-E831FC12EDFB}.Debug|Any CPU.Build.0 = Debug|Any CPU {BA77500E-8B66-4F31-81B0-E831FC12EDFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA77500E-8B66-4F31-81B0-E831FC12EDFB}.Release|Any CPU.ActiveCfg = Release|Any CPU {BA77500E-8B66-4F31-81B0-E831FC12EDFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA77500E-8B66-4F31-81B0-E831FC12EDFB}.Release|Any CPU.Build.0 = Release|Any CPU {BA77500E-8B66-4F31-81B0-E831FC12EDFB}.Release|Any CPU.Build.0 = Release|Any CPU
{BA77500E-8B66-4F31-81B0-E831FC12EDFB}.Release|Any CPU.Deploy.0 = Release|Any CPU
{1D8E6F44-C64E-4DBE-8665-2101EC5BE36E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1D8E6F44-C64E-4DBE-8665-2101EC5BE36E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1D8E6F44-C64E-4DBE-8665-2101EC5BE36E}.Debug|Any CPU.Build.0 = Debug|Any CPU {1D8E6F44-C64E-4DBE-8665-2101EC5BE36E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1D8E6F44-C64E-4DBE-8665-2101EC5BE36E}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D8E6F44-C64E-4DBE-8665-2101EC5BE36E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1D8E6F44-C64E-4DBE-8665-2101EC5BE36E}.Release|Any CPU.Build.0 = Release|Any CPU {1D8E6F44-C64E-4DBE-8665-2101EC5BE36E}.Release|Any CPU.Build.0 = Release|Any CPU
{1D8E6F44-C64E-4DBE-8665-2101EC5BE36E}.Release|Any CPU.Deploy.0 = Release|Any CPU
{01A5CA37-A62E-4EF3-8678-D72CD9525677}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {01A5CA37-A62E-4EF3-8678-D72CD9525677}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01A5CA37-A62E-4EF3-8678-D72CD9525677}.Debug|Any CPU.Build.0 = Debug|Any CPU {01A5CA37-A62E-4EF3-8678-D72CD9525677}.Debug|Any CPU.Build.0 = Debug|Any CPU
{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
{32C4747F-F700-44FD-B4ED-21B4A66B5FAB}.Debug|Any CPU.ActiveCfg = Debug|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}.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.ActiveCfg = Release|Any CPU
@@ -60,6 +65,7 @@ Global
{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
{E0C7176D-2E91-4928-B3C1-55CC91C8F77D}.Release|Any CPU.Build.0 = Release|Any CPU {E0C7176D-2E91-4928-B3C1-55CC91C8F77D}.Release|Any CPU.Build.0 = Release|Any CPU
{E0C7176D-2E91-4928-B3C1-55CC91C8F77D}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{32C4747F-F700-44FD-B4ED-21B4A66B5FAB} = {C307BE2E-FFCC-4BD7-AD89-C82D40B65D03} {32C4747F-F700-44FD-B4ED-21B4A66B5FAB} = {C307BE2E-FFCC-4BD7-AD89-C82D40B65D03}
+7 -7
View File
@@ -33,7 +33,7 @@ public partial class App
private IMediator _mediator = null!; private IMediator _mediator = null!;
private FormState _formState = null!; private FormState _formState = null!;
private PythonResourceLoader _resourceLoader = null!; private readonly PythonResourceLoader _resourceLoader = new();
private Stream _securedConfig = null!; private Stream _securedConfig = null!;
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
@@ -54,7 +54,7 @@ public partial class App
"Azaion.Dataset" "Azaion.Dataset"
]; ];
private ApiConfig ReadConfig() private static ApiConfig ReadConfig()
{ {
try try
{ {
@@ -70,7 +70,8 @@ public partial class App
{ {
Url = SecurityConstants.DEFAULT_API_URL, Url = SecurityConstants.DEFAULT_API_URL,
RetryCount = SecurityConstants.DEFAULT_API_RETRY_COUNT, RetryCount = SecurityConstants.DEFAULT_API_RETRY_COUNT,
TimeoutSeconds = SecurityConstants.DEFAULT_API_TIMEOUT_SECONDS TimeoutSeconds = SecurityConstants.DEFAULT_API_TIMEOUT_SECONDS,
ResourcesFolder = ""
}; };
} }
} }
@@ -81,10 +82,9 @@ public partial class App
var login = new Login(); var login = new Login();
login.CredentialsEntered += (_, credentials) => login.CredentialsEntered += (_, credentials) =>
{ {
var apiConfig = ReadConfig(); var config = ReadConfig();
var api = AzaionApiClient.Create(credentials, apiConfig); credentials.Folder = config.ResourcesFolder;
_resourceLoader.Login(credentials);
_resourceLoader = new PythonResourceLoader(apiConfig, credentials, api);
_securedConfig = _resourceLoader.LoadFileFromPython("secured-config.json"); _securedConfig = _resourceLoader.LoadFileFromPython("secured-config.json");
AppDomain.CurrentDomain.AssemblyResolve += (_, a) => AppDomain.CurrentDomain.AssemblyResolve += (_, a) =>
+5 -5
View File
@@ -30,9 +30,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Azaion.Annotator\Azaion.Annotator.csproj" />
<ProjectReference Include="..\Azaion.Common\Azaion.Common.csproj" /> <ProjectReference Include="..\Azaion.Common\Azaion.Common.csproj" />
<ProjectReference Include="..\Azaion.Dataset\Azaion.Dataset.csproj" /> <ProjectReference Include="..\Dummy\Azaion.Annotator\Azaion.Annotator.csproj" />
<ProjectReference Include="..\Dummy\Azaion.Dataset\Azaion.Dataset.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -54,9 +54,9 @@
</ItemGroup> </ItemGroup>
<Target Name="PostBuild" AfterTargets="Build"> <Target Name="PostBuild" AfterTargets="Build">
<MakeDir Directories="$(TargetDir)secure" /> <MakeDir Directories="$(TargetDir)dummy" />
<Move SourceFiles="$(TargetDir)Azaion.Annotator.dll" DestinationFolder="$(TargetDir)secure" /> <Move SourceFiles="$(TargetDir)Azaion.Annotator.dll" DestinationFolder="$(TargetDir)dummy" />
<Move SourceFiles="$(TargetDir)Azaion.Dataset.dll" DestinationFolder="$(TargetDir)secure" /> <Move SourceFiles="$(TargetDir)Azaion.Dataset.dll" DestinationFolder="$(TargetDir)dummy" />
<Exec Command="upload.cmd $(ConfigurationName)" /> <Exec Command="upload.cmd $(ConfigurationName)" />
</Target> </Target>
+6 -2
View File
@@ -8,6 +8,7 @@ using Azaion.Common.DTO;
using Azaion.Common.DTO.Config; using Azaion.Common.DTO.Config;
using Azaion.Common.Extensions; using Azaion.Common.Extensions;
using Azaion.Common.Services; using Azaion.Common.Services;
using Azaion.CommonSecurity.Services;
using Azaion.Dataset; using Azaion.Dataset;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@@ -24,20 +25,22 @@ public partial class MainSuite
private readonly IGalleryService _galleryService; private readonly IGalleryService _galleryService;
private readonly IDbFactory _dbFactory; private readonly IDbFactory _dbFactory;
private readonly Dictionary<WindowEnum, Window> _openedWindows = new(); private readonly Dictionary<WindowEnum, Window> _openedWindows = new();
private readonly IResourceLoader _resourceLoader;
public MainSuite(IOptions<AppConfig> appConfig, public MainSuite(IOptions<AppConfig> appConfig,
IConfigUpdater configUpdater, IConfigUpdater configUpdater,
IEnumerable<IAzaionModule> modules, IEnumerable<IAzaionModule> modules,
IServiceProvider sp, IServiceProvider sp,
IGalleryService galleryService, IGalleryService galleryService,
IDbFactory dbFactory IDbFactory dbFactory,
) IResourceLoader resourceLoader)
{ {
_configUpdater = configUpdater; _configUpdater = configUpdater;
_modules = modules; _modules = modules;
_sp = sp; _sp = sp;
_galleryService = galleryService; _galleryService = galleryService;
_dbFactory = dbFactory; _dbFactory = dbFactory;
_resourceLoader = resourceLoader;
_appConfig = appConfig.Value; _appConfig = appConfig.Value;
InitializeComponent(); InitializeComponent();
Loaded += OnLoaded; Loaded += OnLoaded;
@@ -107,6 +110,7 @@ public partial class MainSuite
_openedWindows[module.WindowEnum] = window; _openedWindows[module.WindowEnum] = window;
window.Closed += (_, _) => window.Closed += (_, _) =>
{ {
_resourceLoader.StopPython();
_openedWindows.Remove(module.WindowEnum); _openedWindows.Remove(module.WindowEnum);
if (!_openedWindows.Any()) if (!_openedWindows.Any())
Close(); Close();
+2 -5
View File
@@ -1,10 +1,7 @@
{ {
"ApiConfig": { "ApiConfig": {
"Url": "https://api.azaion.com/", "TimeoutSeconds": 20.0,
"RetryCount": 3, "ResourcesFolder": "stage"
"TimeoutSeconds": 40.0,
"ResourcesFolder": "stage",
"TokenFile": "token.txt"
}, },
"DirectoriesConfig": { "DirectoriesConfig": {
"VideosDirectory": "E:\\Azaion6", "VideosDirectory": "E:\\Azaion6",
+3 -2
View File
@@ -7,11 +7,12 @@ set RESOURCES_FOLDER=stage
set EMAIL=uploader@azaion.com set EMAIL=uploader@azaion.com
set PASSWORD=Az@1on_10Upl0@der set PASSWORD=Az@1on_10Upl0@der
echo %cd%
set FILE1_TO_UPLOAD=%cd%\bin\%CONFIG%\net8.0-windows\secure\Azaion.Annotator.dll set FILE1_TO_UPLOAD=%cd%\..\Azaion.Annotator\bin\%CONFIG%\net8.0-windows\Azaion.Annotator.dll
set "FILE1_TO_UPLOAD=%FILE1_TO_UPLOAD:\=/%" set "FILE1_TO_UPLOAD=%FILE1_TO_UPLOAD:\=/%"
set FILE2_TO_UPLOAD=%cd%\bin\%CONFIG%\net8.0-windows\secure\Azaion.Dataset.dll set FILE2_TO_UPLOAD=%cd%\..\Azaion.Dataset\bin\%CONFIG%\net8.0-windows\Azaion.Dataset.dll
set "FILE2_TO_UPLOAD=%FILE2_TO_UPLOAD:\=/%" set "FILE2_TO_UPLOAD=%FILE2_TO_UPLOAD:\=/%"
-1
View File
@@ -20,7 +20,6 @@ public class DictTest
} }
[Theory] [Theory]
[InlineData(null, 0)]
[InlineData(new int[]{}, 0)] [InlineData(new int[]{}, 0)]
[InlineData(new int[]{1, 2, 5}, 1)] [InlineData(new int[]{1, 2, 5}, 1)]
[InlineData(new int[]{3, -2, 5}, -2)] [InlineData(new int[]{3, -2, 5}, -2)]
@@ -27,7 +27,6 @@
<PackageReference Include="SkiaSharp" Version="2.88.9" /> <PackageReference Include="SkiaSharp" Version="2.88.9" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.21" /> <PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.21" />
<PackageReference Include="WindowsAPICodePack" Version="7.0.4" /> <PackageReference Include="WindowsAPICodePack" Version="7.0.4" />
<PackageReference Include="YoloV8.Gpu" Version="5.0.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>