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

628 lines
27 KiB
C#

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<CorrectionUnit> _SelectedVisionCorrectionList;
public ObservableCollection<CorrectionUnit> SelectedVisionCorrectionList { get => _SelectedVisionCorrectionList; set => SetProperty(ref _SelectedVisionCorrectionList, value); }
private ObservableCollection<CorrectionUnit> _VisionCorrectionList;
public ObservableCollection<CorrectionUnit> 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<CorrectionUnit>();
SelectedVisionCorrectionList = new ObservableCollection<CorrectionUnit>();
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()
{
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<IDrawObject> 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<PointF> _MarkingPositionList_MM = new List<PointF>();
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<IDrawObject> drawObjects = new List<IDrawObject>();
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); }
}
}