using Aurora; using Aurora.Api; using Aurora.Api.Manager; using Aurora.View; using AuroraLaser; using AuroraLaserScanner; using CoreUnit.Control.UserControl.Canvas; using CoreUnit.Scanner.UserControl.ScannerCanvas; using HarfBuzzSharp; using Microsoft.Win32; using MilUI; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Configuration; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Reactive.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; using System.Windows.Threading; using VisionCommon; using WeldingCommon.Models; using WeldingCommon.Sequences; using WeldingCommon.Settings; using WeldingCommon.Utils; using static CoreUnit.Control.UserControl.Canvas.WeldingParameterIDEditor; namespace WeldingCommon.Views { public class UCScannerCalibrationViewModel : NotifyPropertyChangedBase { private readonly Logger Log = new Logger("UCScannerCalibrationViewModel"); public ICommand CMD_VisibleChanged { get; } public ICommand CMD_ApplyGridItems { get; } public ICommand CMD_SaveDataFile { get; } public ICommand CMD_RunCorreXionPro { get; } public ICommand CMD_RunMarkGrid { get; } public ICommand CMD_StopMarkGrid { get; } public ICommand CMD_RunScannerCalibration { get; } public ICommand CMD_StopScannerCalibration { get; } //public ICommand CMD_ChangedCaltype { get; } //public ICommand CMD_SeqStop { get; } private readonly IAuroraEnvironment _Aurora; private readonly Scanner _Scanner; private readonly Laser _Laser; private readonly LaserConfig _LaserConfig; private readonly ScannerConfig _ScannerConfig; //private ISequence _SeqMarkGrid; private ISequence _SeqTeachingRun; private bool _IsSequenceRunning_MarkGrid; public bool IsSequenceRunning_MarkGrid { get => _IsSequenceRunning_MarkGrid; set => SetProperty(ref _IsSequenceRunning_MarkGrid, value); } private ISequence _SeqScannerCalibration; private bool _IsSequenceRunning_ScannerCalibration; public bool IsSequenceRunning_ScannerCalibration { get => _IsSequenceRunning_ScannerCalibration; set => SetProperty(ref _IsSequenceRunning_ScannerCalibration, value); } // Support VisionCorrectionList Selection. private ObservableCollection _SelectedVisionCorrectionList; public ObservableCollection SelectedVisionCorrectionList { get => _SelectedVisionCorrectionList; set => SetProperty(ref _SelectedVisionCorrectionList, value); } private ObservableCollection _VisionCorrectionList; public ObservableCollection VisionCorrectionList { get => _VisionCorrectionList; set => SetProperty(ref _VisionCorrectionList, value); } // Correction Param private int _GridRowCount; public int GridRowCount { get => _GridRowCount; set => SetProperty(ref _GridRowCount, value); } private int _GridColumnCount; public int GridColumnCount { get => _GridColumnCount; set => SetProperty(ref _GridColumnCount, value); } private float _UnitLength; public float UnitLength { get => _UnitLength; set => SetProperty(ref _UnitLength, value); } private float _TiltingDegree; public float TiltingDegree { get => _TiltingDegree; set => SetProperty(ref _TiltingDegree, value); } private double _CurrentX; public double CurrentX { get => _CurrentX; set => SetProperty(ref _CurrentX, value); } private double _CurrentY; public double CurrentY { get => _CurrentY; set => SetProperty(ref _CurrentY, value); } // Vision Param private double _MPosOffsetX; public double MPosOffsetX { get => _MPosOffsetX; set => SetProperty(ref _MPosOffsetX, value); } private double _MPosOffsetY; public double MPosOffsetY { get => _MPosOffsetY; set => SetProperty(ref _MPosOffsetY, value); } private double _OffsetThreshold; public double OffsetThreshold { get => _OffsetThreshold; set => SetProperty(ref _OffsetThreshold, value); } // Vision Param - Calibration Type private bool _IsVisionCalibration; public bool IsVisionCalibration { get => _IsVisionCalibration; set => SetProperty(ref _IsVisionCalibration, value); } // Laser Param private double _MarkPower; public double MarkPower { get => _MarkPower; set => SetProperty(ref _MarkPower, value); } private double _MarkSpeed; public double MarkSpeed { get => _MarkSpeed; set => SetProperty(ref _MarkSpeed, value); } private double _MarkLength; public double MarkLength { get => _MarkLength; set => SetProperty(ref _MarkLength, value); } private double _MarkFreq = 1000; public double MarkFreq { get => _MarkFreq; set => SetProperty(ref _MarkFreq, value); } private double _MarkPulseWidth = 1000000; public double MarkPulseWidth { get => _MarkPulseWidth; set => SetProperty(ref _MarkPulseWidth, value); } private bool _LaserStatus; public bool LaserStatus { get => _LaserStatus; set => SetProperty(ref _LaserStatus, value); } // Scanner Move private int _SelectedIndex; public int SelectedIndex { get => _SelectedIndex; set => SetProperty(ref _SelectedIndex, value); } private bool _Is1StepCalibration; public bool Is1StepCalibration { get => _Is1StepCalibration; set => SetProperty(ref _Is1StepCalibration, value); } private FovInfo _FovInfo; private CameraDisplay _CameraDisplay; private readonly DispatcherTimer _MonitorTimer; //private string _CalibrationType; public UCScannerCalibrationViewModel(IAuroraEnvironment aurora) { _Aurora = aurora; _Scanner = _Aurora.DeviceManager[CommonDeviceIds.Scanner] as Scanner; _Laser = _Aurora.DeviceManager[CommonDeviceIds.Laser] as Laser; _LaserConfig = _Aurora.DeviceManager.GetDeviceConfig(_Laser.DeviceId) as LaserConfig; _ScannerConfig = _Aurora.DeviceManager.GetDeviceConfig(_Scanner.DeviceId) as ScannerConfig; //_SeqMarkGrid = _Aurora.SequenceEntry[CommonSequenceIds.SeqMarkGrid] as ISequence; _SeqScannerCalibration = _Aurora.SequenceEntry[CommonSequenceIds.SeqScannerCalibration] as ISequence; _FovInfo = VisionRecipeManager.GetFovInfos().FirstOrDefault(); _CameraDisplay = CameraDisplayManager.GetDisplay(_FovInfo, eDisplayType.Calibration) as CameraDisplay; _SeqTeachingRun = _Aurora.SequenceEntry[CommonSequenceIds.SeqComponentTeachingRun]; VisionCorrectionList = new ObservableCollection(); SelectedVisionCorrectionList = new ObservableCollection(); InitVariable(); CMD_VisibleChanged = new DelegateCommand(VisibleChanged); CMD_ApplyGridItems = new DelegateCommand(PerformCmdApplyGridItems); CMD_SaveDataFile = new DelegateCommand(PerformCmdSaveDataFile); CMD_RunCorreXionPro = new DelegateCommand(PerformCmdRunCorreXionPro); CMD_RunMarkGrid = new DelegateCommand(PerformCmdRunMarkGrid); CMD_StopMarkGrid = new DelegateCommand(PerformCmdStopMarkGrid); CMD_RunScannerCalibration = new DelegateCommand(PerformCmdRunScannerCalibration); CMD_StopScannerCalibration = new DelegateCommand(PerformCmdStopScannerCalibration); _MonitorTimer = new DispatcherTimer(); _MonitorTimer.Tick += OnMonitoring; _MonitorTimer.Interval = TimeSpan.FromMilliseconds(100); // ian0513, 아래 Sequence 에서 보내 주는 걸로만 event 처리 하지 말고, event 등록 해서 처리 되도록 하는 것이 명확함. _Aurora.EventObservable.AsObservable(CommonSequenceIds.SeqScannerCalibration) .ObserveOnDispatcher().Subscribe(x => OnUpdateCorrectionTable(x)); } private void InitVariable() { GridRowCount = 13; GridColumnCount = 13; UnitLength = 10; TiltingDegree = 0; MPosOffsetX = 0; MPosOffsetY = 0; OffsetThreshold = 0.01; IsVisionCalibration = true; MarkPower = 10; MarkSpeed = 100; MarkLength = 10; LaserStatus = false; //InitializeDataGridViewItem(GridColumnCount, GridRowCount, UnitLength, TiltingDegree); } private void VisibleChanged(object obj) { bool visible = (bool)obj; if (visible) { _MonitorTimer.Start(); } else { _MonitorTimer.Stop(); } } private void OnMonitoring(object sender, EventArgs e) { double xPos, yPos; if (_Scanner != null) { _Scanner.GetXYPos(out xPos, out yPos); CurrentX = xPos; CurrentY = yPos; } if (_Laser.GetLaserReady()) { LaserStatus = true; } else { LaserStatus = false; } } private void OnUpdateCorrectionTable(EventMessage obj) { var updateDataGridItem = obj.Param as UpdateDataGridItem; if (updateDataGridItem != null) { VisionCorrectionList[updateDataGridItem.Upositionindex].VisionX = updateDataGridItem.UVisionX; VisionCorrectionList[updateDataGridItem.Upositionindex].VisionY = updateDataGridItem.UVisionY; } } private void PerformCmdApplyGridItems(object obj) { // 1. 원래 있던 data table 을 전부 초기화한다 // 2. 새로 적용된 row count 와 column count 로 새로 data table 을 만든다 // 3. VisionX 와 VisionY 는 0값을 넣어둔다. Calibration에서 패턴매칭 값을 이벤트로 받아와서 업데이트한다. try { InitializeDataGridViewItem(GridColumnCount, GridRowCount, UnitLength, TiltingDegree); // 코드 수정 필요 } catch (Exception ex) { MessageUtil.ShowErrorToast(ex.Message); } } private void PerformCmdSaveDataFile(object obj) { // 저장양식이 정해지면 업데이트하자 // 이전 버전은 database 에 저장하게 되어있음 try { SaveFileDialog dlg = new SaveFileDialog(); dlg.InitialDirectory = @"D:\LASA.eqpdata\Scanner\Correction"; dlg.DefaultExt = "dat"; dlg.Filter = "data 파일 (*.dat)|*.dat"; dlg.Title = "data 파일 저장"; if (dlg.ShowDialog() == true) { using (StreamWriter sw = new StreamWriter(dlg.FileName, false, Encoding.Default)) { string oldCorrectionFile = IsVisionCalibration ? _Scanner.Config.CorrectionFile : _Scanner.Config.LaserCorrectionFile; FileInfo oldCorrectionFileInfo = new FileInfo(oldCorrectionFile); sw.WriteLine(string.Format("OLDCTFILE\t= {0}", oldCorrectionFileInfo.Name)); sw.WriteLine(string.Format("NEWCTFILE\t= NewCorrectionTable.ct5")); sw.WriteLine(string.Format("\r\nTOLERANCE\t= 1.0")); uint newCal = (uint)Math.Round(_Scanner.Config.ScaleXYFactor); sw.WriteLine(string.Format("\r\nNEWCAL\t= {0}", newCal)); //sw.WriteLine(string.Format("LIMITS")); sw.WriteLine(string.Format("\r\n// RTC-X [Bit] RTC-Y[Bit] REAL-X[mm] REAL-Y[mm]\r\n")); double visionX = 0; double visionY = 0; double realX = 0; double realY = 0; double tempX = 0; double tempY = 0; for (int i = 0; i < GridRowCount * GridColumnCount; i++) { visionX = Convert.ToDouble(VisionCorrectionList[i].VisionX); visionY = Convert.ToDouble(VisionCorrectionList[i].VisionY); realX = Convert.ToDouble(VisionCorrectionList[i].MasterX); realY = Convert.ToDouble(VisionCorrectionList[i].MasterY); sw.WriteLine(string.Format("{0}\t{1}\t{2}\t{3}", visionX, visionY, realX, realY)); //string finalX; //string finalY; //if (tempX == 0) //{ // finalX = "0,0"; //} //else //{ // string sX; // string upperX; // string lowerX; // if ((Math.Round(tempX, 3) - Math.Truncate(tempX)) == 0) // { // sX = Convert.ToString(Math.Truncate(tempX)); // upperX = sX; // lowerX = "0"; // } // else // { // //sX = Convert.ToString(Math.Round(tempX, 3)); // sX = Math.Round(tempX, 3).ToString("f3"); // upperX = sX.Substring(0, sX.IndexOf(".")); // lowerX = sX.Substring(sX.IndexOf(".") + 1); // } // finalX = upperX + "," + lowerX; //} //if (tempY == 0) //{ // finalY = "0,0"; //} //else //{ // string sY; // string upperY; // string lowerY; // if ((Math.Round(tempY, 3) - Math.Truncate(tempY)) == 0) // { // sY = Convert.ToString(Math.Truncate(tempY)); // upperY = sY; // lowerY = "0"; // } // else // { // //sY = Convert.ToString(Math.Round(tempY, 3)); // sY = Math.Round(tempY, 3).ToString("f3"); // upperY = sY.Substring(0, sY.IndexOf(".")); // lowerY = sY.Substring(sY.IndexOf(".") + 1); // } // finalY = upperY + "," + lowerY; //} } } } } catch (Exception ex) { Log.E(string.Format(ex.Message)); } } private void PerformCmdRunCorreXionPro(object obj) { var filePath = ConfigurationManager.AppSettings.Get("DATA_PATH"); var fullPath = Path.Combine(filePath, "_device", "_scanner", "correXionPro.exe"); if (File.Exists(fullPath)) { Process.Start(fullPath); } else { Log.I("correXionPro is not exist"); } } private void PerformCmdRunMarkGrid(object obj) { if (_ScannerConfig.UseMarkingOnTheFly == true) { MessageUtil.ShowWarningToast(string.Format("Please Check Marking on the Fly(MOTF)")); return; } if (VisionCorrectionList.Count <= 0 || LaserStatus == false) { MessageUtil.ShowWarningToast(string.Format("Please Check DataCorrection or LaserStatus or LaserCalType")); return; } var result = MessageUtil.ShowYesNoMessageDialog("Laser Calibration", "Start Laser Calibration Sequence?"); if (result == false) { return; } if (_SeqTeachingRun.RunState == SequenceRunState.Running) { MessageUtil.ShowWarningToast(string.Format("이미 시퀀스가 Start 상태입니다.", "_SeqMarkGrid"), Log); return; } var currentModelInfo = EquipmentSpecificModelUtil.GetCurrentRecipeEquipmentSpecificModel(); if (currentModelInfo.equipmentSpecificModel == null) { MessageUtil.ShowWarningToast(string.Format("선택된 model 이 없습니다.", "SeqTeachingRun")); return; } var intervalTimeBlowOn = 0; var intervalTimeBlowOff = 0; if (currentModelInfo.equipmentSpecificModel.UseBlow) { intervalTimeBlowOn = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOn; intervalTimeBlowOff = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOff; } var param = new SeqTeachingRun_InitParam() { IsUseExternalObjects = true, ExternalObjects = CreateLinesForCalibration(), //UseStream = _EquipmentInfo.UseStream, UseStream = currentModelInfo.equipmentSpecificModel.UseStream, UseBlow = currentModelInfo.equipmentSpecificModel.UseBlow, IntervalTimeBlowOn = intervalTimeBlowOn, IntervalTimeBlowOff = intervalTimeBlowOff, NeedSetLaserTiming = false, }; IsSequenceRunning_MarkGrid = true; _SeqTeachingRun.Start(param, OnCompleteMarkGrid); } private void OnCompleteMarkGrid(SequenceActionResult res) { if (res != null) { if (res.Param is SeqTeachingRun_OutParam result && result.Success == true) { IsSequenceRunning_MarkGrid = false; MessageUtil.ShowSuccessToast(string.Format("Success SeqMarkGrid")); return; } } IsSequenceRunning_MarkGrid = false; MessageUtil.ShowWarningToast(string.Format("Fail SeqMarkGrid")); } private void PerformCmdStopMarkGrid(object obj) { if (_SeqTeachingRun.RunState == SequenceRunState.Idle) { return; } IsSequenceRunning_MarkGrid = false; _SeqTeachingRun.Stop(); } private void PerformCmdRunScannerCalibration(object obj) { if (_ScannerConfig.UseMarkingOnTheFly == true) { MessageUtil.ShowWarningToast(string.Format("Please Check Marking on the Fly(MOTF)")); return; } if (VisionCorrectionList.Count <= 0) { MessageUtil.ShowWarningToast(string.Format("\"Please Check Data Correction apply")); return; } var result = MessageUtil.ShowYesNoMessageDialog("Scanner Calibration", "Start Scanner Calibration Sequence?\nCheck Calibration Type\nTo calibrate after MarkGrid, CalType set to Laser"); if (result == false) { return; } if (_SeqScannerCalibration.RunState == SequenceRunState.Running) { MessageUtil.ShowWarningToast(string.Format("이미 시퀀스가 Start 상태입니다.", "_SeqScannerCalibration"), Log); return; } var param = new SeqScannerCalibration_InitParam() { FovInfo = _FovInfo, IsVisionCalibration = IsVisionCalibration, ColumnCount = GridColumnCount, RowCount = GridRowCount, UnitLength = UnitLength, TiltingDegree = TiltingDegree, CheckOffset = OffsetThreshold, MasterPosOffsetX = MPosOffsetX, // ian0513, 원본에는 MPosOffsetY 로 되어 있던데... 확인 필요. MasterPosOffsetY = MPosOffsetY, Is1StepCalibration = Is1StepCalibration, oneStepCalibrationIndex = SelectedIndex, }; IsSequenceRunning_ScannerCalibration = true; _SeqScannerCalibration.Start(param, OnCompleteScannerCalibration); } private void OnCompleteScannerCalibration(SequenceActionResult res) { if (res != null) { if (res.Param is SeqScannerCalibration_OutParam result && result.Success == true) { IsSequenceRunning_ScannerCalibration = false; MessageUtil.ShowSuccessToast(string.Format("Success SeqScannerCalibration")); return; } } IsSequenceRunning_ScannerCalibration = false; MessageUtil.ShowWarningToast(string.Format("Fail SeqScannerCalibration")); } private void PerformCmdStopScannerCalibration(object obj) { if (_SeqScannerCalibration.RunState == SequenceRunState.Idle) { return; } IsSequenceRunning_ScannerCalibration = false; _SeqScannerCalibration.Stop(); } private void InitializeDataGridViewItem(int columnCount, int rowCount, float unitLength, float tiltingDegree) { try { VisionCorrectionList.Clear(); for (int column = 0; column < columnCount; column++) { for (int row = 0; row < rowCount; row++) { CorrectionUnit dtItem = new CorrectionUnit(); dtItem.Column = column + 1; dtItem.Row = row + 1; float tempX = unitLength * ((column % columnCount) - (columnCount - 1) / 2f); float tempY = unitLength * ((row % rowCount) - (rowCount - 1) / 2f); float masterX = tempX * (float)Math.Cos(tiltingDegree * Math.PI / 180) - tempY * (float)Math.Sin(tiltingDegree * Math.PI / 180); float masterY = tempX * (float)Math.Sin(tiltingDegree * Math.PI / 180) + tempY * (float)Math.Cos(tiltingDegree * Math.PI / 180); dtItem.MasterX = masterX; dtItem.MasterY = masterY; dtItem.VisionX = 0; dtItem.VisionY = 0; VisionCorrectionList.Add(dtItem); } } } catch (Exception) { MessageUtil.CreateAndShowMessageDialog("Correction", "Init Table Fail."); } } public void MoveScanner() { // 선택된 셀이 없거나(-1) 리스트 범위를 벗어난 인덱스면 무시 (그리드 빈영역/헤더 더블클릭, Apply 전, Esc 선택해제 등) if (SelectedIndex < 0 || SelectedIndex >= VisionCorrectionList.Count) return; double scannerPosX = VisionCorrectionList[SelectedIndex].MasterX; double scannerPosY = VisionCorrectionList[SelectedIndex].MasterY; var res = MessageUtil.ShowYesNoMessageDialog("Scanner", $"Move Scanner to Postion(X : {scannerPosX}, Y: {scannerPosY}) of Selected Cell?"); if (res) { _Scanner.JumpAbs(scannerPosX, scannerPosY, eFunctionType.COMMAND); } } private List CreateLinesForCalibration() { ScannerParam param = new ScannerParam(); param.LaserPower1 = (uint)(MarkPower / 100 * LaserPowerCalParam.MaxCenterPower); param.LaserPower2 = 0; // ScannerCalibration은 링을 쏠 필요가 없음. param.MarkSpeed = MarkSpeed; param.JumpSpeed = 1000; param.LaserFrequency = MarkFreq; param.LaserPulseDuration = MarkPulseWidth; param.MarkDelay = 200; param.JumpDelay = 200; param.PolygonDelay = 200; ScannerCanvasUserCtrl.ScannerParams[127] = param; // Initialize MasterPos List _MarkingPositionList_MM = new List(); float posX = 0; float posY = 0; for (int column = 0; column < GridColumnCount; column++) { posX = UnitLength * ((column % GridColumnCount) - (GridColumnCount - 1) / 2f); for (int row = 0; row < GridRowCount; row++) { posY = UnitLength * ((row % GridRowCount) - (GridRowCount - 1) / 2f); _MarkingPositionList_MM.Add(new PointF(posX, posY)); } } List drawObjects = new List(); for (int i = 0; i < _MarkingPositionList_MM.Count; i++) { Line line = new Line(); line.Enabled = true; line.Selected = true; line.ObjectOffset = new PointF(0, 0); line.ScannerParamNo = 127; //십자마킹 가로 line.P1 = new UnitPoint(_MarkingPositionList_MM[i].X - MarkLength * 0.5, _MarkingPositionList_MM[i].Y); line.P2 = new UnitPoint(_MarkingPositionList_MM[i].X + MarkLength * 0.5, _MarkingPositionList_MM[i].Y); drawObjects.Add(line); line = new Line(); line.Enabled = true; line.Selected = true; line.ObjectOffset = new PointF(0, 0); line.ScannerParamNo = 127; //십자마킹 세로 line.P1 = new UnitPoint(_MarkingPositionList_MM[i].X, _MarkingPositionList_MM[i].Y - MarkLength * 0.5); line.P2 = new UnitPoint(_MarkingPositionList_MM[i].X, _MarkingPositionList_MM[i].Y + MarkLength * 0.5); drawObjects.Add(line); } foreach (IDrawObject obj in drawObjects) { Debug.WriteLine(string.Format("Objects : ({0},{1})->({2},{3})", obj.PF1.X, obj.PF1.Y, obj.PF2.X, obj.PF2.Y)); } return drawObjects; } } public class CorrectionUnit : NotifyPropertyChangedBase { private int _Column; public int Column { get => _Column; set => SetProperty(ref _Column, value); } private int _Row; public int Row { get => _Row; set => SetProperty(ref _Row, value); } private float _MasterX; public float MasterX { get => _MasterX; set => SetProperty(ref _MasterX, value); } private float _MasterY; public float MasterY { get => _MasterY; set => SetProperty(ref _MasterY, value); } private int _VisionX; public int VisionX { get => _VisionX; set => SetProperty(ref _VisionX, value); } private int _VisionY; public int VisionY { get => _VisionY; set => SetProperty(ref _VisionY, value); } } }