forked from amkovkov/GranuSightSoftware2
feat: starting to build a application itself
add "capture" and "camera-tuning" commands for cli
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
using System.CommandLine;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using OpenCvSharp;
|
||||
|
||||
using GSS2.Core;
|
||||
using GSS2.Core.Logging;
|
||||
using GSS2.Core.Hardware;
|
||||
using GSS2.Core.Extensions;
|
||||
|
||||
namespace GSS2.Commands;
|
||||
|
||||
public static class Capture
|
||||
{
|
||||
private class CaptureWorker : BackgroundService
|
||||
{
|
||||
private readonly ILogger<CaptureWorker> _logger;
|
||||
private readonly IHostApplicationLifetime _applicationLifetime;
|
||||
private readonly CameraService _cameraService;
|
||||
private readonly IlluminatorService _illuminatorService;
|
||||
|
||||
private readonly FileInfo _output;
|
||||
private readonly double _intensityWhite;
|
||||
private readonly double _intensityUv365;
|
||||
private readonly double _intensityUv254;
|
||||
|
||||
public CaptureWorker(
|
||||
ILogger<CaptureWorker> logger,
|
||||
IHostApplicationLifetime applicationLifetime,
|
||||
CameraService cameraService,
|
||||
IlluminatorService illuminatorService,
|
||||
FileInfo output,
|
||||
double intensityWhite,
|
||||
double intensityUv365,
|
||||
double intensityUv254)
|
||||
{
|
||||
_applicationLifetime = applicationLifetime;
|
||||
_logger = logger;
|
||||
_cameraService = cameraService;
|
||||
_illuminatorService = illuminatorService;
|
||||
_output = output;
|
||||
_intensityWhite = intensityWhite;
|
||||
_intensityUv365 = intensityUv365;
|
||||
_intensityUv254 = intensityUv254;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
// Логгируем информацию
|
||||
_logger.LogInformation("Захват изображения с камеры...");
|
||||
_logger.LogInformation("Файл выходного изображения: {}", _output);
|
||||
_logger.LogInformation("Интенсивность белого канала осветителя: {}", _intensityWhite);
|
||||
_logger.LogInformation("Интенсивность УФ канала осветителя 365 нм: {}", _intensityUv365);
|
||||
_logger.LogInformation("Интенсивность УФ канала осветителя 254 нм: {}", _intensityUv254);
|
||||
|
||||
// Инициализируем камеру
|
||||
await _cameraService.InitializeAsync(stoppingToken);
|
||||
stoppingToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Включаем осветитель
|
||||
_illuminatorService.SetIntensity(_intensityWhite, _intensityUv365, _intensityUv254);
|
||||
_illuminatorService.TurnOn();
|
||||
|
||||
// Захватываем изображение
|
||||
Mat? image;
|
||||
try
|
||||
{
|
||||
image = await _cameraService.CaptureImage(stoppingToken, 5);
|
||||
|
||||
if (image is null)
|
||||
throw new Exception("Не удаётся захватить изображение с камеры.");
|
||||
|
||||
_illuminatorService.TurnOff();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_illuminatorService.TurnOff();
|
||||
_logger.LogCritical(ex, "Ошибка при захвате изображения");
|
||||
_applicationLifetime.StopApplication();
|
||||
return;
|
||||
}
|
||||
|
||||
// Сохраняем изображение в файл
|
||||
try
|
||||
{
|
||||
image.ImWrite(path: _output.FullName);
|
||||
_logger.LogInformation("Изображение сохранено: {}", _output.FullName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogCritical(ex, "Ошибка при сохранении изображения");
|
||||
_applicationLifetime.StopApplication();
|
||||
return;
|
||||
}
|
||||
|
||||
_applicationLifetime.StopApplication();
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly RootCommand RootCommand;
|
||||
private static readonly Command Command;
|
||||
private static readonly Option<FileInfo> Output;
|
||||
private static readonly Option<double> IntensityWhite;
|
||||
private static readonly Option<double> IntensityUv365;
|
||||
private static readonly Option<double> IntensityUv254;
|
||||
|
||||
static Capture()
|
||||
{
|
||||
// Создаём команды, опции, аргументы и пр.
|
||||
RootCommand = new("Захватить изображение с камеры");
|
||||
Command = new("capture")
|
||||
{
|
||||
Description = "Захватить изображение с камеры"
|
||||
};
|
||||
Output = new("--output", ["-o"])
|
||||
{
|
||||
Description = "Файл выходного изображения",
|
||||
DefaultValueFactory = (_) => new FileInfo("image.png")
|
||||
};
|
||||
IntensityWhite = new("--intensity-white", ["-iw"])
|
||||
{
|
||||
Description = "Интенсивность белого канала осветителя (от 0 до 1)",
|
||||
DefaultValueFactory = (_) => 1
|
||||
};
|
||||
IntensityUv365 = new("--intensity-uv365", ["-i365"])
|
||||
{
|
||||
Description = "Интенсивность УФ канала осветителя 365 нм (от 0 до 1)",
|
||||
DefaultValueFactory = (_) => 0
|
||||
};
|
||||
IntensityUv254 = new("--intensity-uv254", ["-i254"])
|
||||
{
|
||||
Description = "Интенсивность УФ канала осветителя 254 нм (from 0 to 1)",
|
||||
DefaultValueFactory = (_) => 0
|
||||
};
|
||||
|
||||
// Изменяем описания стандартных опций
|
||||
Helper.TranslateDefaultOptionDescriptions(RootCommand);
|
||||
|
||||
// Наполняем команды
|
||||
RootCommand.Add(Output);
|
||||
RootCommand.Add(IntensityWhite);
|
||||
RootCommand.Add(IntensityUv365);
|
||||
RootCommand.Add(IntensityUv254);
|
||||
RootCommand.SetAction(CommandAction);
|
||||
|
||||
Command.Add(Output);
|
||||
Command.Add(IntensityWhite);
|
||||
Command.Add(IntensityUv365);
|
||||
Command.Add(IntensityUv254);
|
||||
Command.SetAction(CommandAction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить команду как корневую
|
||||
/// </summary>
|
||||
public static RootCommand GetCommand() => RootCommand;
|
||||
|
||||
/// <summary>
|
||||
/// Зарегистрировать команду как подкоманду
|
||||
/// </summary>
|
||||
/// <param name="command">Команда в которой необходимо зарегистрировать подкоманду</param>
|
||||
public static void RegisterCommand(Command command) => command.Add(Command);
|
||||
|
||||
private static void CommandAction(ParseResult result)
|
||||
{
|
||||
/// Получаем значения аргументов командной строки
|
||||
var outputFile = result.GetRequiredValue(Output);
|
||||
var intensityWhite = result.GetRequiredValue(IntensityWhite);
|
||||
var intensityUv365 = result.GetRequiredValue(IntensityUv365);
|
||||
var intensityUv254 = result.GetRequiredValue(IntensityUv254);
|
||||
|
||||
// Создаём хост
|
||||
var builder = Host.CreateApplicationBuilder();
|
||||
|
||||
// Добавляем конфигурацию из appsettings.json
|
||||
builder.Configuration.AddAppSettingsJson();
|
||||
|
||||
// Конфигурируем логгер
|
||||
builder.Logging.ConfigureLogging();
|
||||
builder.Services.AddSingleton<LibCameraLogSink>();
|
||||
|
||||
// Добавляем сервисы аппаратной части
|
||||
builder.Services.AddIlluminatorService("Hardware:Illuminator");
|
||||
builder.Services.AddCameraService("Hardware:Camera");
|
||||
|
||||
// Добавляем рабочий сервис
|
||||
builder.Services.AddHostedService<CaptureWorker>(services =>
|
||||
new CaptureWorker(
|
||||
services.GetRequiredService<ILogger<CaptureWorker>>(),
|
||||
services.GetRequiredService<IHostApplicationLifetime>(),
|
||||
services.GetRequiredService<CameraService>(),
|
||||
services.GetRequiredService<IlluminatorService>(),
|
||||
outputFile,
|
||||
intensityWhite, intensityUv365, intensityUv254
|
||||
)
|
||||
);
|
||||
|
||||
// Собираем хост
|
||||
var host = builder.Build();
|
||||
|
||||
// Поскольку LibCameraLogSink, который перенаправляет логи из libcamera в хост,
|
||||
// не будет создан ни одним из других сервисов, его необходимо создать превентивно
|
||||
host.Services.GetRequiredService<LibCameraLogSink>();
|
||||
|
||||
// Запускаем хост
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user