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 _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 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 Output; private static readonly Option IntensityWhite; private static readonly Option IntensityUv365; private static readonly Option 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); } /// /// Получить команду как корневую /// public static RootCommand GetCommand() => RootCommand; /// /// Зарегистрировать команду как подкоманду /// /// Команда в которой необходимо зарегистрировать подкоманду 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(); // Добавляем сервисы аппаратной части builder.Services.AddIlluminatorService("Hardware:Illuminator"); builder.Services.AddCameraService("Hardware:Camera"); // Добавляем рабочий сервис builder.Services.AddHostedService(services => new CaptureWorker( services.GetRequiredService>(), services.GetRequiredService(), services.GetRequiredService(), services.GetRequiredService(), outputFile, intensityWhite, intensityUv365, intensityUv254 ) ); // Собираем хост var host = builder.Build(); // Поскольку LibCameraLogSink, который перенаправляет логи из libcamera в хост, // не будет создан ни одним из других сервисов, его необходимо создать превентивно host.Services.GetRequiredService(); // Запускаем хост host.Run(); } }