forked from amkovkov/GranuSightSoftware2
feat: mock run mode
extract IImageCapturerService interface from ImageCapturerService add ImageCapturerMockService as implementation of IImageCapturerService add --mock flag in command line interface and implement its usage
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
using OpenCvSharp;
|
||||||
|
|
||||||
|
namespace GSS2.Core.Analysis.AmineContent;
|
||||||
|
|
||||||
|
public interface IImageCapturerService
|
||||||
|
{
|
||||||
|
public Task CaptureImages(Action<(int i, double visible, double uv365, double uv254, Mat image)> callback, CancellationToken cancellationToken);
|
||||||
|
public Task CaptureImages(Func<(int i, double visible, double uv365, double uv254, Mat image), Task> callback, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
using OpenCvSharp;
|
||||||
|
|
||||||
|
using GSS2.Core.Extensions;
|
||||||
|
|
||||||
|
namespace GSS2.Core.Analysis.AmineContent;
|
||||||
|
|
||||||
|
public class ImageCapturerMockService : IImageCapturerService
|
||||||
|
{
|
||||||
|
// Список интенсивностей освещения для каждого кадра
|
||||||
|
public static readonly ReadOnlyCollection<(double visible, double uv365, double uv254)> IlluminatorIntensities =
|
||||||
|
[
|
||||||
|
(0.02500, 0.0, 0.0),
|
||||||
|
(0.01250, 0.0, 0.0),
|
||||||
|
(0.00625, 0.0, 0.0),
|
||||||
|
(0.00000, 1.0, 0.0),
|
||||||
|
(0.00000, 0.0, 1.0),
|
||||||
|
(0.00000, 1.0, 1.0),
|
||||||
|
(0.01250, 1.0, 0.0),
|
||||||
|
(0.01250, 0.0, 1.0),
|
||||||
|
(0.01250, 1.0, 1.0),
|
||||||
|
(0.00625, 1.0, 0.0),
|
||||||
|
(0.00625, 0.0, 1.0),
|
||||||
|
(0.00625, 1.0, 1.0)
|
||||||
|
];
|
||||||
|
|
||||||
|
private readonly string _imagesDirectoryPath;
|
||||||
|
private readonly ILogger<ImageCapturerMockService> _logger;
|
||||||
|
|
||||||
|
public ImageCapturerMockService(string imagesDirectoryPath, ILogger<ImageCapturerMockService> logger)
|
||||||
|
{
|
||||||
|
_imagesDirectoryPath = imagesDirectoryPath;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CaptureImages(Action<(int i, double visible, double uv365, double uv254, Mat image)> callback, CancellationToken cancellationToken) =>
|
||||||
|
await CaptureImages(async (data) => callback(data), cancellationToken);
|
||||||
|
|
||||||
|
public async Task CaptureImages(Func<(int i, double visible, double uv365, double uv254, Mat image), Task> callback, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
foreach (var (i, (visible, uv365, uv254)) in IlluminatorIntensities.Enumerate())
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
_logger.LogInformation("Capturing images: {} / {} ({};{};{})", i + 1, IlluminatorIntensities.Count(), visible, uv365, uv254);
|
||||||
|
|
||||||
|
Mat? image = Cv2.ImRead(Path.Join(_imagesDirectoryPath, $"{visible}-{uv365}-{uv254}.png"));
|
||||||
|
if (image is null)
|
||||||
|
throw new Exception("Image capture failed");
|
||||||
|
|
||||||
|
await callback.Invoke((i, visible, uv365, uv254, image));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ using OpenCvSharp;
|
|||||||
|
|
||||||
namespace GSS2.Core.Analysis.AmineContent;
|
namespace GSS2.Core.Analysis.AmineContent;
|
||||||
|
|
||||||
public class ImageCapturerService
|
public class ImageCapturerService : IImageCapturerService
|
||||||
{
|
{
|
||||||
// Список интенсивностей освещения для каждого кадра
|
// Список интенсивностей освещения для каждого кадра
|
||||||
public static readonly ReadOnlyCollection<(double visible, double uv365, double uv254)> IlluminatorIntensities =
|
public static readonly ReadOnlyCollection<(double visible, double uv365, double uv254)> IlluminatorIntensities =
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ using Microsoft.Extensions.Logging.Console;
|
|||||||
|
|
||||||
using GSS2.Core.Logging;
|
using GSS2.Core.Logging;
|
||||||
using GSS2.Core.Hardware;
|
using GSS2.Core.Hardware;
|
||||||
using GSS2.Core.Analysis.AmineContent.Database.Calibration;
|
using GSS2.Core.Analysis.AmineContent.Database;
|
||||||
using GSS2.Core.Analysis.AmineContent.Database.Results;
|
|
||||||
using GSS2.Core.Analysis.AmineContent;
|
using GSS2.Core.Analysis.AmineContent;
|
||||||
|
|
||||||
using ConsoleFormatter = GSS2.Core.Logging.ConsoleFormatter;
|
using ConsoleFormatter = GSS2.Core.Logging.ConsoleFormatter;
|
||||||
@@ -128,8 +127,19 @@ public static class DependencyInjectionHelper
|
|||||||
ServiceLifetime.Singleton
|
ServiceLifetime.Singleton
|
||||||
);
|
);
|
||||||
|
|
||||||
public static IServiceCollection AddAmineContentImageCapturerService(this IServiceCollection services) => services
|
public static IServiceCollection AddAmineContentImageCapturerService(this IServiceCollection services, bool mock = false)
|
||||||
.AddSingleton<ImageCapturerService>();
|
{
|
||||||
|
if (!mock)
|
||||||
|
return services.AddSingleton<ImageCapturerService>();
|
||||||
|
|
||||||
|
return services.AddSingleton<ImageCapturerMockService>(serviceProvider =>
|
||||||
|
{
|
||||||
|
var logger = serviceProvider.GetService<ILogger<ImageCapturerMockService>>();
|
||||||
|
if (logger is null)
|
||||||
|
throw new Exception("Cannot get logger");
|
||||||
|
return new ImageCapturerMockService("./", logger);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddAmineContentAnalyzerService(this IServiceCollection services) => services
|
public static IServiceCollection AddAmineContentAnalyzerService(this IServiceCollection services) => services
|
||||||
.AddSingleton<AnalyzerService>();
|
.AddSingleton<AnalyzerService>();
|
||||||
|
|||||||
+19
-14
@@ -10,9 +10,7 @@ using Avalonia.OpenGL.Egl;
|
|||||||
|
|
||||||
using GSS2.Core;
|
using GSS2.Core;
|
||||||
using GSS2.Core.Logging;
|
using GSS2.Core.Logging;
|
||||||
using GSS2.Core.Analysis.AmineContent.Database.Calibration;
|
using GSS2.Core.Analysis.AmineContent.Database;
|
||||||
using GSS2.Core.Analysis.AmineContent.Database.Results;
|
|
||||||
using GSS2.Core.Hardware;
|
|
||||||
|
|
||||||
namespace GSS2.Commands;
|
namespace GSS2.Commands;
|
||||||
|
|
||||||
@@ -29,6 +27,7 @@ public static class UI
|
|||||||
public static readonly Option<bool> Fullscreen;
|
public static readonly Option<bool> Fullscreen;
|
||||||
public static readonly Option<RenderingModes> RenderingMode;
|
public static readonly Option<RenderingModes> RenderingMode;
|
||||||
public static readonly Option<bool> HideConsole;
|
public static readonly Option<bool> HideConsole;
|
||||||
|
public static readonly Option<bool> Mock;
|
||||||
|
|
||||||
static UI()
|
static UI()
|
||||||
{
|
{
|
||||||
@@ -52,6 +51,11 @@ public static class UI
|
|||||||
Description = "Отключить вывод в консоль",
|
Description = "Отключить вывод в консоль",
|
||||||
DefaultValueFactory = (result) => result.GetValue(RenderingMode) == RenderingModes.DRM
|
DefaultValueFactory = (result) => result.GetValue(RenderingMode) == RenderingModes.DRM
|
||||||
};
|
};
|
||||||
|
Mock = new("--mock")
|
||||||
|
{
|
||||||
|
Description = "Запуск в фиктивном режиме без инициализации и использования периферии",
|
||||||
|
DefaultValueFactory = (result) => false
|
||||||
|
};
|
||||||
|
|
||||||
// Изменяем описания стандартных опций
|
// Изменяем описания стандартных опций
|
||||||
Helper.TranslateDefaultOptionDescriptions(RootCommand);
|
Helper.TranslateDefaultOptionDescriptions(RootCommand);
|
||||||
@@ -60,11 +64,13 @@ public static class UI
|
|||||||
RootCommand.Add(Fullscreen);
|
RootCommand.Add(Fullscreen);
|
||||||
RootCommand.Add(RenderingMode);
|
RootCommand.Add(RenderingMode);
|
||||||
RootCommand.Add(HideConsole);
|
RootCommand.Add(HideConsole);
|
||||||
|
RootCommand.Add(Mock);
|
||||||
RootCommand.SetAction(CommandAction);
|
RootCommand.SetAction(CommandAction);
|
||||||
|
|
||||||
Command.Add(Fullscreen);
|
Command.Add(Fullscreen);
|
||||||
Command.Add(RenderingMode);
|
Command.Add(RenderingMode);
|
||||||
Command.Add(HideConsole);
|
Command.Add(HideConsole);
|
||||||
|
Command.Add(Mock);
|
||||||
Command.SetAction(CommandAction);
|
Command.SetAction(CommandAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +91,7 @@ public static class UI
|
|||||||
var fullscreen = result.GetValue(Fullscreen);
|
var fullscreen = result.GetValue(Fullscreen);
|
||||||
var renderingMode = result.GetValue(RenderingMode);
|
var renderingMode = result.GetValue(RenderingMode);
|
||||||
var hideConsole = result.GetValue(HideConsole);
|
var hideConsole = result.GetValue(HideConsole);
|
||||||
|
var mock = result.GetValue(Mock);
|
||||||
|
|
||||||
// Если выбран режим рендеринга DRM, то приложение будет рисоваться вместе с консолью,
|
// Если выбран режим рендеринга DRM, то приложение будет рисоваться вместе с консолью,
|
||||||
// поэтому если не перехватить консоль, то вывод в неё будет рисоваться вместе с интерфейсом
|
// поэтому если не перехватить консоль, то вывод в неё будет рисоваться вместе с интерфейсом
|
||||||
@@ -92,7 +99,7 @@ public static class UI
|
|||||||
SilenceConsole();
|
SilenceConsole();
|
||||||
|
|
||||||
// Собираем хост
|
// Собираем хост
|
||||||
var host = BuildHost();
|
var host = BuildHost(mock);
|
||||||
|
|
||||||
// Запускаем хост асинхронно без блокировки потока
|
// Запускаем хост асинхронно без блокировки потока
|
||||||
_ = host.RunAsync();
|
_ = host.RunAsync();
|
||||||
@@ -137,7 +144,7 @@ public static class UI
|
|||||||
thread.Start();
|
thread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IHost BuildHost()
|
private static IHost BuildHost(bool mock)
|
||||||
{
|
{
|
||||||
// Создаём хост
|
// Создаём хост
|
||||||
var builder = Host.CreateApplicationBuilder();
|
var builder = Host.CreateApplicationBuilder();
|
||||||
@@ -150,17 +157,19 @@ public static class UI
|
|||||||
builder.Services.AddSingleton<LibCameraLogSink>();
|
builder.Services.AddSingleton<LibCameraLogSink>();
|
||||||
|
|
||||||
// Добавляем сервисы баз данных
|
// Добавляем сервисы баз данных
|
||||||
builder.Services.AddAmineContentCalibrationContext(builder.Configuration);
|
builder.Services.AddAmineContentContext(builder.Configuration);
|
||||||
builder.Services.AddAmineContentResultsContext(builder.Configuration);
|
|
||||||
|
|
||||||
// Добавляем сервисы аппаратной части и прочее
|
// Добавляем сервисы аппаратной части и прочее
|
||||||
|
if (!mock)
|
||||||
|
{
|
||||||
builder.Services.AddLightsService("Hardware:Lights");
|
builder.Services.AddLightsService("Hardware:Lights");
|
||||||
builder.Services.AddTemperatureHumidityService("Hardware:TemperatureHumidity");
|
builder.Services.AddTemperatureHumidityService("Hardware:TemperatureHumidity");
|
||||||
builder.Services.AddIlluminatorService("Hardware:Illuminator");
|
builder.Services.AddIlluminatorService("Hardware:Illuminator");
|
||||||
builder.Services.AddCameraService("Hardware:Camera", true);
|
builder.Services.AddCameraService("Hardware:Camera", true);
|
||||||
builder.Services.AddComputeResourcesService();
|
builder.Services.AddComputeResourcesService();
|
||||||
|
}
|
||||||
builder.Services.AddImageStorageService(new DirectoryInfo(builder.Configuration.GetValue<string>("ImageStorage") ?? "images"));
|
builder.Services.AddImageStorageService(new DirectoryInfo(builder.Configuration.GetValue<string>("ImageStorage") ?? "images"));
|
||||||
builder.Services.AddAmineContentImageCapturerService();
|
builder.Services.AddAmineContentImageCapturerService(mock);
|
||||||
builder.Services.AddAmineContentAnalyzerService();
|
builder.Services.AddAmineContentAnalyzerService();
|
||||||
|
|
||||||
// Добавляем элементы интерфейса как сервисы
|
// Добавляем элементы интерфейса как сервисы
|
||||||
@@ -171,17 +180,13 @@ public static class UI
|
|||||||
|
|
||||||
// Поскольку LibCameraLogSink, который перенаправляет логи из libcamera в хост,
|
// Поскольку LibCameraLogSink, который перенаправляет логи из libcamera в хост,
|
||||||
// не будет создан ни одним из других сервисов, его необходимо создать превентивно
|
// не будет создан ни одним из других сервисов, его необходимо создать превентивно
|
||||||
|
if (!mock)
|
||||||
host.Services.GetRequiredService<LibCameraLogSink>();
|
host.Services.GetRequiredService<LibCameraLogSink>();
|
||||||
|
|
||||||
// Применяем миграции баз данных если их схема данных изменялась
|
// Применяем миграции баз данных если их схема данных изменялась
|
||||||
using (var scope = host.Services.CreateScope())
|
using (var scope = host.Services.CreateScope())
|
||||||
{
|
{
|
||||||
var context = scope.ServiceProvider.GetRequiredService<CalibrationContext>();
|
var context = scope.ServiceProvider.GetRequiredService<Context>();
|
||||||
context.Database.Migrate();
|
|
||||||
}
|
|
||||||
using (var scope = host.Services.CreateScope())
|
|
||||||
{
|
|
||||||
var context = scope.ServiceProvider.GetRequiredService<ResultsContext>();
|
|
||||||
context.Database.Migrate();
|
context.Database.Migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user