write logs for inference and loader to file

This commit is contained in:
Alex Bezdieniezhnykh
2025-06-14 16:08:32 +03:00
parent 8aa2f563a4
commit 6f297c4ebf
30 changed files with 218 additions and 140 deletions
+2
View File
@@ -26,6 +26,8 @@ tmp_ret = collect_all('pynvml')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2] datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
tmp_ret = collect_all('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('loguru')
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
a = Analysis( a = Analysis(
+1
View File
@@ -33,6 +33,7 @@ venv\Scripts\pyinstaller --name=azaion-inference ^
--collect-all pycuda ^ --collect-all pycuda ^
--collect-all pynvml ^ --collect-all pynvml ^
--collect-all jwt ^ --collect-all jwt ^
--collect-all loguru ^
--hidden-import constants ^ --hidden-import constants ^
--hidden-import file_data ^ --hidden-import file_data ^
--hidden-import remote_command ^ --hidden-import remote_command ^
+2 -2
View File
@@ -13,5 +13,5 @@ cdef str MODELS_FOLDER
cdef int SMALL_SIZE_KB cdef int SMALL_SIZE_KB
cdef log(str log_message)
cdef log(str log_message, bytes client_id=*) cdef logerror(str error)
+32 -5
View File
@@ -1,4 +1,6 @@
import time import sys
from loguru import logger
cdef str CONFIG_FILE = "config.yaml" # Port for the zmq cdef str CONFIG_FILE = "config.yaml" # Port for the zmq
@@ -10,7 +12,32 @@ cdef str MODELS_FOLDER = "models"
cdef int SMALL_SIZE_KB = 3 cdef int SMALL_SIZE_KB = 3
cdef log(str log_message, bytes client_id=None): logger.remove()
local_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) log_format = "[{time:HH:mm:ss} {level}] {message}"
client_str = '' if client_id is None else f' {client_id}' logger.add(
print(f'[{local_time}{client_str}]: {log_message}') sink="Logs/log_inference_{time:YYYYMMDD}.txt",
level="INFO",
format=log_format,
enqueue=True,
rotation="1 day",
retention="30 days",
)
logger.add(
sys.stdout,
level="DEBUG",
format=log_format,
filter=lambda record: record["level"].name in ("INFO", "DEBUG", "SUCCESS"),
colorize=True
)
logger.add(
sys.stderr,
level="WARNING",
format=log_format,
colorize=True
)
cdef log(str log_message):
logger.info(log_message)
cdef logerror(str error):
logger.error(error)
+10 -10
View File
@@ -16,7 +16,7 @@ cdef int check_tensor_gpu_index():
deviceCount = pynvml.nvmlDeviceGetCount() deviceCount = pynvml.nvmlDeviceGetCount()
if deviceCount == 0: if deviceCount == 0:
print('No NVIDIA GPUs found.') constants.logerror('No NVIDIA GPUs found.')
return -1 return -1
for i in range(deviceCount): for i in range(deviceCount):
@@ -24,10 +24,10 @@ cdef int check_tensor_gpu_index():
major, minor = pynvml.nvmlDeviceGetCudaComputeCapability(handle) major, minor = pynvml.nvmlDeviceGetCudaComputeCapability(handle)
if major > 6 or (major == 6 and minor >= 1): if major > 6 or (major == 6 and minor >= 1):
print('found NVIDIA GPU!') constants.log('found NVIDIA GPU!')
return i return i
print('NVIDIA GPU doesnt support TensorRT!') constants.logerror('NVIDIA GPU doesnt support TensorRT!')
return -1 return -1
except pynvml.NVMLError: except pynvml.NVMLError:
@@ -36,7 +36,7 @@ cdef int check_tensor_gpu_index():
try: try:
pynvml.nvmlShutdown() pynvml.nvmlShutdown()
except: except:
print('Failed to shutdown pynvml cause probably no NVidia GPU') constants.logerror('Failed to shutdown pynvml cause probably no NVIDIA GPU')
pass pass
tensor_gpu_index = check_tensor_gpu_index() tensor_gpu_index = check_tensor_gpu_index()
@@ -70,15 +70,15 @@ cdef class Inference:
res = self.loader_client.load_big_small_resource(engine_filename, models_dir) res = self.loader_client.load_big_small_resource(engine_filename, models_dir)
if res.err is None: if res.err is None:
print('tensor rt engine is here, no need to build') constants.log('tensor rt engine is here, no need to build')
self.is_building_engine = False self.is_building_engine = False
updater_callback('enabled') updater_callback('enabled')
return return
print(res.err) constants.logerror(res.err)
# time.sleep(8) # prevent simultaneously loading dll and models # time.sleep(8) # prevent simultaneously loading dll and models
updater_callback('converting') updater_callback('converting')
print('try to load onnx') constants.log('try to load onnx')
res = self.loader_client.load_big_small_resource(constants.AI_ONNX_MODEL_FILE, models_dir) res = self.loader_client.load_big_small_resource(constants.AI_ONNX_MODEL_FILE, models_dir)
if res.err is not None: if res.err is not None:
updater_callback(f'Error. {res.err}') updater_callback(f'Error. {res.err}')
@@ -87,7 +87,7 @@ cdef class Inference:
res = self.loader_client.upload_big_small_resource(model_bytes, <str> engine_filename, models_dir) res = self.loader_client.upload_big_small_resource(model_bytes, <str> engine_filename, models_dir)
if res.err is not None: if res.err is not None:
updater_callback(f'Error. {res.err}') updater_callback(f'Error. {res.err}')
print(f'uploaded {engine_filename} to CDN and API') constants.log(f'uploaded {engine_filename} to CDN and API')
self.is_building_engine = False self.is_building_engine = False
updater_callback('enabled') updater_callback('enabled')
except Exception as e: except Exception as e:
@@ -212,11 +212,11 @@ cdef class Inference:
# images first, it's faster # images first, it's faster
if len(images) > 0: if len(images) > 0:
for chunk in self.split_list_extend(images, self.engine.get_batch_size()): for chunk in self.split_list_extend(images, self.engine.get_batch_size()):
print(f'run inference on {" ".join(chunk)}...') constants.log(f'run inference on {" ".join(chunk)}...')
self._process_images(cmd, ai_config, chunk) self._process_images(cmd, ai_config, chunk)
if len(videos) > 0: if len(videos) > 0:
for v in videos: for v in videos:
print(f'run inference on {v}...') constants.log(f'run inference on {v}...')
self._process_video(cmd, ai_config, v) self._process_video(cmd, ai_config, v)
+2 -2
View File
@@ -37,7 +37,7 @@ cdef class CommandProcessor:
continue continue
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
print('EXIT!') constants.log('EXIT!')
cdef on_command(self, RemoteCommand command): cdef on_command(self, RemoteCommand command):
try: try:
@@ -54,7 +54,7 @@ cdef class CommandProcessor:
else: else:
pass pass
except Exception as e: except Exception as e:
print(f"Error handling client: {e}") constants.logerror(f"Error handling client: {e}")
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())
+3 -2
View File
@@ -1,5 +1,6 @@
from inference_engine cimport InferenceEngine from inference_engine cimport InferenceEngine
import onnxruntime as onnx import onnxruntime as onnx
cimport constants
cdef class OnnxEngine(InferenceEngine): cdef class OnnxEngine(InferenceEngine):
def __init__(self, model_bytes: bytes, batch_size: int = 1, **kwargs): def __init__(self, model_bytes: bytes, batch_size: int = 1, **kwargs):
@@ -10,9 +11,9 @@ cdef class OnnxEngine(InferenceEngine):
self.input_name = self.model_inputs[0].name self.input_name = self.model_inputs[0].name
self.input_shape = self.model_inputs[0].shape self.input_shape = self.model_inputs[0].shape
self.batch_size = self.input_shape[0] if self.input_shape[0] != -1 else batch_size self.batch_size = self.input_shape[0] if self.input_shape[0] != -1 else batch_size
print(f'AI detection model input: {self.model_inputs} {self.input_shape}') constants.log(f'AI detection model input: {self.model_inputs} {self.input_shape}')
model_meta = self.session.get_modelmeta() model_meta = self.session.get_modelmeta()
print("Metadata:", model_meta.custom_metadata_map) constants.log(f"Metadata: {model_meta.custom_metadata_map}")
cpdef tuple get_input_shape(self): cpdef tuple get_input_shape(self):
shape = self.input_shape shape = self.input_shape
+3 -4
View File
@@ -3,7 +3,6 @@ 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):
@@ -28,7 +27,7 @@ cdef class RemoteCommandHandler:
for _ in range(4): # 4 worker threads for _ in range(4): # 4 worker threads
worker = Thread(target=self._worker_loop, daemon=True) worker = Thread(target=self._worker_loop, daemon=True)
self._workers.append(worker) self._workers.append(worker)
print(f'Listening to commands on port {zmq_port}...') constants.log(f'Listening to commands on port {zmq_port}...')
cdef start(self): cdef start(self):
self._proxy_thread.start() self._proxy_thread.start()
@@ -40,7 +39,7 @@ cdef class RemoteCommandHandler:
zmq.proxy_steerable(self._router, self._dealer, control=self._control) zmq.proxy_steerable(self._router, self._dealer, control=self._control)
except zmq.error.ZMQError as e: except zmq.error.ZMQError as e:
if self._shutdown_event.is_set(): if self._shutdown_event.is_set():
print("Shutdown, exit proxy loop.") constants.log("Shutdown, exit proxy loop.")
else: else:
raise raise
@@ -59,7 +58,7 @@ cdef class RemoteCommandHandler:
client_id, message = worker_socket.recv_multipart() client_id, message = worker_socket.recv_multipart()
cmd = RemoteCommand.from_msgpack(<bytes> message) cmd = RemoteCommand.from_msgpack(<bytes> message)
cmd.client_id = client_id cmd.client_id = client_id
constants.log(<str>f'{cmd}', client_id) constants.log(cmd)
self._on_command(cmd) self._on_command(cmd)
except Exception as e: except Exception as e:
if not self._shutdown_event.is_set(): if not self._shutdown_event.is_set():
+1
View File
@@ -14,3 +14,4 @@ pycuda
tensorrt tensorrt
pynvml pynvml
boto3 boto3
loguru
+2 -2
View File
@@ -103,11 +103,11 @@ cdef class TensorRTEngine(InferenceEngine):
constants.log('Converting to supported fp16') constants.log('Converting to supported fp16')
config.set_flag(trt.BuilderFlag.FP16) config.set_flag(trt.BuilderFlag.FP16)
else: else:
print('Converting to supported fp32. (fp16 is not supported)') constants.log('Converting to supported fp32. (fp16 is not supported)')
plan = builder.build_serialized_network(network, config) plan = builder.build_serialized_network(network, config)
if plan is None: if plan is None:
print('Conversion failed.') constants.logerror('Conversion failed.')
return None return None
constants.log('conversion done!') constants.log('conversion done!')
return bytes(plan) return bytes(plan)
@@ -215,7 +215,7 @@ class Api:
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!') print('500!') #test
key = self.get_encryption_key(hardware.hash) key = self.get_encryption_key(hardware.hash)
+3 -3
View File
@@ -47,7 +47,7 @@ cdef class ApiClient:
token = response.json()["token"] token = response.json()["token"]
self.set_token(token) self.set_token(token)
except HTTPError as e: except HTTPError as e:
constants.log(response.json()) constants.logerror(response.json())
if response.status_code == HTTPStatus.CONFLICT: if response.status_code == HTTPStatus.CONFLICT:
res = response.json() res = response.json()
raise Exception(res['Message']) raise Exception(res['Message'])
@@ -95,7 +95,7 @@ cdef class ApiClient:
r.raise_for_status() r.raise_for_status()
constants.log(f"Uploaded {filename} to {self.api_url}/{folder} successfully: {r.status_code}.") constants.log(f"Uploaded {filename} to {self.api_url}/{folder} successfully: {r.status_code}.")
except Exception as e: except Exception as e:
constants.log(f"Upload fail: {e}") constants.logerror(f"Upload fail: {e}")
cdef list_files(self, str folder, str search_file): cdef list_files(self, str folder, str search_file):
response = self.request('get', f'{self.api_url}/resources/list/{folder}', { response = self.request('get', f'{self.api_url}/resources/list/{folder}', {
@@ -173,7 +173,7 @@ cdef class ApiClient:
resource = Security.decrypt_to(encrypted_bytes_small + local_bytes_big, key) resource = Security.decrypt_to(encrypted_bytes_small + local_bytes_big, key)
return resource return resource
except Exception as ex: except Exception as ex:
constants.log('Local file {folder}\\{big_part} doesnt match with api file, old version') constants.logerror('Local file {folder}\\{big_part} 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)
+1
View File
@@ -30,6 +30,7 @@ venv\Scripts\pyinstaller --name=azaion-loader ^
--collect-all boto3 ^ --collect-all boto3 ^
--collect-all cryptography ^ --collect-all cryptography ^
--collect-all yaml ^ --collect-all yaml ^
--collect-all loguru ^
--hidden-import constants ^ --hidden-import constants ^
--hidden-import file_data ^ --hidden-import file_data ^
--hidden-import remote_command ^ --hidden-import remote_command ^
+2 -2
View File
@@ -30,7 +30,7 @@ cdef class CDNManager:
constants.log(f'uploaded {filename} ({len(file_bytes)} bytes) to the {bucket}') constants.log(f'uploaded {filename} ({len(file_bytes)} bytes) to the {bucket}')
return True return True
except Exception as e: except Exception as e:
print(e) constants.logerror(e)
return False return False
cdef download(self, str folder, str filename): cdef download(self, str folder, str filename):
@@ -40,5 +40,5 @@ cdef class CDNManager:
constants.log(f'downloaded {filename} from the {folder}') constants.log(f'downloaded {filename} from the {folder}')
return True return True
except Exception as e: except Exception as e:
print(e) constants.logerror(e)
return False return False
+2 -1
View File
@@ -14,4 +14,5 @@ cdef str MODELS_FOLDER
cdef int SMALL_SIZE_KB cdef int SMALL_SIZE_KB
cdef log(str log_message, bytes client_id=*) cdef log(str log_message)
cdef logerror(str error)
+33 -4
View File
@@ -1,4 +1,6 @@
import sys
import time import time
from loguru import logger
cdef str CONFIG_FILE = "config.yaml" # Port for the zmq cdef str CONFIG_FILE = "config.yaml" # Port for the zmq
@@ -10,7 +12,34 @@ cdef str MODELS_FOLDER = "models"
cdef int SMALL_SIZE_KB = 3 cdef int SMALL_SIZE_KB = 3
cdef log(str log_message, bytes client_id=None): cdef int ALIGNMENT_WIDTH = 32
local_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
client_str = '' if client_id is None else f' {client_id}' logger.remove()
print(f'[{local_time}{client_str}]: {log_message}') log_format = "[{time:HH:mm:ss} {level}] {message}"
logger.add(
sink="Logs/log_loader_{time:YYYYMMDD}.txt",
level="INFO",
format=log_format,
enqueue=True,
rotation="1 day",
retention="30 days",
)
logger.add(
sys.stdout,
level="DEBUG",
format=log_format,
filter=lambda record: record["level"].name in ("INFO", "DEBUG", "SUCCESS"),
colorize=True
)
logger.add(
sys.stderr,
level="WARNING",
format=log_format,
colorize=True
)
cdef log(str log_message):
logger.info(log_message)
cdef logerror(str error):
logger.error(error)
+3 -2
View File
@@ -1,6 +1,7 @@
import threading import threading
from threading import Thread from threading import Thread
import traceback import traceback
cimport constants
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
@@ -29,7 +30,7 @@ cdef class CommandProcessor:
self.shutdown_event.wait(timeout=1.0) self.shutdown_event.wait(timeout=1.0)
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
print('EXIT!') constants.log('EXIT!')
cdef on_command(self, RemoteCommand command): cdef on_command(self, RemoteCommand command):
@@ -66,7 +67,7 @@ cdef class CommandProcessor:
else: else:
pass pass
except Exception as e: except Exception as e:
print(f"Error handling client: {e}") constants.logerror(f"Error handling client: {e}")
err_command = RemoteCommand(CommandType.ERROR, None, str(e)) err_command = RemoteCommand(CommandType.ERROR, None, str(e))
self.remote_handler.send(command.client_id, err_command.serialize()) self.remote_handler.send(command.client_id, err_command.serialize())
+4 -9
View File
@@ -28,7 +28,7 @@ cdef class RemoteCommandHandler:
for _ in range(4): # 4 worker threads for _ in range(4): # 4 worker threads
worker = Thread(target=self._worker_loop, daemon=True) worker = Thread(target=self._worker_loop, daemon=True)
self._workers.append(worker) self._workers.append(worker)
print(f'Listening to commands on port {zmq_port}...') constants.log(f'Listening to commands on port {zmq_port}...')
cdef start(self): cdef start(self):
self._proxy_thread.start() self._proxy_thread.start()
@@ -40,7 +40,7 @@ cdef class RemoteCommandHandler:
zmq.proxy_steerable(self._router, self._dealer, control=self._control) zmq.proxy_steerable(self._router, self._dealer, control=self._control)
except zmq.error.ZMQError as e: except zmq.error.ZMQError as e:
if self._shutdown_event.is_set(): if self._shutdown_event.is_set():
print("Shutdown, exit proxy loop.") constants.log("Shutdown, exit proxy loop.")
else: else:
raise raise
@@ -59,11 +59,11 @@ cdef class RemoteCommandHandler:
client_id, message = worker_socket.recv_multipart() client_id, message = worker_socket.recv_multipart()
cmd = RemoteCommand.from_msgpack(<bytes> message) cmd = RemoteCommand.from_msgpack(<bytes> message)
cmd.client_id = client_id cmd.client_id = client_id
constants.log(<str>f'{cmd}', client_id) constants.log(<str>f'{cmd}')
self._on_command(cmd) self._on_command(cmd)
except Exception as e: except Exception as e:
if not self._shutdown_event.is_set(): if not self._shutdown_event.is_set():
constants.log(f"Worker error: {e}") constants.logerror(f"Worker error: {e}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
finally: finally:
@@ -72,11 +72,6 @@ cdef class RemoteCommandHandler:
cdef send(self, bytes client_id, bytes data): cdef send(self, bytes client_id, bytes data):
self._router.send_multipart([client_id, data]) self._router.send_multipart([client_id, data])
# with self._context.socket(zmq.DEALER) as socket:
# socket.connect("inproc://backend")
# socket.send_multipart([client_id, data])
# # constants.log(<str>f'Sent {len(data)} bytes.', client_id)
cdef stop(self): cdef stop(self):
self._shutdown_event.set() self._shutdown_event.set()
try: try:
+1
View File
@@ -7,4 +7,5 @@ zmq
requests requests
pyyaml pyyaml
boto3 boto3
loguru
cryptography==44.0.2 cryptography==44.0.2
+1 -2
View File
@@ -2,7 +2,6 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Serilog; using Serilog;
namespace Azaion.LoaderUI; namespace Azaion.LoaderUI;
@@ -29,7 +28,7 @@ public partial class App
var host = Host.CreateDefaultBuilder() var host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((_, config) => config .ConfigureAppConfiguration((_, config) => config
.AddCommandLine(Environment.GetCommandLineArgs()) .AddCommandLine(Environment.GetCommandLineArgs())
.AddJsonFile(Constants.CONFIG_JSON_FILE)) .AddJsonFile(Constants.CONFIG_JSON_FILE, optional: true))
.UseSerilog() .UseSerilog()
.ConfigureServices((context, services) => .ConfigureServices((context, services) =>
{ {
+1 -3
View File
@@ -6,6 +6,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<ApplicationIcon>..\logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -22,9 +23,6 @@
<ItemGroup> <ItemGroup>
<None Remove="loaderconfig.json" /> <None Remove="loaderconfig.json" />
<Content Include="loaderconfig.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup> </ItemGroup>
</Project> </Project>
+24 -24
View File
@@ -10,8 +10,8 @@ namespace Azaion.LoaderUI;
public interface IAzaionApi public interface IAzaionApi
{ {
void Login(ApiCredentials credentials); void Login(ApiCredentials credentials);
string GetLastInstallerName(string folder); Task<string> GetLastInstallerName(string folder);
(string name, Stream stream) DownloadInstaller(string folder); Task<(string name, Stream stream)> DownloadInstaller(string folder);
} }
public class AzaionApi(HttpClient client) : IAzaionApi public class AzaionApi(HttpClient client) : IAzaionApi
@@ -25,39 +25,39 @@ public class AzaionApi(HttpClient client) : IAzaionApi
_credentials = credentials; _credentials = credentials;
} }
public string GetLastInstallerName(string folder) public async Task<string> GetLastInstallerName(string folder)
{ {
var res = Get<List<string>>($"/resources/list/{folder}"); var res = await Get<List<string>>($"/resources/list/{folder}");
return res?.FirstOrDefault() ?? ""; return res?.FirstOrDefault() ?? "";
} }
public (string name, Stream stream) DownloadInstaller(string folder) public async Task<(string name, Stream stream)> DownloadInstaller(string folder)
{ {
var response = Send(new HttpRequestMessage(HttpMethod.Get, $"resources/get-installer/{folder}")); var response = await Send(new HttpRequestMessage(HttpMethod.Get, $"resources/get-installer/{folder}"));
var fileStream = response.Content.ReadAsStream(); var fileStream = await response.Content.ReadAsStreamAsync();
var fileName = response.Content.Headers.ContentDisposition?.FileName?.Trim('"') ?? "installer.exe"; var fileName = response.Content.Headers.ContentDisposition?.FileName?.Trim('"') ?? "installer.exe";
return (fileName, fileStream); return (fileName, fileStream);
} }
private HttpResponseMessage Send(HttpRequestMessage request) private async Task<HttpResponseMessage> Send(HttpRequestMessage request)
{ {
if (string.IsNullOrEmpty(_jwtToken)) if (string.IsNullOrEmpty(_jwtToken))
Authorize(); await Authorize();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _jwtToken); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _jwtToken);
var response = client.Send(request); var response = await client.SendAsync(request);
if (response.StatusCode == HttpStatusCode.Unauthorized) if (response.StatusCode == HttpStatusCode.Unauthorized)
{ {
Authorize(); await Authorize();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _jwtToken); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _jwtToken);
response = client.Send(request); response = await client.SendAsync(request);
} }
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
return response; return response;
var stream = response.Content.ReadAsStream(); var stream = await response.Content.ReadAsStreamAsync();
var content = new StreamReader(stream).ReadToEnd(); var content = await new StreamReader(stream).ReadToEndAsync();
if (response.StatusCode == HttpStatusCode.Conflict) if (response.StatusCode == HttpStatusCode.Conflict)
{ {
var result = JsonConvert.DeserializeObject<BusinessExceptionDto>(content); var result = JsonConvert.DeserializeObject<BusinessExceptionDto>(content);
@@ -66,23 +66,23 @@ public class AzaionApi(HttpClient client) : IAzaionApi
throw new Exception($"Failed: {response.StatusCode}! Result: {content}"); throw new Exception($"Failed: {response.StatusCode}! Result: {content}");
} }
private T? Get<T>(string url) private async Task<T?> Get<T>(string url)
{ {
var response = Send(new HttpRequestMessage(HttpMethod.Get, url)); var response = await Send(new HttpRequestMessage(HttpMethod.Get, url));
var stream = response.Content.ReadAsStream(); var stream = await response.Content.ReadAsStreamAsync();
var json = new StreamReader(stream).ReadToEnd(); var json = await new StreamReader(stream).ReadToEndAsync();
return JsonConvert.DeserializeObject<T>(json); return JsonConvert.DeserializeObject<T>(json);
} }
private void Put<T>(string url, T obj) private async Task Put<T>(string url, T obj)
{ {
Send(new HttpRequestMessage(HttpMethod.Put, url) await Send(new HttpRequestMessage(HttpMethod.Put, url)
{ {
Content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, APP_JSON) Content = new StringContent(JsonConvert.SerializeObject(obj), Encoding.UTF8, APP_JSON)
}); });
} }
private void Authorize() private async Task Authorize()
{ {
try try
{ {
@@ -95,13 +95,13 @@ public class AzaionApi(HttpClient client) : IAzaionApi
password = _credentials.Password password = _credentials.Password
}), Encoding.UTF8, APP_JSON); }), Encoding.UTF8, APP_JSON);
var message = new HttpRequestMessage(HttpMethod.Post, "login") { Content = content }; var message = new HttpRequestMessage(HttpMethod.Post, "login") { Content = content };
var response = client.Send(message); var response = await client.SendAsync(message);
if (!response.IsSuccessStatusCode) if (!response.IsSuccessStatusCode)
throw new Exception($"EnterCredentials failed: {response.StatusCode}"); throw new Exception($"EnterCredentials failed: {response.StatusCode}");
var stream = response.Content.ReadAsStream(); var stream = await response.Content.ReadAsStreamAsync();
var json = new StreamReader(stream).ReadToEnd(); var json = await new StreamReader(stream).ReadToEndAsync();
var result = JsonConvert.DeserializeObject<LoginResponse>(json); var result = JsonConvert.DeserializeObject<LoginResponse>(json);
if (string.IsNullOrEmpty(result?.Token)) if (string.IsNullOrEmpty(result?.Token))
+2 -1
View File
@@ -3,6 +3,7 @@ namespace Azaion.LoaderUI;
public static class Constants public static class Constants
{ {
public const string CONFIG_JSON_FILE = "loaderconfig.json"; public const string CONFIG_JSON_FILE = "loaderconfig.json";
public const string API_URL = "http://localhost:5219"; //"https://api.azaion.com"; public const string API_URL = "https://api.azaion.com";
public const string AZAION_SUITE_EXE = "Azaion.Suite.exe"; public const string AZAION_SUITE_EXE = "Azaion.Suite.exe";
public const string SUITE_FOLDER = "suite";
} }
+41 -32
View File
@@ -23,7 +23,7 @@
Color ="Gray" /> Color ="Gray" />
</Border.Effect> </Border.Effect>
<StackPanel Orientation="Vertical" <StackPanel Orientation="Vertical"
Margin="20"> Margin="15">
<Canvas> <Canvas>
<Button Padding="5" ToolTip="Закрити" Background="DarkGray" BorderBrush="DarkGray" Canvas.Left="290" Cursor="Hand" <Button Padding="5" ToolTip="Закрити" Background="DarkGray" BorderBrush="DarkGray" Canvas.Left="290" Cursor="Hand"
Name="CloseBtn" Name="CloseBtn"
@@ -59,6 +59,8 @@
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Text="Email" <TextBlock Text="Email"
Grid.Row="0" Grid.Row="0"
@@ -90,38 +92,45 @@
BorderThickness="0,0,0,1" BorderThickness="0,0,0,1"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Password=""/> Password=""/>
<Button x:Name="LoginBtn"
Grid.Row="4"
Content="Вхід"
Foreground="White"
Background="DimGray"
Margin="0,15"
Height="35"
Width="280"
Cursor="Hand"
Click="LoginClick">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="LoginBorder" Background="{TemplateBinding Background}"
CornerRadius="16">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightGray" TargetName="LoginBorder" />
<Setter Property="TextBlock.Foreground" Value="Black" TargetName="LoginBorder" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<TextBlock Name="TbStatus"
Text=""
Grid.Row="5"
Margin="0, -10, 0, 5"
HorizontalAlignment="Left"/>
</Grid> </Grid>
<Button x:Name="LoginBtn"
Content="Вхід"
Foreground="White"
Background="DimGray"
Margin="0,25"
Height="35"
Width="280"
Cursor="Hand"
Click="LoginClick">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="LoginBorder" Background="{TemplateBinding Background}"
CornerRadius="16">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightGray" TargetName="LoginBorder" />
<Setter Property="TextBlock.Foreground" Value="Black" TargetName="LoginBorder" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</StackPanel> </StackPanel>
</Border> </Border>
</Window> </Window>
+27 -13
View File
@@ -3,23 +3,27 @@ using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace Azaion.LoaderUI; namespace Azaion.LoaderUI;
public partial class Login public partial class Login
{ {
private readonly IAzaionApi _azaionApi; private readonly IAzaionApi _azaionApi;
private readonly DirectoriesConfig _dirConfig; private readonly ILogger<Login> _logger;
private readonly DirectoriesConfig? _dirConfig;
public Login(IAzaionApi azaionApi, IOptions<DirectoriesConfig> directoriesConfig) public Login(IAzaionApi azaionApi, IOptions<DirectoriesConfig> directoriesConfig, ILogger<Login> logger)
{ {
_azaionApi = azaionApi; _azaionApi = azaionApi;
_logger = logger;
_dirConfig = directoriesConfig.Value; _dirConfig = directoriesConfig.Value;
InitializeComponent(); InitializeComponent();
} }
private void LoginClick(object sender, RoutedEventArgs e) private async void LoginClick(object sender, RoutedEventArgs e)
{ {
var creds = new ApiCredentials(TbEmail.Text, TbPassword.Password); var creds = new ApiCredentials(TbEmail.Text, TbPassword.Password);
if (!creds.IsValid()) if (!creds.IsValid())
@@ -30,33 +34,43 @@ public partial class Login
_azaionApi.Login(creds); _azaionApi.Login(creds);
if (GetInstallerVer() > GetLocalVer()) var installerVersion = await GetInstallerVer();
DownloadAndRunInstaller(); var localVersion = GetLocalVer();
if (installerVersion > localVersion)
{
TbStatus.Text = $"Updating from {localVersion} to {installerVersion}...";
await DownloadAndRunInstaller();
TbStatus.Text = $"Installed {installerVersion}!";
}
else
TbStatus.Text = $"Your version is up to date!";
Process.Start(Constants.AZAION_SUITE_EXE, $"-e {creds.Email} -p {creds.Password}"); Process.Start(Constants.AZAION_SUITE_EXE, $"-e {creds.Email} -p {creds.Password}");
Close(); Close();
} }
private void DownloadAndRunInstaller() private async Task DownloadAndRunInstaller()
{ {
var (installerName, stream) = _azaionApi.DownloadInstaller(_dirConfig.SuiteInstallerDirectory); var (installerName, stream) = await _azaionApi.DownloadInstaller(_dirConfig?.SuiteInstallerDirectory ?? "");
var localFileStream = new FileStream(installerName, FileMode.Create, FileAccess.Write); var localFileStream = new FileStream(installerName, FileMode.Create, FileAccess.Write);
stream.CopyTo(localFileStream); await stream.CopyToAsync(localFileStream);
localFileStream.Close(); localFileStream.Close();
stream.Close(); stream.Close();
var processInfo = new ProcessStartInfo(installerName) var processInfo = new ProcessStartInfo(installerName)
{ {
UseShellExecute = true, UseShellExecute = true,
Verb = "runas" Arguments = "/VERYSILENT"
}; };
Process.Start(processInfo); var process = Process.Start(processInfo);
await process!.WaitForExitAsync();
} }
private Version GetInstallerVer() private async Task<Version> GetInstallerVer()
{ {
var installerName = _azaionApi.GetLastInstallerName(_dirConfig.SuiteInstallerDirectory); TbStatus.Text = "Checking for the newer version...";
var installerName = await _azaionApi.GetLastInstallerName(_dirConfig?.SuiteInstallerDirectory ?? Constants.SUITE_FOLDER);
var version = installerName var version = installerName
.Replace("AzaionSuite.Iterative.", "") .Replace("AzaionSuite.Iterative.", "")
.Replace(".exe", ""); .Replace(".exe", "");
-6
View File
@@ -1,6 +0,0 @@
{
"DirectoriesConfig":
{
"SuiteInstallerDirectory": ""
}
}
-1
View File
@@ -23,7 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{CF141A48
build\init.cmd = build\init.cmd build\init.cmd = build\init.cmd
build\installer.full.iss = build\installer.full.iss build\installer.full.iss = build\installer.full.iss
build\installer.iterative.iss = build\installer.iterative.iss build\installer.iterative.iss = build\installer.iterative.iss
build\publish-full.cmd = build\publish-full.cmd
build\publish.cmd = build\publish.cmd build\publish.cmd = build\publish.cmd
build\requirements.txt = build\requirements.txt build\requirements.txt = build\requirements.txt
build\upload.cmd = build\upload.cmd build\upload.cmd = build\upload.cmd
+8 -5
View File
@@ -1,12 +1,15 @@
#define MyAppVersion GetFileVersion("..\dist-azaion\Azaion.Suite.exe")
[Setup] [Setup]
AppId={{CCFEC8E2-0FCC-4B03-8EEA-00AF20D265E5}} AppId={{CCFEC8E2-0FCC-4B03-8EEA-00AF20D265E5}}
AppName=Azaion Suite AppName=Azaion Suite
AppVersion=1.5.0 AppVersion={#MyAppVersion}
AppPublisher=Azaion Ukraine VersionInfoVersion={#MyAppVersion}
AppPublisher=Azaion LLC
DefaultDirName={localappdata}\Azaion\Azaion Suite DefaultDirName={localappdata}\Azaion\Azaion Suite
DefaultGroupName=Azaion Suite DefaultGroupName=Azaion Suite
OutputDir=..\ OutputDir=..\
OutputBaseFilename=AzaionSuite.Full.1.5.0 OutputBaseFilename=AzaionSuite.Full.{#MyAppVersion}
SetupIconFile=..\dist-azaion\logo.ico SetupIconFile=..\dist-azaion\logo.ico
UninstallDisplayName=Azaion Suite UninstallDisplayName=Azaion Suite
UninstallDisplayIcon={app}\Azaion.Suite.exe UninstallDisplayIcon={app}\Azaion.Suite.exe
@@ -25,7 +28,7 @@ Source: "..\dist-dlls\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
Source: "..\dist-azaion\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Source: "..\dist-azaion\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs;
[Icons] [Icons]
Name: "{group}\Azaion Suite"; Filename: "{app}\Azaion.Suite.exe" Name: "{group}\Azaion Suite"; Filename: "{app}\Azaion.LoaderUI.exe"
Name: "{commondesktop}\Azaion Suite"; Filename: "{app}\Azaion.Suite.exe"; Tasks: desktopicon Name: "{commondesktop}\Azaion Suite"; Filename: "{app}\Azaion.LoaderUI.exe"; Tasks: desktopicon
[UninstallRun] [UninstallRun]
+2 -2
View File
@@ -26,7 +26,7 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{
Source: "..\dist-azaion\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Source: "..\dist-azaion\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs;
[Icons] [Icons]
Name: "{group}\Azaion Suite"; Filename: "{app}\Azaion.Suite.exe" Name: "{group}\Azaion Suite"; Filename: "{app}\Azaion.LoaderUI.exe"
Name: "{commondesktop}\Azaion Suite"; Filename: "{app}\Azaion.Suite.exe"; Tasks: desktopicon Name: "{commondesktop}\Azaion Suite"; Filename: "{app}\Azaion.LoaderUI.exe"; Tasks: desktopicon
[UninstallRun] [UninstallRun]
+4 -2
View File
@@ -15,10 +15,12 @@ call ..\gps-denied\image-matcher\build_gps
call build\download_models call build\download_models
echo building installer... echo building and upload iterative installer...
iscc build\installer.iterative.iss iscc build\installer.iterative.iss
call build\upload.cmd "suite" call build\upload.cmd "suite"
echo building full installer
iscc build\installer.full.iss
cd /d %CURRENT_DIR% cd /d %CURRENT_DIR%
echo Done! echo Done!