forked from amkovkov/GranuSightSoftware2
comprehensive update
1) remove unused components: camera view (due it causes segmentation faults), compute resources and all related, illuminator controller (due in useless without camera view), image storage service, temperature and humidity service 2) database schema - reduce tables references, features storing in records themselves in compressed form, add records creation and editing date and time, add separator comment column 3) analysis - rework of pipeline and ui, now database storing only raw data and all display values calculated from it 4) lights service - add reconnection if disconnected 5) add width and height command line arguments 6) fix some typos and other issues
This commit is contained in:
@@ -35,6 +35,45 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Внутренняя модель записи
|
||||
/// </summary>
|
||||
public partial class RecordViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="SeparatorRecord.Id"/>
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial int Id { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// <see cref="SeparatorRecord.Uuid"/>
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial string Uuid { get; set; } = Guid.NewGuid().ToString();
|
||||
/// <summary>
|
||||
/// <see cref="SampleRecord.CreateDateTime"/>
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial DateTime CreateDateTime { get; set; } = DateTime.UtcNow;
|
||||
/// <summary>
|
||||
/// <see cref="SampleRecord.EditDateTime"/>
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial DateTime EditDateTime { get; set; } = DateTime.UtcNow;
|
||||
/// <summary>
|
||||
/// <see cref="SeparatorRecord.Type"/>
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial string Type { get; set; } = "";
|
||||
/// <summary>
|
||||
/// <see cref="SeparatorRecord.Comment"/>
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial string Comment { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Представление <see cref="SeparatorRecord.ImageId"/>
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial AsyncImageRecordViewModel Image { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Данные <see cref="SeparatorRecord.Features"/>
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial List<float[]> Features { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Логгер
|
||||
/// </summary>
|
||||
@@ -65,15 +104,6 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
/// </summary>
|
||||
private CancellationTokenSource? _captureImagesTaskCts = null;
|
||||
|
||||
/// <summary>
|
||||
/// Данные <see cref="SeparatorRecord.ImageId"/> выбранного элемента
|
||||
/// </summary>
|
||||
private string? _selectedImageData = null;
|
||||
/// <summary>
|
||||
/// Данные <see cref="SeparatorRecord.FeatureIds"/> выбранного элемента
|
||||
/// </summary>
|
||||
private List<float[]> _selectedFeaturesData = new();
|
||||
|
||||
/// <summary>
|
||||
/// Меню открыто
|
||||
/// </summary>
|
||||
@@ -88,21 +118,9 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
[ObservableProperty] public partial int MenuSelectedIndex { get; set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SeparatorRecord.Id"/> выбранного элемента
|
||||
/// Запись
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial int SelectedId { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// <see cref="SeparatorRecord.Uuid"/> выбранного элемента
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial string SelectedUuid { get; set; } = Guid.NewGuid().ToString();
|
||||
/// <summary>
|
||||
/// <see cref="SeparatorRecord.Type"/> выбранного элемента
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial string SelectedType { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Представление <see cref="SeparatorRecord.ImageId"/> выбранного элемента
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial AsyncImageRecordViewModel SelectedImage { get; set; } = new();
|
||||
[ObservableProperty] public partial RecordViewModel Record { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Текущий экран главной секции
|
||||
@@ -112,6 +130,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
/// Можно ли переходить на следующий экран главной секции
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial bool MainSectionCanScrollBackward { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Можно ли переходить на предыдущий экран главной секции
|
||||
/// </summary>
|
||||
@@ -120,6 +139,22 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
/// Происходит ли переход между экранами главной секции
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial bool MainSectionIsScrolling { get; set; } = false;
|
||||
/// <summary>
|
||||
/// Текущий экран информационной секции
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial int InfoSectionCurrentIndex { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// Можно ли переходить на следующий экран информационной секции
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial bool InfoSectionCanScrollBackward { get; set; } = false;
|
||||
/// <summary>
|
||||
/// Можно ли переходить на предыдущий экран информационной секции
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial bool InfoSectionCanScrollForward { get; set; } = false;
|
||||
/// <summary>
|
||||
/// Происходит ли переход между экранами информационной секции
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial bool InfoSectionIsScrolling { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Проводится ли съёмка изображений
|
||||
@@ -131,9 +166,9 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
[ObservableProperty] public partial string ImagesCapturingProgress { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Можно ли производить перемещение между экранами
|
||||
/// Можно ли взаимодействовать с объектом
|
||||
/// </summary>
|
||||
[ObservableProperty] public partial bool CanScroll { get; set; }
|
||||
[ObservableProperty] public partial bool CanInteract { get; set; } = true;
|
||||
|
||||
public SeparatorCalibrationViewModel(
|
||||
ILogger<SeparatorCalibrationViewModel> logger,
|
||||
@@ -162,8 +197,9 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
PropertyChanged += (_, ea) =>
|
||||
{
|
||||
if (ea.PropertyName == nameof(MenuOpened) ||
|
||||
ea.PropertyName == nameof(MainSectionIsScrolling))
|
||||
UpdateCanScroll();
|
||||
ea.PropertyName == nameof(MainSectionIsScrolling) ||
|
||||
ea.PropertyName == nameof(InfoSectionIsScrolling))
|
||||
UpdateCanInteract();
|
||||
};
|
||||
|
||||
_logger.LogInformation("Инициализировано");
|
||||
@@ -187,24 +223,26 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
if (selectedRecord is not null)
|
||||
{
|
||||
// Заполняем простые поля
|
||||
SelectedId = selectedRecord.Id;
|
||||
SelectedUuid = selectedRecord.Uuid;
|
||||
SelectedType = selectedRecord.Type;
|
||||
Record.Id = selectedRecord.Id;
|
||||
Record.Uuid = selectedRecord.Uuid;
|
||||
Record.CreateDateTime = selectedRecord.CreateDateTime;
|
||||
Record.EditDateTime = selectedRecord.EditDateTime;
|
||||
Record.Type = selectedRecord.Type;
|
||||
Record.Comment = selectedRecord.Comment;
|
||||
if (selectedRecord.Features.Count() > 0)
|
||||
Record.Features = selectedRecord.Features.Chunk(AnalyzerService.FEATURES_LENGTH).Select(v => v.ToArray()).ToList();
|
||||
else
|
||||
Record.Features = new List<float[]>();
|
||||
|
||||
// Ищем изображение в базе данных
|
||||
var imageRecord = _context.ImageRecords.Find(selectedRecord.ImageId);
|
||||
// Если изображение не найдено в базе данных, то логгируем как ошибку
|
||||
if (imageRecord is null)
|
||||
_logger.LogError("Не удалось найти элемент с Id({}) в таблице {}", selectedRecord.ImageId, nameof(_context.ImageRecords));
|
||||
// В любом случае заполняем изображение, оно заполнится null если запись не найдена
|
||||
_selectedImageData = imageRecord?.Base64;
|
||||
SelectedImage.Data = _selectedImageData;
|
||||
// В любом случае заполняем изображение
|
||||
Record.Image.Data = imageRecord?.Base64 ?? "";
|
||||
|
||||
// Ищем все признаки в базе данных
|
||||
_selectedFeaturesData = _context.FeatureRecords
|
||||
.Where(r => r.SeparatorRecordId == SelectedId)
|
||||
.Select(r => r.Values.ToArray())
|
||||
.ToList();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -224,20 +262,25 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
|
||||
// Если элемент не выбран, заполняем поля пустыми значениями
|
||||
_logger.LogTrace("Выбран новый элемент");
|
||||
SelectedId = 0;
|
||||
SelectedUuid = Guid.NewGuid().ToString();
|
||||
SelectedType = "";
|
||||
SelectedImage.Data = null;
|
||||
var dateTime = DateTime.UtcNow;
|
||||
Record.Id = 0;
|
||||
Record.Uuid = Guid.NewGuid().ToString();
|
||||
Record.CreateDateTime = dateTime;
|
||||
Record.EditDateTime = dateTime;
|
||||
Record.Type = "";
|
||||
Record.Comment = "";
|
||||
Record.Image.Data = "";
|
||||
Record.Features = new List<float[]>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновить значение <see cref="CanScroll"/>
|
||||
/// Обновить значение <see cref="CanInteract"/>
|
||||
/// </summary>
|
||||
private void UpdateCanScroll()
|
||||
private void UpdateCanInteract()
|
||||
{
|
||||
CanScroll = !MenuOpened;
|
||||
CanScroll &= !MainSectionIsScrolling;
|
||||
CanScroll &= !ImagesIsCapturing;
|
||||
CanInteract = !MenuOpened;
|
||||
CanInteract &= !MainSectionIsScrolling;
|
||||
CanInteract &= !InfoSectionIsScrolling;
|
||||
}
|
||||
|
||||
// Команды управления меню
|
||||
@@ -258,7 +301,15 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
/// Переключить главную секцию информация <-> изображения
|
||||
/// </summary>
|
||||
[RelayCommand] private void SwitchMainSection() => MainSectionCurrentIndex = MainSectionCurrentIndex == 0 ? 1 : 0;
|
||||
|
||||
/// <summary>
|
||||
/// Переключить информационную секцию на следующую
|
||||
/// </summary>
|
||||
[RelayCommand] private void InfoSectionPrevious() => InfoSectionCurrentIndex--;
|
||||
/// <summary>
|
||||
/// Переключить информационную секцию на предыдущую
|
||||
/// </summary>
|
||||
[RelayCommand] private void InfoSectionNext() => InfoSectionCurrentIndex++;
|
||||
|
||||
/// <summary>
|
||||
/// Создать новую запись
|
||||
/// </summary>
|
||||
@@ -285,7 +336,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
// TODO: Добавить определение и отображение количества затрагиваемых, записей которые будут повреждены при удалении этой записи
|
||||
try
|
||||
{
|
||||
_logger.LogTrace("Удаление записи: {}={}, {}={}", nameof(SeparatorRecord.Id), SelectedId, nameof(SeparatorRecord.Type), SelectedType);
|
||||
_logger.LogTrace("Удаление записи: {}={}, {}={}", nameof(SeparatorRecord.Id), Record.Id, nameof(SeparatorRecord.Type), Record.Type);
|
||||
// Если запись не выбрана, то просто создаём новую запись
|
||||
if (MenuSelectedIndex < 0)
|
||||
{
|
||||
@@ -320,7 +371,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
|
||||
// Сохраняем индекс удалённый записи и находим её в базе данных
|
||||
var deletedMenuIndex = MenuSelectedIndex;
|
||||
var deletedRecordId = SelectedId;
|
||||
var deletedRecordId = Record.Id;
|
||||
var deletedRecord = _context.SeparatorRecords.Find(deletedRecordId);
|
||||
// Создаём новую запись
|
||||
CreateRecord();
|
||||
@@ -330,7 +381,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
// Если удалённая запись не найдена в базе данных, то логгируем как ошибку
|
||||
if (deletedRecord is null)
|
||||
{
|
||||
_logger.LogError("Не удалось найти элемент с Id({}) в таблице {}", deletedRecordId, nameof(_context.BrandRecords));
|
||||
_logger.LogError("Не удалось найти элемент с Id({}) в таблице {}", deletedRecordId, nameof(_context.SeparatorRecords));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -372,27 +423,36 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogTrace("Сохранение записи: {}={}, {}={}", nameof(SeparatorRecord.Id), SelectedId, nameof(SeparatorRecord.Type), SelectedType);
|
||||
_logger.LogTrace("Сохранение записи: {}={}, {}={}", nameof(SeparatorRecord.Id), Record.Id, nameof(SeparatorRecord.Type), Record.Type);
|
||||
// Если запись не выбрана
|
||||
if (MenuSelectedIndex < 0)
|
||||
{
|
||||
// Создаём запись изображения
|
||||
_logger.LogTrace("Создаём новую запись изображения");
|
||||
// Заполняем запись изображения
|
||||
var imageRecord = new ImageRecord(base64: _selectedImageData ?? "");
|
||||
// Добавляем запись изображения в базу данных
|
||||
var attachedImageRecord = _context.ImageRecords.Attach(imageRecord);
|
||||
// Сохраняем изменения в базе данных
|
||||
_context.SaveChanges();
|
||||
_logger.LogTrace("Запись изображения сохранена");
|
||||
var imageId = 0;
|
||||
if (!string.IsNullOrEmpty(Record.Image.Data))
|
||||
{
|
||||
_logger.LogTrace("Создаём новую запись изображения");
|
||||
// Заполняем запись изображения
|
||||
var imageRecord = new ImageRecord(base64: Record.Image.Data);
|
||||
// Добавляем запись изображения в базу данных
|
||||
var attachedImageRecord = _context.ImageRecords.Attach(imageRecord);
|
||||
// Сохраняем изменения в базе данных
|
||||
_context.SaveChanges();
|
||||
_logger.LogTrace("Запись изображения сохранена");
|
||||
imageId = attachedImageRecord.Entity.Id;
|
||||
}
|
||||
|
||||
_logger.LogTrace("Создаём новую запись");
|
||||
var dateTime = DateTime.UtcNow;
|
||||
// Заполняем запись
|
||||
var newRecord = new SeparatorRecord(
|
||||
uuid: SelectedUuid,
|
||||
type: SelectedType,
|
||||
imageId: attachedImageRecord.Entity.Id,
|
||||
featureIds: []
|
||||
uuid: Record.Uuid,
|
||||
createDateTime: dateTime,
|
||||
editDateTime: dateTime,
|
||||
type: Record.Type,
|
||||
comment: Record.Comment,
|
||||
imageId: imageId,
|
||||
features: Record.Features
|
||||
);
|
||||
// Добавляем запись в базу данных
|
||||
var attachedRecord = _context.SeparatorRecords.Add(newRecord);
|
||||
@@ -400,29 +460,6 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
_context.SaveChanges();
|
||||
_logger.LogTrace("Запись сохранена");
|
||||
|
||||
_logger.LogTrace("Создаём новые записи признаков");
|
||||
// Заполняем записи признаков и добавляем их в базу данных
|
||||
var attachedFeatureRecords = new List<FeatureRecord>();
|
||||
foreach (var feature in _selectedFeaturesData)
|
||||
{
|
||||
var featureRecord = new FeatureRecord(
|
||||
values: feature,
|
||||
sampleRecordId: attachedRecord.Entity.Id
|
||||
);
|
||||
var attachedFeatureRecord = _context.FeatureRecords.Attach(featureRecord);
|
||||
attachedFeatureRecords.Add(attachedFeatureRecord.Entity);
|
||||
}
|
||||
// Сохраняем изменения в базе данных
|
||||
_context.SaveChanges();
|
||||
_logger.LogTrace("Записи признаков сохранены");
|
||||
|
||||
_logger.LogTrace("Заполняем признаки в сохранённой записи");
|
||||
// Добавляем идентификаторы признаков в запись
|
||||
attachedRecord.Entity.FeatureIds.AddRange(attachedFeatureRecords.Select(r => r.Id));
|
||||
// Сохраняем изменения в базе данных
|
||||
_context.SaveChanges();
|
||||
_logger.LogTrace("Сохранение записи завершено");
|
||||
|
||||
// Добавляем новый элемент в меню
|
||||
MenuItems.Add(new MenuItemViewModel(attachedRecord.Entity.Id, attachedRecord.Entity.Type));
|
||||
// Выбираем последний элемент из меню
|
||||
@@ -432,11 +469,11 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
else
|
||||
{
|
||||
// Находим запись в базе данных
|
||||
var attachedRecord = _context.SeparatorRecords.Find(SelectedId);
|
||||
var attachedRecord = _context.SeparatorRecords.Find(Record.Id);
|
||||
// Если запись не найдена, то логгируем как ошибку
|
||||
if (attachedRecord is null)
|
||||
{
|
||||
_logger.LogError("Не удалось найти элемент с Id({}) в таблице {}", SelectedId, nameof(_context.SeparatorRecords));
|
||||
_logger.LogError("Не удалось найти элемент с Id({}) в таблице {}", Record.Id, nameof(_context.SeparatorRecords));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -449,62 +486,48 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
{
|
||||
// Логгируем как ошибку
|
||||
_logger.LogError("Не удалось найти элемент с Id({}) в таблице {}", attachedRecord.ImageId, nameof(_context.ImageRecords));
|
||||
_logger.LogTrace("Создаём новую запись изображения");
|
||||
// Заполняем запись изображения
|
||||
var imageRecord = new ImageRecord(base64: _selectedImageData ?? "");
|
||||
// Добавляем запись изображения в базу данных
|
||||
attachedImageRecord = _context.ImageRecords.Attach(imageRecord).Entity;
|
||||
// Сохраняем изменения в базе данных
|
||||
_context.SaveChanges();
|
||||
_logger.LogTrace("Запись изображения сохранена");
|
||||
}
|
||||
|
||||
// Ищем записи признаков в базе данных
|
||||
var attachedFeatureRecords = _context.FeatureRecords.Where(r => r.SeparatorRecordId == SelectedId);
|
||||
// Если количество признаков в базе данных больше чем текущее
|
||||
if (attachedFeatureRecords.Count() > _selectedFeaturesData.Count())
|
||||
{
|
||||
// Находим количество признаков которое нужно удалить
|
||||
var featuresRecordsToRemove = attachedFeatureRecords.Count() - _selectedFeaturesData.Count();
|
||||
// Логгируем
|
||||
_logger.LogTrace("Количество записей признаков в базе данных больше чем текущее, удаление лишних записей: {} ({} - {})", featuresRecordsToRemove, attachedFeatureRecords.Count(), _selectedFeaturesData.Count());
|
||||
// Удаляем необходимое количество признаков из конца
|
||||
attachedFeatureRecords.TakeLast(featuresRecordsToRemove);
|
||||
}
|
||||
|
||||
// Заполняем все имеющиеся признаки в базе данных новыми значениями
|
||||
foreach (var (i, attachedFeatureRecord) in attachedFeatureRecords.Enumerate())
|
||||
attachedFeatureRecord.Values = _selectedFeaturesData[i];
|
||||
|
||||
// Если количество признаков в безе данных меньше чем текущее
|
||||
if (attachedFeatureRecords.Count() < _selectedFeaturesData.Count())
|
||||
{
|
||||
// Находим количество признаков которое нужно добавить
|
||||
var featuresRecordsToAdd = _selectedFeaturesData.Count() - attachedFeatureRecords.Count();
|
||||
// Логгируем
|
||||
_logger.LogTrace("Количество записей признаков в базе данных меньше чем текущее, добавление недостающих записей: {} ({} - {})", featuresRecordsToAdd, _selectedFeaturesData.Count(), attachedFeatureRecords.Count());
|
||||
// Заполняем записи недостающих признаков и добавляем их в базу данных
|
||||
for (var i = attachedFeatureRecords.Count(); i < _selectedFeaturesData.Count(); i++)
|
||||
// Если изображение есть
|
||||
if (!string.IsNullOrEmpty(Record.Image.Data))
|
||||
{
|
||||
var featureRecord = new FeatureRecord(
|
||||
values: _selectedFeaturesData[i],
|
||||
sampleRecordId: attachedRecord.Id
|
||||
);
|
||||
var attachedFeatureRecord = _context.FeatureRecords.Attach(featureRecord);
|
||||
attachedFeatureRecords.Append(attachedFeatureRecord.Entity);
|
||||
_logger.LogTrace("Создаём новую запись изображения");
|
||||
// Заполняем запись изображения
|
||||
var imageRecord = new ImageRecord(base64: Record.Image.Data);
|
||||
// Добавляем запись изображения в базу данных
|
||||
attachedImageRecord = _context.ImageRecords.Attach(imageRecord).Entity;
|
||||
// Сохраняем изменения в базе данных
|
||||
_context.SaveChanges();
|
||||
_logger.LogTrace("Запись изображения сохранена");
|
||||
attachedRecord.ImageId = attachedImageRecord.Id;
|
||||
}
|
||||
}
|
||||
// Если запись найдена
|
||||
else
|
||||
{
|
||||
// Если изображение есть
|
||||
if (!string.IsNullOrEmpty(Record.Image.Data))
|
||||
{
|
||||
_logger.LogTrace("Записываем изменения в запись изображения");
|
||||
// Вносим изменения
|
||||
attachedImageRecord.Base64 = Record.Image.Data;
|
||||
// Сохраняем изменения в базе данных
|
||||
_context.SaveChanges();
|
||||
}
|
||||
// Если изображения нет
|
||||
else
|
||||
{
|
||||
// Удаляем изображение из базы данных
|
||||
_context.ImageRecords.Remove(attachedImageRecord);
|
||||
// Сохраняем изменения в базе данных
|
||||
_context.SaveChanges();
|
||||
attachedRecord.ImageId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
_context.SaveChanges();
|
||||
_logger.LogTrace("Записи признаков сохранена");
|
||||
|
||||
// Если в данных есть изменения, то записываем их
|
||||
if (attachedRecord.Type != SelectedType)
|
||||
attachedRecord.Type = SelectedType;
|
||||
if (attachedRecord.ImageId != attachedImageRecord.Id)
|
||||
attachedRecord.ImageId = attachedImageRecord.Id;
|
||||
// Для признаков особый случай, они в любом случае будут перезаписаны, потому что так быстрее
|
||||
attachedRecord.FeatureIds = attachedFeatureRecords.Select(r => r.Id).ToList();
|
||||
// Записываем изменения
|
||||
attachedRecord.EditDateTime = DateTime.UtcNow;
|
||||
attachedRecord.Type = Record.Type;
|
||||
attachedRecord.Comment = Record.Comment;
|
||||
attachedRecord.Features = Record.Features.SelectMany(fs => fs).ToList();
|
||||
|
||||
// Сохраняем изменения в базе данных
|
||||
_context.SaveChanges();
|
||||
@@ -512,7 +535,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
|
||||
// Записываем изменения в меню
|
||||
_logger.LogTrace("Записываем изменения в меню");
|
||||
MenuItems[MenuSelectedIndex].Type = SelectedType;
|
||||
MenuItems[MenuSelectedIndex].Type = Record.Type;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -538,10 +561,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
_captureImagesTaskCts = new CancellationTokenSource();
|
||||
var cancellationToken = _captureImagesTaskCts.Token;
|
||||
|
||||
// Обновляем данные в интерфейсе
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что этот метод может (и скорее всего будет) вызван в другом потоке,
|
||||
// а изменения данных в интерфейсе могут быть вызваны только из потока в котором интерфейс был создан
|
||||
_ = Dispatcher.UIThread.InvokeAsync(() => ImagesCapturingProgress = $"Получение изображений: 0 / {ImageCapturerService.IlluminatorIntensities.Count()}");
|
||||
ImagesCapturingProgress = $"Получение изображений: 0 / {ImageCapturerService.IlluminatorIntensities.Count()}";
|
||||
|
||||
// Запускаем получение изображений асинхронно
|
||||
_сaptureImagesTask = Task.Run(async () =>
|
||||
@@ -550,7 +570,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
var imagesData = new List<Task<ImageData>>();
|
||||
|
||||
// Обновляем данные в интерфейсе
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что этот метод может (и скорее всего будет) вызван в другом потоке,
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что неизвестно как будет выполнятся эта задача,
|
||||
// а изменения данных в интерфейсе могут быть вызваны только из потока в котором интерфейс был создан
|
||||
_ = Dispatcher.UIThread.InvokeAsync(() => ImagesIsCapturing = true);
|
||||
|
||||
@@ -563,7 +583,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Обновляем данные в интерфейсе
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что этот метод может (и скорее всего будет) вызван в другом потоке,
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что неизвестно как будет выполнятся эта задача,
|
||||
// а изменения данных в интерфейсе могут быть вызваны только из потока в котором интерфейс был создан
|
||||
_ = Dispatcher.UIThread.InvokeAsync(() => ImagesCapturingProgress = $"Получение изображений: {i + 1} / {ImageCapturerService.IlluminatorIntensities.Count()}");
|
||||
|
||||
@@ -595,7 +615,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// Обновляем данные в интерфейсе
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что этот метод может (и скорее всего будет) вызван в другом потоке,
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что неизвестно как будет выполнятся эта задача,
|
||||
// а изменения данных в интерфейсе могут быть вызваны только из потока в котором интерфейс был создан
|
||||
_ = Dispatcher.UIThread.InvokeAsync(() => ImagesCapturingProgress = $"Обработка изображений");
|
||||
|
||||
@@ -648,7 +668,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
var (avgImage, roi, features) = result.Value;
|
||||
|
||||
// Сохраняем вектор признаков
|
||||
_selectedFeaturesData = features;
|
||||
Record.Features = features;
|
||||
|
||||
// Обрабатываем среднее изображение
|
||||
avgImage = new OpenCvSharp.Mat(avgImage, roi);
|
||||
@@ -663,15 +683,14 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
|
||||
// Кодируем среднее изображение
|
||||
OpenCvSharp.Cv2.ImEncode(".png", avgImage, out var avgData);
|
||||
_selectedImageData = Convert.ToBase64String(avgData);
|
||||
|
||||
// Очищаем память среднего изображения
|
||||
avgImage.Dispose();
|
||||
|
||||
|
||||
// Обновляем данные в интерфейсе
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что этот метод может (и скорее всего будет) вызван в другом потоке,
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что неизвестно как будет выполнятся эта задача,
|
||||
// а изменения данных в интерфейсе могут быть вызваны только из потока в котором интерфейс был создан
|
||||
_ = Dispatcher.UIThread.InvokeAsync(() => SelectedImage.Data = _selectedImageData);
|
||||
_ = Dispatcher.UIThread.InvokeAsync(() => Record.Image.Data = Convert.ToBase64String(avgData));
|
||||
}, cancellationToken);
|
||||
|
||||
// Создаём задачу которая выполнится после завершения всех других
|
||||
@@ -724,7 +743,7 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
}
|
||||
|
||||
// Обновляем данные в интерфейсе
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что этот метод может (и скорее всего будет) вызван в другом потоке,
|
||||
// Dispatcher.UIThread.Invoke нужен потому-что неизвестно как будет выполнятся эта задача,
|
||||
// а изменения данных в интерфейсе могут быть вызваны только из потока в котором интерфейс был создан
|
||||
_ = Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
@@ -737,6 +756,8 @@ public partial class SeparatorCalibrationViewModel : ViewModelBase
|
||||
});
|
||||
}
|
||||
|
||||
// Команда отмены задачи съёмки изображений
|
||||
/// <summary>
|
||||
/// Отменить задачу съёмки изображения
|
||||
/// </summary>
|
||||
[RelayCommand] private void CancelImageCapturing() => _captureImagesTaskCts?.Cancel();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user