398 lines
17 KiB
C#
398 lines
17 KiB
C#
/*
|
|
* Copyright (c) 2025 LG Electronics Inc.
|
|
* SPDX-License-Identifier: LicenseRef-LGE-Proprietary
|
|
*/
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Aurora;
|
|
using System.Reactive.Linq;
|
|
using System.Windows;
|
|
using Aurora.Api;
|
|
using System.Collections.ObjectModel;
|
|
using System.Windows.Input;
|
|
using Aurora.View;
|
|
using VisionCommon;
|
|
using VisionSequence.Setup;
|
|
using VisionCommonUI;
|
|
using VisionCommonUI.Resources;
|
|
using VisionSequence.Aligner;
|
|
using System.Reactive.Disposables;
|
|
|
|
namespace VisionUI.Setup {
|
|
/// <summary>
|
|
/// 비전 반복 View Model
|
|
/// </summary>
|
|
public class VisionRepeatViewModel : Aurora.View.NotifyPropertyChangedBase, IUpdateRecipe {
|
|
private Logger _Logger;
|
|
private ISequence _Seq;
|
|
|
|
private FovInfo _FovInfo;
|
|
private FovRecipe _FovRecipe;
|
|
private CsvManager _CsvManager = new CsvManager();
|
|
|
|
private VisionRepeatRecipe _VisionRepeatRecipe;
|
|
/// <summary>
|
|
/// 비전 반복도 측정 레시피
|
|
/// </summary>
|
|
public VisionRepeatRecipe VisionRepeatRecipe { get => _VisionRepeatRecipe; set => SetProperty(ref _VisionRepeatRecipe, value); }
|
|
|
|
private int _count;
|
|
/// <summary>반복 횟수</summary>
|
|
public int RepeatCount { get => _count; set => SetProperty(ref _count, value); }
|
|
private bool _enalbed;
|
|
/// <summary>활성화</summary>
|
|
public bool Enabled { get => _enalbed; set => SetProperty(ref _enalbed, value); }
|
|
/// <summary>비전 반복도 로그 아이템 리스트</summary>
|
|
public ObservableCollection<VisionRepeatLog> VisionRepeatLogItems { get; set; } = new ObservableCollection<VisionRepeatLog>();
|
|
|
|
private bool _isShapeModelFinder;
|
|
/// <summary>패턴 종류가 Shape인 여부</summary>
|
|
public bool IsShapeModelFinder {
|
|
get => _isShapeModelFinder;
|
|
set {
|
|
SetProperty(ref _isShapeModelFinder, value);
|
|
NotifyPropertyChanged(nameof(IsShapeModelFinder));
|
|
}
|
|
}
|
|
|
|
private DateTime selectedDate;
|
|
/// <summary>선택한 날짜</summary>
|
|
public DateTime SelectedDate { get => selectedDate; set { SetProperty(ref selectedDate, value); CalendarChanged(value); } }
|
|
|
|
private PatternInfo _selectedPatternInfo;
|
|
public PatternInfo SelectedPatternInfo { get => _selectedPatternInfo; set => SetProperty(ref _selectedPatternInfo, value); }
|
|
|
|
#region Image Pos Binding Properties
|
|
private double _meanX_I;
|
|
/// <summary>평균 X (이미지 좌표)</summary>
|
|
public double MeanX_I { get => _meanX_I; set => SetProperty(ref _meanX_I, value); }
|
|
private double _meanY_I;
|
|
/// <summary>평균 Y (이미지 좌표)</summary>
|
|
public double MeanY_I { get => _meanY_I; set => SetProperty(ref _meanY_I, value); }
|
|
private double _sdX_I;
|
|
/// <summary>SdX (이미지 좌표)</summary>
|
|
public double SdX_I { get => _sdX_I; set => SetProperty(ref _sdX_I, value); }
|
|
private double _sdY_I;
|
|
/// <summary>SdY (이미지 좌표)</summary>
|
|
public double SdY_I { get => _sdY_I; set => SetProperty(ref _sdY_I, value); }
|
|
private double _minX_I;
|
|
/// <summary>최소값 X (이미지 좌표)</summary>
|
|
public double MinX_I { get => _minX_I; set => SetProperty(ref _minX_I, value); }
|
|
private double _minY_I;
|
|
/// <summary>최소값 Y (이미지 좌표)</summary>
|
|
public double MinY_I { get => _minY_I; set => SetProperty(ref _minY_I, value); }
|
|
private double _maxX_I;
|
|
/// <summary>최댓값 X (이미지 좌표)</summary>
|
|
public double MaxX_I { get => _maxX_I; set => SetProperty(ref _maxX_I, value); }
|
|
private double _maxY_I;
|
|
/// <summary>최댓값 Y (이미지 좌표)</summary>
|
|
public double MaxY_I { get => _maxY_I; set => SetProperty(ref _maxY_I, value); }
|
|
private double _rangeX_I;
|
|
/// <summary>범위 X (이미지 좌표)</summary>
|
|
public double RangeX_I { get => _rangeX_I; set => SetProperty(ref _rangeX_I, value); }
|
|
private double _rangeY_I;
|
|
/// <summary>범위 Y (이미지 좌표)</summary>
|
|
public double RangeY_I { get => _rangeY_I; set => SetProperty(ref _rangeY_I, value); }
|
|
#endregion
|
|
|
|
#region Motion Pos Binding Properties
|
|
/// <summary>반복도 로그 아이템리스트 (모터 좌표)</summary>
|
|
private double _meanX_S;
|
|
/// <summary>평균 X (모터 좌표)</summary>
|
|
public double MeanX_S { get => _meanX_S; set => SetProperty(ref _meanX_S, value); }
|
|
private double _meanY_S;
|
|
/// <summary>평균 Y (모터 좌표)</summary>
|
|
public double MeanY_S { get => _meanY_S; set => SetProperty(ref _meanY_S, value); }
|
|
private double _sdX_S;
|
|
/// <summary>SdX (모터 좌표)</summary>
|
|
public double SdX_S { get => _sdX_S; set => SetProperty(ref _sdX_S, value); }
|
|
private double _sdY_S;
|
|
/// <summary>SdY (모터 좌표)</summary>
|
|
public double SdY_S { get => _sdY_S; set => SetProperty(ref _sdY_S, value); }
|
|
private double _minX_S;
|
|
/// <summary>최소값 X (모터 좌표)</summary>
|
|
public double MinX_S { get => _minX_S; set => SetProperty(ref _minX_S, value); }
|
|
private double _minY_S;
|
|
/// <summary>최소값 Y (모터 좌표)</summary>
|
|
public double MinY_S { get => _minY_S; set => SetProperty(ref _minY_S, value); }
|
|
private double _maxX_S;
|
|
/// <summary>최댓값 X (모터 좌표)</summary>
|
|
public double MaxX_S { get => _maxX_S; set => SetProperty(ref _maxX_S, value); }
|
|
private double _maxY_S;
|
|
/// <summary>최댓값 Y (모터 좌표)</summary>
|
|
public double MaxY_S { get => _maxY_S; set => SetProperty(ref _maxY_S, value); }
|
|
private double _rangeX_S;
|
|
/// <summary>범위 X (모터 좌표)</summary>
|
|
public double RangeX_S { get => _rangeX_S; set => SetProperty(ref _rangeX_S, value); }
|
|
private double _rangeY_S;
|
|
/// <summary>범위 Y (모터 좌표)</summary>
|
|
public double RangeY_S { get => _rangeY_S; set => SetProperty(ref _rangeY_S, value); }
|
|
#endregion
|
|
|
|
#region Button Commands
|
|
public ICommand ButtonCommandStart { get; set; }
|
|
public ICommand ButtonCommandStartDynamic { get; set; }
|
|
public ICommand ButtonCommandCancel { get; set; }
|
|
public ICommand ButtonCommandSave { get; set; }
|
|
public ICommand ButtonCommandLog { get; set; }
|
|
#endregion
|
|
/// <summary>
|
|
/// 생성자
|
|
/// </summary>
|
|
/// <param name="fovInfo">Fov 정보</param>
|
|
public VisionRepeatViewModel(FovInfo fovInfo) {
|
|
_FovInfo = fovInfo;
|
|
_FovRecipe = VisionRecipeManager.GetFovRecipe(fovInfo);
|
|
_Logger = new Logger(string.Format("VisionRepeatViewModel[{0}]", fovInfo.SubSysName));
|
|
|
|
ButtonCommandStart = new DelegateCommand(new Action<object>(DoStart));
|
|
ButtonCommandStartDynamic = new DelegateCommand(new Action<object>(DoStartDynamic));
|
|
ButtonCommandCancel = new DelegateCommand(new Action<object>(DoCancel));
|
|
ButtonCommandSave = new DelegateCommand(new Action<object>(DoSave));
|
|
|
|
SelectedDate = DateTime.Now.Date;
|
|
|
|
UpdateRecipeFromFile();
|
|
SetObservable();
|
|
}
|
|
|
|
private void SetObservable() {
|
|
var disposable = VisionEnv.AuroraEnv.EventObservable.AsObservable(VisionEventId.UPDATE_VISION_REPEAT_LOG).
|
|
Where(x => x.From == _FovInfo.GetKey()).Subscribe(param => UpdateVisionRepeatLog(param));
|
|
DisposableManager.AddDisposable(eDpsGroupKey.SubMenu_VisionRepeat, disposable);
|
|
}
|
|
|
|
private void UpdateVisionRepeatLog(EventMessage msg) {
|
|
var visionRepeatLog = msg.Param as VisionRepeatLog;
|
|
|
|
Application.Current.Dispatcher.BeginInvoke(
|
|
new Action(() => {
|
|
VisionRepeatLogItems.Add(visionRepeatLog);
|
|
}));
|
|
}
|
|
/// <summary>
|
|
/// 비전 반복도 시퀀스 시작
|
|
/// </summary>
|
|
/// <param name="display"></param>
|
|
public void DoStart(object display = null) {
|
|
if (RepeatCount > 0 && RepeatCount <= 100) {
|
|
if (MessageBoxUtil.ShowMessageBoxQuestion(
|
|
string.Format(StrTokens.MSG_Q_ConfirmRunVisionRepeat)) == MessageBoxResult.Yes) {
|
|
VisionRepeatLogItems.Clear();
|
|
_FovRecipe = VisionRecipeManager.GetFovRecipe(_FovInfo);
|
|
_Seq = Application.Current.AuroraEnv().SequenceEntry[VisionKeys.VisionRepeatSeqKey];
|
|
_Seq?.Start(new SetupSeqParams_VisionRepeat(_FovInfo, SelectedPatternInfo, RepeatCount), OnCompleted);
|
|
}
|
|
}
|
|
else {
|
|
MessageBoxUtil.ShowMessageBoxError(
|
|
string.Format(StrTokens.MSG_E_NumberInvalid, "Step Count", 0, 100));
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 다이나믹 반복 측정
|
|
/// </summary>
|
|
/// <param name="display"></param>
|
|
public void DoStartDynamic(object display = null) {
|
|
if (RepeatCount > 0 && RepeatCount <= 100) {
|
|
if (MessageBoxUtil.ShowMessageBoxQuestion(
|
|
string.Format(StrTokens.MSG_Q_ConfirmRunVisionRepeat)) == MessageBoxResult.Yes) {
|
|
VisionRepeatLogItems.Clear();
|
|
_FovRecipe = VisionRecipeManager.GetFovRecipe(_FovInfo);
|
|
_Seq = Application.Current.AuroraEnv().SequenceEntry[VisionKeys.VisionRepeatDynamicSeqKey];
|
|
_Seq?.Start(new SetupSeqParams_DynamicRepeat(_FovInfo, SelectedPatternInfo, motionRepeatCount: RepeatCount), OnCompletedDynamic);
|
|
}
|
|
}
|
|
else {
|
|
MessageBoxUtil.ShowMessageBoxError(
|
|
string.Format(StrTokens.MSG_E_NumberInvalid, "Step Count", 0, 100));
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 시퀀스 정지
|
|
/// </summary>
|
|
/// <param name="display">null</param>
|
|
public void DoCancel(object display = null) {
|
|
_Seq?.Stop();
|
|
}
|
|
|
|
/// <summary>
|
|
/// UI로부터 레시피 저장
|
|
/// </summary>
|
|
/// <param name="o">null</param>
|
|
public void DoSave(object o = null) {
|
|
UpdateRecipeFromUI();
|
|
RecipeFileManager.SaveVisionRecipe();
|
|
|
|
VisionEnv.AuroraEnv.EventPublisher.Publish(new EventMessage(
|
|
_FovRecipe.FovName, VisionEventId.NOTIFY_VISION_REPEAT_SAVE_PARAMS, VisionRepeatRecipe));
|
|
|
|
}
|
|
|
|
private void OnCompleted(SequenceActionResult SeqResult) {
|
|
if (SeqResult == null) {
|
|
_Logger.FuncE("SeqResult is null.");
|
|
return;
|
|
}
|
|
|
|
Application.Current.Dispatcher.Invoke(() => {
|
|
PrintVisionRepeatResult_I(SeqResult);
|
|
PrintVisionRepeatResult_S(SeqResult);
|
|
});
|
|
}
|
|
|
|
private void OnCompletedDynamic(SequenceActionResult SeqResult) {
|
|
if (SeqResult == null) {
|
|
_Logger.FuncE("SeqResult is null.");
|
|
return;
|
|
}
|
|
Application.Current.Dispatcher.Invoke(() => {
|
|
PrintVisionRepeatResult_I(SeqResult);
|
|
PrintVisionRepeatResult_S(SeqResult);
|
|
});
|
|
}
|
|
|
|
|
|
private void PrintVisionRepeatResult_I(SequenceActionResult SeqResult) {
|
|
var visionRepeatSeqResult = SeqResult.Param as SetupSeqResultParams_VisionRepeat;
|
|
if (visionRepeatSeqResult.ImagePoints.Count == 0) {
|
|
return;
|
|
}
|
|
|
|
var imagePoints = visionRepeatSeqResult.ImagePoints.Select(item => item.Clone());
|
|
int i = 1;
|
|
MeanX_I = MeanY_I = SdX_I = SdY_I = MinX_I = MinY_I = MaxX_I = MaxY_I = RangeX_I = RangeY_I = 0.0;
|
|
|
|
foreach (var imagePoint in imagePoints) {
|
|
MeanX_I += imagePoint.X;
|
|
MeanY_I += imagePoint.Y;
|
|
if (i == 1) {
|
|
MinX_I = imagePoint.X;
|
|
MinY_I = imagePoint.Y;
|
|
MaxX_I = imagePoint.X;
|
|
MaxY_I = imagePoint.Y;
|
|
}
|
|
else {
|
|
if (MinX_I > imagePoint.X) MinX_I = imagePoint.X;
|
|
if (MinY_I > imagePoint.Y) MinY_I = imagePoint.Y;
|
|
if (MaxX_I < imagePoint.X) MaxX_I = imagePoint.X;
|
|
if (MaxY_I < imagePoint.Y) MaxY_I = imagePoint.Y;
|
|
}
|
|
i++;
|
|
}
|
|
MeanX_I /= imagePoints.Count();
|
|
MeanY_I /= imagePoints.Count();
|
|
|
|
foreach (var item in imagePoints)
|
|
{
|
|
#warning SdX_S 계산식 확인 할 것
|
|
SdX_I = (item.X - MeanX_I) * (item.X - MeanX_I);
|
|
SdY_I = (item.Y - MeanY_I) * (item.Y - MeanY_I);
|
|
}
|
|
if (imagePoints.Count() > 1) {
|
|
SdX_I = Math.Sqrt(SdX_I) / (imagePoints.Count() - 1);
|
|
SdY_I = Math.Sqrt(SdY_I) / (imagePoints.Count() - 1);
|
|
|
|
RangeX_I = MaxX_I - MinX_I;
|
|
RangeY_I = MaxY_I - MinY_I;
|
|
}
|
|
}
|
|
|
|
private void PrintVisionRepeatResult_S(SequenceActionResult SeqResult) {
|
|
var visionRepeatSeqResult = SeqResult.Param as SetupSeqResultParams_VisionRepeat;
|
|
if (visionRepeatSeqResult.StaticPoints.Count == 0) {
|
|
return;
|
|
}
|
|
|
|
var staticPoints = visionRepeatSeqResult.StaticPoints.Select(item => item.Clone());
|
|
int i = 1;
|
|
MeanX_S = MeanY_S = SdX_S = SdY_S = MinX_S = MinY_S = MaxX_S = MaxY_S = RangeX_S = RangeY_S = 0.0;
|
|
|
|
foreach (var staticPoint in staticPoints) {
|
|
MeanX_S += staticPoint.X;
|
|
MeanY_S += staticPoint.Y;
|
|
if (i == 1) {
|
|
MinX_S = staticPoint.X;
|
|
MinY_S = staticPoint.Y;
|
|
MaxX_S = staticPoint.X;
|
|
MaxY_S = staticPoint.Y;
|
|
}
|
|
else {
|
|
if (MinX_S > staticPoint.X) MinX_S = staticPoint.X;
|
|
if (MinY_S > staticPoint.Y) MinY_S = staticPoint.Y;
|
|
if (MaxX_S < staticPoint.X) MaxX_S = staticPoint.X;
|
|
if (MaxY_S < staticPoint.Y) MaxY_S = staticPoint.Y;
|
|
}
|
|
i++;
|
|
}
|
|
MeanX_S /= staticPoints.Count();
|
|
MeanY_S /= staticPoints.Count();
|
|
#warning SdX_S 계산식 확인 할 것
|
|
foreach (var item in staticPoints) {
|
|
SdX_S = (item.X - MeanX_S) * (item.X - MeanX_S);
|
|
SdY_S = (item.Y - MeanY_S) * (item.Y - MeanY_S);
|
|
}
|
|
if (staticPoints.Count() > 1) {
|
|
SdX_S = Math.Sqrt(SdX_S) / (staticPoints.Count() - 1);
|
|
SdY_S = Math.Sqrt(SdY_S) / (staticPoints.Count() - 1);
|
|
|
|
RangeX_S = MaxX_S - MinX_S;
|
|
RangeY_S = MaxY_S - MinY_S;
|
|
}
|
|
}
|
|
|
|
public void PatternChanged() {
|
|
|
|
}
|
|
/// <summary>
|
|
/// 비전 레시피 Refresh
|
|
/// </summary>
|
|
public void RefreshVisionRecipes() {
|
|
_FovRecipe = VisionRecipeManager.GetFovRecipe(_FovInfo);
|
|
}
|
|
/// <summary>
|
|
/// 파일로부터 레시피 불러오기
|
|
/// </summary>
|
|
public void UpdateRecipeFromFile() {
|
|
var visionRepeatRecipe = VisionRecipeManager.GetVisionRepeatRecipe(_FovInfo);
|
|
VisionRepeatRecipe = visionRepeatRecipe.Clone();
|
|
RepeatCount = VisionRepeatRecipe.RepeatCount;
|
|
}
|
|
/// <summary>
|
|
/// UI로부터 레시피 불러오기
|
|
/// </summary>
|
|
public void UpdateRecipeFromUI() {
|
|
var visionRepeatRecipe = VisionRecipeManager.GetVisionRepeatRecipe(_FovInfo);
|
|
VisionRepeatRecipe.RepeatCount = RepeatCount;
|
|
VisionRepeatRecipe.CopyTo(visionRepeatRecipe);
|
|
}
|
|
/// <summary>
|
|
/// 현재 시간 기준으로 CSV 불러오기
|
|
/// </summary>
|
|
public void LoadCsvNow() {
|
|
DateTime dateTime = DateTime.Now;
|
|
LoadCsv(dateTime);
|
|
}
|
|
|
|
private void CalendarChanged(DateTime dateTime) {
|
|
LoadCsv(dateTime);
|
|
}
|
|
|
|
private void LoadCsv(DateTime dateTime) {
|
|
try {
|
|
VisionRepeatLogItems?.Clear();
|
|
List<VisionRepeatLog> items =
|
|
_CsvManager.LoadCsv<VisionRepeatLog>(_FovInfo.ToSubSysInfo(), dateTime, eSaveDataType.VisionRepeat);
|
|
if (items != null) { // 로드할 파일이 없으면 items가 null임.
|
|
foreach (VisionRepeatLog item in items) {
|
|
VisionRepeatLogItems.Add(item);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
_Logger.FuncE(e.Message);
|
|
}
|
|
}
|
|
}
|
|
} |