forked from amkovkov/GranuSightSoftware2
345 lines
12 KiB
C#
345 lines
12 KiB
C#
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.Extensions;
|
|
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? _selectedRecord;
|
|
[ObservableProperty] private SeparatorRecord _editingRecord;
|
|
[ObservableProperty] private bool _isMenuOpen = false;
|
|
|
|
[ObservableProperty] private int _mainSectionsCurrentIndex = 0;
|
|
[ObservableProperty] private bool _mainSectionCanScrollBackward = false;
|
|
[ObservableProperty] private bool _mainSectionCanScrollForward = true;
|
|
[ObservableProperty] private bool _mainSectionIsScrolling = true;
|
|
|
|
[ObservableProperty] private Task? _captureImagesTask = null;
|
|
|
|
[ObservableProperty] private ObservableCollection<AsyncImageRecordViewModel> _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;
|
|
[ObservableProperty] private bool _imagesSectionIsScrolling = true;
|
|
|
|
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));
|
|
|
|
SelectedRecord = Records.FirstOrDefault();
|
|
if (SelectedRecord is null)
|
|
EditingRecord = new()
|
|
{
|
|
Type = "",
|
|
Batch = "",
|
|
Images = new()
|
|
};
|
|
else
|
|
EditingRecord = CopyRecord(SelectedRecord);
|
|
|
|
_logger.LogInformation("Initialized");
|
|
}
|
|
|
|
partial void OnSelectedRecordChanged(SeparatorRecord? value)
|
|
{
|
|
if (SelectedRecord is null)
|
|
EditingRecord = new()
|
|
{
|
|
Type = "",
|
|
Batch = "",
|
|
Images = new()
|
|
};
|
|
else
|
|
EditingRecord = CopyRecord(SelectedRecord);
|
|
}
|
|
partial void OnEditingRecordChanged(SeparatorRecord value)
|
|
{
|
|
var images = value.Images
|
|
.Select(i =>
|
|
{
|
|
var path = _imageStorageService.GetFullPath(i.ImagePath, "separator");
|
|
if (path is null)
|
|
return null;
|
|
return new AsyncImageRecordViewModel(path, i);
|
|
})
|
|
.OfType<AsyncImageRecordViewModel>();
|
|
|
|
Images = new(images);
|
|
}
|
|
|
|
[RelayCommand] private void ToggleMenu() => IsMenuOpen = !IsMenuOpen;
|
|
[RelayCommand] private void CloseMenu() => IsMenuOpen = false;
|
|
|
|
[RelayCommand] private void CreateRecord() => SelectedRecord = null;
|
|
|
|
[RelayCommand]
|
|
private void DeleteRecord()
|
|
{
|
|
var deletedRecord = SelectedRecord;
|
|
CreateRecord();
|
|
if (deletedRecord is not null)
|
|
{
|
|
Records.Remove(deletedRecord);
|
|
_context.SeparatorRecords.Remove(deletedRecord);
|
|
_context.SaveChanges();
|
|
}
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void SaveRecord()
|
|
{
|
|
if (SelectedRecord is null)
|
|
{
|
|
var newEntity = CopyRecord(EditingRecord);
|
|
|
|
for (int i = 0; i < newEntity.Images.Count(); i++)
|
|
{
|
|
var image = newEntity.Images[i];
|
|
if (_context.Entry(image).State is not EntityState.Detached)
|
|
continue;
|
|
|
|
var attachedImage = _context.ImageRecords.Add(image);
|
|
newEntity.Images.RemoveAt(i);
|
|
newEntity.Images.Insert(i, attachedImage.Entity);
|
|
}
|
|
if (_context.Entry(newEntity).State is EntityState.Detached)
|
|
{
|
|
var attachedRecord = _context.SeparatorRecords.Add(newEntity);
|
|
newEntity = attachedRecord.Entity;
|
|
}
|
|
|
|
_context.SaveChanges();
|
|
|
|
Records.Add(newEntity);
|
|
SelectedRecord = newEntity;
|
|
}
|
|
else
|
|
{
|
|
SelectedRecord.Type = EditingRecord.Type;
|
|
SelectedRecord.Batch = EditingRecord.Batch;
|
|
|
|
var imagesToRemove = SelectedRecord.Images
|
|
.Where(e => EditingRecord.Images.All(i => i.Id != e.Id))
|
|
.ToList();
|
|
|
|
foreach (var image in imagesToRemove)
|
|
SelectedRecord.Images.Remove(image);
|
|
|
|
foreach (var editedImage in EditingRecord.Images)
|
|
{
|
|
var existing = EditingRecord.Images
|
|
.FirstOrDefault(i => i.Id == editedImage.Id);
|
|
|
|
if (existing is null)
|
|
{
|
|
SelectedRecord.Images.Add(
|
|
new ImageRecord
|
|
{
|
|
ImagePath = editedImage.ImagePath,
|
|
VisibleIntensity = editedImage.VisibleIntensity,
|
|
Uv365Intensity = editedImage.Uv365Intensity,
|
|
Uv254Intensity = editedImage.Uv254Intensity
|
|
}
|
|
);
|
|
}
|
|
else
|
|
{
|
|
existing.ImagePath = editedImage.ImagePath;
|
|
existing.VisibleIntensity = editedImage.VisibleIntensity;
|
|
existing.Uv365Intensity = editedImage.Uv365Intensity;
|
|
existing.Uv254Intensity = editedImage.Uv254Intensity;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < SelectedRecord.Images.Count(); i++)
|
|
{
|
|
var image = SelectedRecord.Images[i];
|
|
if (_context.Entry(image).State is not EntityState.Detached)
|
|
continue;
|
|
|
|
var attachedImage = _context.ImageRecords.Add(image);
|
|
SelectedRecord.Images.RemoveAt(i);
|
|
SelectedRecord.Images.Insert(i, attachedImage.Entity);
|
|
}
|
|
if (_context.Entry(SelectedRecord).State is EntityState.Detached)
|
|
{
|
|
var attachedRecord = _context.SeparatorRecords.Add(SelectedRecord);
|
|
var index = Records.IndexOf(SelectedRecord);
|
|
Records[index] = attachedRecord.Entity;
|
|
SelectedRecord = attachedRecord.Entity;
|
|
}
|
|
|
|
_context.SaveChanges();
|
|
}
|
|
}
|
|
|
|
[RelayCommand] private void SwitchMainSection() => MainSectionsCurrentIndex = MainSectionsCurrentIndex == 0 ? 1 : 0;
|
|
|
|
[RelayCommand]
|
|
private void CaptureImage()
|
|
{
|
|
if (CaptureImagesTask is not null)
|
|
return;
|
|
|
|
_captureImagesTaskCts = new CancellationTokenSource();
|
|
var cancellationToken = _captureImagesTaskCts.Token;
|
|
|
|
CaptureImagesTask = Task.Run(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())
|
|
{
|
|
_logger.LogInformation("Capturing images: {} / {} ({};{};{})", i + 1, intensities.Count(), intensity.visible, intensity.uv365, intensity.uv254);
|
|
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
|
|
.Select(i =>
|
|
{
|
|
var path = _imageStorageService.GetFullPath(i.ImagePath, "separator");
|
|
if (path is null)
|
|
return null;
|
|
return new AsyncImageRecordViewModel(path, i);
|
|
})
|
|
.OfType<AsyncImageRecordViewModel>();
|
|
|
|
Dispatcher.UIThread.Invoke(() =>
|
|
{
|
|
EditingRecord.Images.Clear();
|
|
EditingRecord.Images.AddRange(imageRecords);
|
|
|
|
Images.Clear();
|
|
Images = new(images);
|
|
});
|
|
}
|
|
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);
|
|
}
|
|
|
|
[RelayCommand] private void CancelImageCapturing() => _captureImagesTaskCts?.Cancel();
|
|
[RelayCommand] private void ImagesSectionPrevious() => ImagesSectionCurrentIndex--;
|
|
[RelayCommand] private void ImagesSectionNext() => ImagesSectionCurrentIndex++;
|
|
|
|
private static SeparatorRecord CopyRecord(SeparatorRecord source)
|
|
{
|
|
return new SeparatorRecord
|
|
{
|
|
Id = source.Id,
|
|
Type = source.Type,
|
|
Batch = source.Batch,
|
|
Images = source.Images
|
|
.Select(i => new ImageRecord
|
|
{
|
|
Id = i.Id,
|
|
ImagePath = i.ImagePath,
|
|
VisibleIntensity = i.VisibleIntensity,
|
|
Uv365Intensity = i.Uv365Intensity,
|
|
Uv254Intensity = i.Uv254Intensity
|
|
})
|
|
.ToList()
|
|
};
|
|
}
|
|
} |