using System.Collections.Specialized; using System.Collections.ObjectModel; using Microsoft.Extensions.Logging; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Avalonia.Media; using Avalonia.Media.Imaging; using Avalonia.Threading; using GSS2.Core.Hardware; using GSS2.Core.Extentions; using GSS2.Core.Analysis.AmineContent.Database.Calibration; namespace GSS2.UI.Core.ViewModels; public partial class AmineContentCalibrationViewModel : ViewModelBase { private readonly ILogger _logger; private readonly AmineContentCalibrationContext _context; private readonly IlluminatorService _illuminatorService; private readonly CameraService _cameraService; private readonly ImageStorageService _imageStorageService; private CancellationTokenSource? _captureImagesTaskCts = null; [ObservableProperty] private ObservableCollection _records; [ObservableProperty] private CalibrationRecord _currentRecord; [ObservableProperty] private bool _isMenuOpen = false; [ObservableProperty] private bool _isNewRecord = false; [ObservableProperty] private int _mainSectionsCurrentIndex = 0; [ObservableProperty] private string _switchMainSectionButtonText = "К изображениям"; [ObservableProperty] private int _imageTypeSectionCurrentIndex = 0; [ObservableProperty] private string _switchImageTypeSectionButtonText = "К изображениям пробы"; [ObservableProperty] private Task? _captureImagesTask = null; [ObservableProperty] private ObservableCollection> _separatorImages = new(); [ObservableProperty] private bool _separatorImagesIsCapturing = false; [ObservableProperty] private string _separatorImagesCapturingProgress = ""; [ObservableProperty] private int _separatorImagesSectionCurrentIndex = 0; [ObservableProperty] private bool _separatorImagesSectionCanScrollForward = false; [ObservableProperty] private bool _separatorImagesSectionCanScrollBackward = false; [ObservableProperty] private ObservableCollection> _sampleImages = new(); [ObservableProperty] private bool _sampleImagesIsCapturing = false; [ObservableProperty] private string _sampleImagesCapturingProgress = ""; [ObservableProperty] private int _sampleImagesSectionCurrentIndex = 0; [ObservableProperty] private bool _sampleImagesSectionCanScrollForward = false; [ObservableProperty] private bool _sampleImagesSectionCanScrollBackward = false; public AmineContentCalibrationViewModel(ILogger logger, AmineContentCalibrationContext context, IlluminatorService illuminatorService, CameraService cameraService, ImageStorageService imageStorageService) { _logger = logger; _logger.LogInformation("Initialization"); _context = context; _illuminatorService = illuminatorService; _cameraService = cameraService; _imageStorageService = imageStorageService; Records = new ObservableCollection(_context.CalibrationRecords); if (Records.Count() > 0) CurrentRecord = Records.First(); else { CurrentRecord = new CalibrationRecord() { SampleName = null, SampleComment = null, SampleBrand = null, SampleMass = null, SamplingDateTime = null, SamplingPlace = null, MixtureName = null, MixtureAmineContent = null, MixtureNormalRate = null, MixtureActualRate = null, SampleImages = new List(), SeparatorImages = new List() }; IsNewRecord = true; } _logger.LogInformation("Initialized"); } partial void OnRecordsChanged(ObservableCollection? oldValue, ObservableCollection newValue) { oldValue?.CollectionChanged -= OnRecordsCollectionChanged; newValue?.CollectionChanged += OnRecordsCollectionChanged; } partial void OnCurrentRecordChanged(CalibrationRecord value) { SeparatorImages = new( value.SeparatorImages .Select(i => new KeyValuePair(_imageStorageService.GetFullPath(i.ImagePath, "calibration"), i)) .Select(kv => new KeyValuePair(new Bitmap(kv.Key), kv.Value)) ); SampleImages = new( value.SampleImages .Select(i => new KeyValuePair(_imageStorageService.GetFullPath(i.ImagePath, "calibration"), i)) .Select(kv => new KeyValuePair(new Bitmap(kv.Key), kv.Value)) ); } private void OnRecordsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs ea) { Console.WriteLine($"RecordsCollectionChanged {ea.Action}"); } [RelayCommand] private void ToggleMenu() => IsMenuOpen = !IsMenuOpen; [RelayCommand] private void CloseMenu() => IsMenuOpen = false; [RelayCommand] private void CreateRecord() { var record = new CalibrationRecord() { SampleName = null, SampleComment = null, SampleBrand = null, SampleMass = null, SamplingDateTime = null, SamplingPlace = null, MixtureName = null, MixtureAmineContent = null, MixtureNormalRate = null, MixtureActualRate = null, SampleImages = new List(), SeparatorImages = new List() }; CurrentRecord = record; IsNewRecord = true; } // [RelayCommand] // private void DeleteRecord() // { // //TODO // // if (Record == null) return; // // _context.CalibrationRecords.Remove(Record); // // Records.Remove(Record); // // if (Records.Count() > 0) // // Record = Records.First(); // // else // // { // // var record = new CalibrationRecord(); // // _context.CalibrationRecords.Add(record); // // Records.Add(record); // // Record = record; // // } // IsNewRecord = true; // CreateRecord(); // } // [RelayCommand] // private void SaveRecord() // { // //TODO // // _context.SaveChanges(); // IsNewRecord = false; // } [RelayCommand] private void SwitchMainSection() { MainSectionsCurrentIndex = MainSectionsCurrentIndex == 0 ? 1 : 0; SwitchMainSectionButtonText = MainSectionsCurrentIndex == 0 ? "К изображениям" : "К общей информации"; } [RelayCommand] private void SwitchImageTypeSection() { ImageTypeSectionCurrentIndex = ImageTypeSectionCurrentIndex == 0 ? 1 : 0; SwitchImageTypeSectionButtonText = ImageTypeSectionCurrentIndex == 0 ? "К изображениям пробы" : "К изображениям сепаратора"; } [RelayCommand] private void CaptureImage() { if (CaptureImagesTask is not null) return; _captureImagesTaskCts = new CancellationTokenSource(); var cancellationToken = _captureImagesTaskCts.Token; var imageTypeSectionCurrentIndex = ImageTypeSectionCurrentIndex; CaptureImagesTask = new Task(async () => { _illuminatorService.TurnOn(); (double visible, double uv365, double uv254)[] intensities = { (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) }; Dispatcher.UIThread.Invoke(() => { if (imageTypeSectionCurrentIndex == 0) SeparatorImagesIsCapturing = true; else SampleImagesIsCapturing = true; }); try { var imageRecords = new List(); foreach (var (i, intensity) in intensities.Enumerate()) { cancellationToken.ThrowIfCancellationRequested(); Dispatcher.UIThread.Invoke(() => { if (imageTypeSectionCurrentIndex == 0) SeparatorImagesCapturingProgress = $"Получение изображений: {i + 1} / {intensities.Count()}"; else SampleImagesCapturingProgress = $"Получение изображений: {i + 1} / {intensities.Count()}"; }); _illuminatorService.SetIntensity(intensity.visible, intensity.uv365, intensity.uv254); OpenCvSharp.Mat? image; image = await _cameraService.CaptureImage(cancellationToken, 5); if (image is null) { _logger.LogError("Cannot get image"); return; } var bytes = image.ImEncode(".png"); string fileName; using (var stream = new MemoryStream(bytes)) fileName = await _imageStorageService.SaveAsync(stream, ".png", "calibration", cancellationToken); image.Dispose(); image = null; imageRecords.Add( new ImageRecord() { ImagePath = fileName, VisibleIntensity = intensity.visible, Uv365Intensity = intensity.uv365, Uv254Intensity = intensity.uv254, } ); } _illuminatorService.TurnOff(); Dispatcher.UIThread.Invoke(() => { if (imageTypeSectionCurrentIndex == 0) { CurrentRecord.SeparatorImages.Clear(); CurrentRecord.SeparatorImages.AddRange(imageRecords); SeparatorImages.Clear(); imageRecords .Select(i => new KeyValuePair(_imageStorageService.GetFullPath(i.ImagePath, "calibration"), i)) .Select(kv => new KeyValuePair(new Bitmap(kv.Key), kv.Value)) .ToList() .ForEach(SeparatorImages.Add); } else { CurrentRecord.SampleImages.Clear(); CurrentRecord.SampleImages.AddRange(imageRecords); SampleImages.Clear(); imageRecords .Select(i => new KeyValuePair(_imageStorageService.GetFullPath(i.ImagePath, "calibration"), i)) .Select(kv => new KeyValuePair(new Bitmap(kv.Key), kv.Value)) .ToList() .ForEach(SampleImages.Add); } }); } catch (TaskCanceledException) { } catch (Exception ex) { _logger.LogError(ex, "Exception while image capturing"); } finally { _illuminatorService.TurnOff(); _captureImagesTaskCts = null; Dispatcher.UIThread.Invoke(() => { if (imageTypeSectionCurrentIndex == 0) { SeparatorImagesIsCapturing = false; SeparatorImagesCapturingProgress = ""; } else { SampleImagesIsCapturing = false; SampleImagesCapturingProgress = ""; } CaptureImagesTask = null; }); } }, cancellationToken); CaptureImagesTask.Start(); } [RelayCommand] private void CencelImageCapturing() => _captureImagesTaskCts?.Cancel(); [RelayCommand] private void SeparatorImagesSectionPrevious() => SeparatorImagesSectionCurrentIndex--; [RelayCommand] private void SeparatorImagesSectionNext() => SeparatorImagesSectionCurrentIndex++; [RelayCommand] private void SampleImagesSectionPrevious() => SampleImagesSectionCurrentIndex--; [RelayCommand] private void SampleImagesSectionNext() => SampleImagesSectionCurrentIndex++; // [RelayCommand] // private void SelectImagesFromAnotherRecord() // { // } }