2026-03-30 17:09:29 +09:00

392 lines
17 KiB
C#

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;
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() {
VisionEnv.AuroraEnv.EventObservable.AsObservable(VisionEventId.UPDATE_VISION_REPEAT_LOG).
Where(x => x.From == _FovInfo.GetKey()).Subscribe(param => UpdateVisionRepeatLog(param));
}
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.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);
}
}
}
}