forked from amkovkov/GranuSightSoftware2
feat: amine content separator calibration db editor
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
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;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GSS2.UI.Core.ViewModels;
|
||||
|
||||
public partial class AmineContentSeparatorCalibrationViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ILogger<AmineContentSeparatorCalibrationViewModel> _logger;
|
||||
private readonly AmineContentCalibrationContext _context;
|
||||
private readonly IlluminatorService _illuminatorService;
|
||||
private readonly CameraService _cameraService;
|
||||
private readonly ImageStorageService _imageStorageService;
|
||||
|
||||
private CancellationTokenSource? _captureImagesTaskCts = null;
|
||||
|
||||
[ObservableProperty] private ObservableCollection<SeparatorRecord> _records;
|
||||
[ObservableProperty] private SeparatorRecord _currentRecord;
|
||||
[ObservableProperty] private bool _isMenuOpen = false;
|
||||
[ObservableProperty] private bool _isNewRecord = false;
|
||||
|
||||
[ObservableProperty] private int _mainSectionsCurrentIndex = 0;
|
||||
[ObservableProperty] private string _switchMainSectionButtonText = "К изображениям";
|
||||
|
||||
[ObservableProperty] private Task? _captureImagesTask = null;
|
||||
|
||||
[ObservableProperty] private ObservableCollection<KeyValuePair<IImage, ImageRecord>> _images = new();
|
||||
[ObservableProperty] private bool _imagesIsCapturing = false;
|
||||
[ObservableProperty] private string _imagesCapturingProgress = "";
|
||||
[ObservableProperty] private int _imagesSectionCurrentIndex = 0;
|
||||
[ObservableProperty] private bool _imagesSectionCanScrollForward = false;
|
||||
[ObservableProperty] private bool _imagesSectionCanScrollBackward = false;
|
||||
|
||||
public AmineContentSeparatorCalibrationViewModel(ILogger<AmineContentSeparatorCalibrationViewModel> 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<SeparatorRecord>(_context.SeparatorRecords.Include(r => r.Images));
|
||||
|
||||
if (Records.Count() > 0)
|
||||
{
|
||||
CurrentRecord = Records.First();
|
||||
IsNewRecord = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentRecord = new SeparatorRecord()
|
||||
{
|
||||
Type = "",
|
||||
Batch = "",
|
||||
Images = new()
|
||||
};
|
||||
IsNewRecord = true;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Initialized");
|
||||
}
|
||||
|
||||
partial void OnRecordsChanged(ObservableCollection<SeparatorRecord>? oldValue, ObservableCollection<SeparatorRecord> newValue)
|
||||
{
|
||||
oldValue?.CollectionChanged -= OnRecordsCollectionChanged;
|
||||
newValue?.CollectionChanged += OnRecordsCollectionChanged;
|
||||
}
|
||||
partial void OnCurrentRecordChanged(SeparatorRecord value)
|
||||
{
|
||||
var images = value.Images
|
||||
.ToAsyncEnumerable()
|
||||
.Select(i => new KeyValuePair<string?, ImageRecord>(_imageStorageService.GetFullPath(i.ImagePath, "separator"), i))
|
||||
.Where(kv => kv.Key is not null)
|
||||
.Select(kv => new KeyValuePair<IImage, ImageRecord>(new Bitmap(kv.Key!), kv.Value));
|
||||
|
||||
Images = new(images.ToBlockingEnumerable());
|
||||
|
||||
IsNewRecord = _context.Entry(value).State == EntityState.Detached;
|
||||
}
|
||||
|
||||
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 SeparatorRecord()
|
||||
{
|
||||
Type = "",
|
||||
Batch = "",
|
||||
Images = new()
|
||||
};
|
||||
CurrentRecord = record;
|
||||
IsNewRecord = true;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void DeleteRecord()
|
||||
{
|
||||
var deletedRecord = CurrentRecord;
|
||||
var deletedIsNewRecord = IsNewRecord;
|
||||
IsNewRecord = true;
|
||||
CreateRecord();
|
||||
|
||||
if (!deletedIsNewRecord)
|
||||
{
|
||||
Records.Remove(deletedRecord);
|
||||
_context.SeparatorRecords.Remove(deletedRecord);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SaveRecord()
|
||||
{
|
||||
for (int i = 0; i < CurrentRecord.Images.Count(); i++)
|
||||
{
|
||||
var image = CurrentRecord.Images[i];
|
||||
if (_context.Entry(image).State == EntityState.Detached)
|
||||
{
|
||||
var attachedImage = _context.ImageRecords.Add(image);
|
||||
CurrentRecord.Images.RemoveAt(i);
|
||||
CurrentRecord.Images.Insert(i, attachedImage.Entity);
|
||||
}
|
||||
}
|
||||
if (_context.Entry(CurrentRecord).State == EntityState.Detached)
|
||||
{
|
||||
var attachedRecord = _context.SeparatorRecords.Add(CurrentRecord);
|
||||
CurrentRecord = attachedRecord.Entity;
|
||||
}
|
||||
if (IsNewRecord)
|
||||
Records.Add(CurrentRecord);
|
||||
|
||||
_context.SaveChanges();
|
||||
IsNewRecord = false;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SwitchMainSection()
|
||||
{
|
||||
MainSectionsCurrentIndex = MainSectionsCurrentIndex == 0 ? 1 : 0;
|
||||
SwitchMainSectionButtonText = MainSectionsCurrentIndex == 0 ?
|
||||
"К изображениям" :
|
||||
"К информации";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CaptureImage()
|
||||
{
|
||||
if (CaptureImagesTask is not null)
|
||||
return;
|
||||
|
||||
_captureImagesTaskCts = new CancellationTokenSource();
|
||||
var cancellationToken = _captureImagesTaskCts.Token;
|
||||
|
||||
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(() => ImagesIsCapturing = true);
|
||||
|
||||
try
|
||||
{
|
||||
var imageRecords = new List<ImageRecord>();
|
||||
|
||||
foreach (var (i, intensity) in intensities.Enumerate())
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
Dispatcher.UIThread.Invoke(() => ImagesCapturingProgress = $"Получение изображений: {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", "separator", cancellationToken);
|
||||
|
||||
image.Dispose();
|
||||
image = null;
|
||||
|
||||
imageRecords.Add(
|
||||
new ImageRecord()
|
||||
{
|
||||
ImagePath = fileName,
|
||||
VisibleIntensity = intensity.visible,
|
||||
Uv365Intensity = intensity.uv365,
|
||||
Uv254Intensity = intensity.uv254,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_illuminatorService.TurnOff();
|
||||
|
||||
var images = imageRecords
|
||||
.ToAsyncEnumerable()
|
||||
.Select(i => new KeyValuePair<string?, ImageRecord>(_imageStorageService.GetFullPath(i.ImagePath, "separator"), i))
|
||||
.Where(kv => kv.Key is not null)
|
||||
.Select(kv => new KeyValuePair<IImage, ImageRecord>(new Bitmap(kv.Key!), kv.Value));
|
||||
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
CurrentRecord.Images.Clear();
|
||||
CurrentRecord.Images.AddRange(imageRecords);
|
||||
|
||||
Images.Clear();
|
||||
Images = new(images.ToBlockingEnumerable());
|
||||
});
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{ }
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Exception while image capturing");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_illuminatorService.TurnOff();
|
||||
_captureImagesTaskCts = null;
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
ImagesIsCapturing = false;
|
||||
ImagesCapturingProgress = "";
|
||||
CaptureImagesTask = null;
|
||||
});
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
CaptureImagesTask.Start();
|
||||
}
|
||||
|
||||
[RelayCommand] private void CencelImageCapturing() => _captureImagesTaskCts?.Cancel();
|
||||
[RelayCommand] private void ImagesSectionPrevious() => ImagesSectionCurrentIndex--;
|
||||
[RelayCommand] private void ImagesSectionNext() => ImagesSectionCurrentIndex++;
|
||||
}
|
||||
Reference in New Issue
Block a user