添加 MX-PD-盘古 项目文件

将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
Shi.Ji
2026-05-18 11:43:09 +08:00
parent 03632a379d
commit e31d3560bb
739 changed files with 99783 additions and 0 deletions

View File

@@ -0,0 +1,275 @@
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Hardware;
using MainShell.Motion;
using Maxwell.SemiFramework.DefaultConfig.Vision;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.ManagerService;
using SemiconductorVisionAlgorithm.SemiParams;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Point = SemiconductorVisionAlgorithm.SemiParams.Point;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class CameraFusionCalibVerifyViewModel : Screen, IPage
{
public string Name { get; set; } = "CameraFusionCalibVerify";
public string CameraName { get; set; }
private readonly FusionCalibMotionService _fusionCalibMotionService;
private HardwareManager _hardware;
private CameraAxisViewModel _cameraAxisViewModelSevice = new CameraAxisViewModel();
public CameraAxisViewModel CameraAxisViewModelSevice
{
get { return _cameraAxisViewModelSevice; }
set
{
_cameraAxisViewModelSevice = value;
OnPropertyChanged(nameof(CameraAxisViewModelSevice));
}
}
private FusionCalibVerifyParItem _wsfusionCalibVerifyParItem = new FusionCalibVerifyParItem();
public FusionCalibVerifyParItem WsfusionCalibVerifyParItem
{
get { return _wsfusionCalibVerifyParItem; }
set
{
_wsfusionCalibVerifyParItem = value;
OnPropertyChanged(nameof(WsfusionCalibVerifyParItem));
}
}
private bool _stopMotion = false;
public CameraFusionCalibVerifyViewModel(HardwareManager hardware, FusionCalibMotionService fusionCalibMotionService)
{
_hardware = hardware;
_fusionCalibMotionService = fusionCalibMotionService ?? throw new ArgumentNullException(nameof(fusionCalibMotionService));
}
public static CameraFusionCalibVerifyViewModel Create(string name, string cameraName, List<HardwareDevice> device, HardwareManager hardware, FusionCalibMotionService motion)
{
var vm = new CameraFusionCalibVerifyViewModel(hardware, motion);
vm.Initialize(name, device);
return vm;
}
private void Initialize(string cameraName, List<HardwareDevice> device)
{
CameraName = cameraName;
Name = cameraName;
var paramList = IoC.Get<IParameterManager>() as IParamList;
}
public void btnMoveWSCalibPosPosition()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
_fusionCalibMotionService.SafeMove(_hardware.Axis_X1, WsfusionCalibVerifyParItem.MoveAxisPos);
_fusionCalibMotionService.MoveWsAvoidance(WsfusionCalibVerifyParItem.ApproachXPos, WsfusionCalibVerifyParItem.ApproachYPos);
//_safeAxisMotion.SafeAbsoluteMove(_gs.Axis_X11, CameraWaferFusionCalibrationItem.WSPlatCalibXPos, block: false);
//_safeAxisMotion.SafeMoveWS(CameraWaferFusionCalibrationItem.WSApproachXPos, CameraWaferFusionCalibrationItem.WSApproachYPos, isBlock: false);
}
catch (Exception ex)
{
}
});
}
public void btnMoveToCenter()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
if (ApproachPointWS(1))
{
WsfusionCalibVerifyParItem.StartX = _hardware.Axis_X2.State.ActualPos;
WsfusionCalibVerifyParItem.StartY = _hardware.Axis_Y1.State.ActualPos;
//CameraWaferFusionCalibrationItem.WaferStartVerifyX = _gs.Axis_X21.State.ActualPos;
//CameraWaferFusionCalibrationItem.WaferStartVerifyY = _gs.Axis_Y21.State.ActualPos;
MwMessageBox.Show("中心对齐完成");
}
else
{
MwMessageBox.Show("逼近失败!");
}
}
catch (Exception ex)
{
}
});
}
public void btnMoveToStartPoint()
{
//_safeAxisMotion.SafeMoveXY(_gs.Axis_X21, _gs.Axis_Y21, CameraWaferFusionCalibrationItem.WaferStartVerifyX, CameraWaferFusionCalibrationItem.WaferStartVerifyY);
_fusionCalibMotionService.MoveWsAvoidance(WsfusionCalibVerifyParItem.StartX, WsfusionCalibVerifyParItem.StartY);
}
public void btnMoveWafer()
{
//WsfusionCalibVerifyParItem.VerifyRealPts = new List<Point>();
//List<Point> points = new List<Point>();
//System.Threading.Tasks.Task.Run(() =>
//{
// for (var i = 0; i < WsfusionCalibVerifyParItem.CountY; i++)
// {
// for (var j = 0; j < WsfusionCalibVerifyParItem.CountX; j++)
// {
// if (_stopMotion)
// {
// _stopMotion = false;
// return;
// }
// double yPos = WsfusionCalibVerifyParItem.StartY + i * WsfusionCalibVerifyParItem.Step;
// double xPos;
// if (i % 2 == 0)
// {
// xPos = WsfusionCalibVerifyParItem.StartX + j * WsfusionCalibVerifyParItem.Step;
// }
// else
// {
// xPos = WsfusionCalibVerifyParItem.StartX + (WsfusionCalibVerifyParItem.CountX - j - 1) * WsfusionCalibVerifyParItem.Step;
// }
// points.Add(new Point(xPos, yPos));
// _fusionCalibMotionService.MoveWsAvoidance(xPos, yPos);
// if (ApproachPointWS())
// {
// //WsfusionCalibVerifyParItem.ApproachXPos = _hardware.Axis_X2.State.ActualPos;
// //CameraWaferFusionCalibrationItem.WSVerityApproachXpos = _gs.Axis_X21.State.ActualPos;
// //CameraWaferFusionCalibrationItem.WSVerityApproachYpos = _gs.Axis_Y21.State.ActualPos;
// Point ruler = new Point(_hardware.Axis_X2.State.ActualPos, _hardware.Axis_Y1.State.ActualPos);
// Point real = _visionOperation.GetRealByRuler(_hardware.Camera_ExtendWS.Id, ruler);
// WsfusionCalibVerifyParItem.VerifyRealPts.Add(new Point(real.X, real.Y));
// }
// else
// {
// MwMessageBox.Show("逼近失败!");
// }
// }
// }
// MwMessageBox.Show("飞拍结束");
//});
}
public void btnVerifyWafer()
{
//if (WsfusionCalibVerifyParItem.VerifyRealPts != null && WsfusionCalibVerifyParItem.VerifyRealPts.Count > 0)
//{
// var result = MwMessageBox.Show("是否已移动到Pad位置确认开始验证?", "确认开始", MessageBoxButton.YesNo, MessageBoxImage.Information);
// if (result == MessageBoxResult.Yes)
// {
// System.Threading.Tasks.Task.Factory.StartNew(() =>
// {
// List<SemiconductorVisionAlgorithm.SemiParams.Point> WSrulerThen = new List<SemiconductorVisionAlgorithm.SemiParams.Point>();
// List<SemiconductorVisionAlgorithm.SemiParams.Point> WSrulerNow = new List<SemiconductorVisionAlgorithm.SemiParams.Point>();
// Point padReal = _visionOperation.GetRealByRuler(_gs.Camera_Extend.Id, new Point(_gs.Axis_X11.State.ActualPos, _gs.Axis_Y11.State.ActualPos));
// for (var i = 0; i < WsfusionCalibVerifyParItem.VerifyRealPts.Count; i++)
// {
// if (_stopMotion)
// {
// _stopMotion = false;
// return;
// }
// SemiconductorVisionAlgorithm.SemiParams.Point rulerPoint = new SemiconductorVisionAlgorithm.SemiParams.Point();
// JM1Vision.JM1Manager.Instance.get_ws_pos(_gs.Camera_Extend.Id, _gs.Camera_ExtendWS.Id, new SemiconductorVisionAlgorithm.SemiParams.Point(padReal.X, padReal.Y), new SemiconductorVisionAlgorithm.SemiParams.Point(CameraWaferFusionCalibrationItem.VerityrealPointRealPoints[i].X, CameraWaferFusionCalibrationItem.VerityrealPointRealPoints[i].Y), out rulerPoint);
// WSrulerThen.Add(new SemiconductorVisionAlgorithm.SemiParams.Point(rulerPoint.X, rulerPoint.Y));
// _fusionCalibMotionService.MoveWsAvoidance(rulerPoint.X, rulerPoint.Y);
// //_safeAxisMotion.SafeMoveWS(rulerPoint.X, rulerPoint.Y, isCalib: true);
// if (ApproachPointWS())
// {
// WSrulerNow.Add(new SemiconductorVisionAlgorithm.SemiParams.Point(_hardware.Axis_X2.State.ActualPos, _hardware.Axis_Y1.State.ActualPos));
// }
// else
// {
// MwMessageBox.Show("逼近失败!");
// }
// }
// string filePath = System.AppDomain.CurrentDomain.BaseDirectory + @"CalibData";
// JM1Vision.JM1Manager.Instance.pos_verify(filePath, WSrulerThen, WSrulerNow);
// MwMessageBox.Show("验证完成!");
// });
// }
//}
//else
//{
// MwMessageBox.Show("请先执行飞拍!");
// return;
//}
}
public void btnStopVerifyWafer()
{
try
{
var result = MwMessageBox.Show("是否停止?", "确认停止", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
_stopMotion = true;
}
}
catch (Exception ex)
{
}
}
private bool ApproachPointWS(int approachTime = 10)
{
return false;
//string modelFileName = WsfusionCalibVerifyParItem.ModelPath;
//string path = System.AppDomain.CurrentDomain.BaseDirectory + modelFileName;
//Rectangle1 r = GetRectangle();
//VisionOperation visionOperation = IoC.Get<VisionOperation>();
////相机拍照
//return visionOperation.ApproachPointCircle(_hardware.Camera_ExtendWS, _hardware.Axis_X2, _hardware.Axis_Y1, path, r, out System.Windows.Point point, approachTime);
}
private Rectangle1 GetRectangle()
{
var region = CameraAxisViewModelSevice.Regions;
Rectangle rectangle = null;
Rectangle1 r = null;
if (region != null && region.Count > 0)
{
RectRegion rect = region[0].Region as RectRegion;
double start_x = rect.CenterPoint.X - rect.Width / 2;
double start_y = rect.CenterPoint.Y - rect.Height / 2;
double end_x = rect.CenterPoint.X + rect.Width / 2;
double end_y = rect.CenterPoint.Y + rect.Height / 2;
rectangle = new Rectangle(new System.Windows.Point(start_y, start_x), new System.Windows.Point(end_y, end_x));
r = new Rectangle1();
r.Start_X = rectangle.StartPoint.X;
r.Start_Y = rectangle.StartPoint.Y;
r.End_X = rectangle.EndPoint.X;
r.End_Y = rectangle.EndPoint.Y;
}
else
{
throw new Exception("请添加矩形!");
}
return r;
}
}
}

View File

@@ -0,0 +1,409 @@
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Filewritable;
using MainShell.Hardware;
using MainShell.Motion;
using Maxwell.SemiFramework.DefaultConfig.Vision;
using Maxwell.SemiFramework.WaferCalibration.View;
using Maxwell.SemiFramework.WaferCalibration.ViewModel;
using MaxwellControl.Tools;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.ControlCanvas.DrawingControl;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.Device;
using MwFramework.ManagerService;
using SemiconductorVisionAlgorithm.SemiParams;
using SemiconductorVisionAlgorithm.SemiWaferRecip;
using Stylet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using CameraAxisViewModel = MainShell.Common.Display.ViewModel.CameraAxisViewModel;
using Point = SemiconductorVisionAlgorithm.SemiParams.Point;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class CameraFusionCalibViewModel:Screen,IPage
{
private bool _stopMotion = false;
public string Name { get; set; } = "CameraFusionCalib";
public string CalibName { get; set; }
public string CameraName { get; set; }
private readonly FusionCalibMotionService _fusionCalibMotionService;
private HardwareManager _hardware;
private DelegateBase _motion = new DelegateBase();
public DelegateBase Motion
{
get
{
return _motion;
}
set
{
_motion = value;
OnPropertyChanged(nameof(Motion));
}
}
private CameraAxisViewModel _cameraAxisViewModelSevice = new CameraAxisViewModel();
public CameraAxisViewModel CameraAxisViewModelSevice
{
get { return _cameraAxisViewModelSevice; }
set
{
_cameraAxisViewModelSevice = value;
OnPropertyChanged(nameof(CameraAxisViewModelSevice));
}
}
private FusionCalibParItem _wsFusionCalibParItem = new FusionCalibParItem();
public FusionCalibParItem WsFusionCalibParItem
{
get { return _wsFusionCalibParItem; }
set
{
_wsFusionCalibParItem = value;
OnPropertyChanged(nameof(WsFusionCalibParItem));
}
}
private CameraFusionCalibViewModel _service;
public CameraFusionCalibViewModel Service
{
get { return _service; }
set
{
_service = value;
NotifyOfPropertyChange();
}
}
public CameraFusionCalibViewModel()
{
}
private bool _isInitialized = false;
public void viewLoad()
{
base.OnViewLoaded();
if (_isInitialized)
{
return;
}
if (_hardware != null)
{
_cameraAxisViewModelSevice = new CameraAxisViewModel();
_cameraAxisViewModelSevice.CameraAxisDevices.HardwareDeviceList = _hardware.CameraAxisManager.TopPositionCameraAxisDevices;
NotifyOfPropertyChange(nameof(CameraAxisViewModelSevice));
}
//RebuildModuleViewModels();
_isInitialized = true;
}
public void viewUnLoad()
{
}
public CameraFusionCalibViewModel(HardwareManager hardware, FusionCalibMotionService fusionCalibMotionService)
{
_hardware = hardware;
_fusionCalibMotionService = fusionCalibMotionService ?? throw new ArgumentNullException(nameof(fusionCalibMotionService));
}
public static CameraFusionCalibViewModel Create(string name,string cameraName, List<HardwareDevice> device, HardwareManager hardware, FusionCalibMotionService motion)
{
var vm = new CameraFusionCalibViewModel(hardware,motion);
vm.Initialize(name, device);
return vm;
}
private void Initialize(string cameraName, List<HardwareDevice> device)
{
CameraName = cameraName;
Name = cameraName;
//Hardware = device;
//FileSaveDir = Path.Combine(Paths.CalibSettingPath, "bottomCamera");
var paramList = IoC.Get<IParameterManager>() as IParamList;
Service = new CameraFusionCalibViewModel();
//Service.IsShowSolidLine = true;
//Service.ShapeThickness = 1;
//Service.DrawInConcurrency = false;
//Service.IsAxisControlLDBVisible = Visibility.Visible;
}
public void btnMoveWSCalibPosPosition()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
_fusionCalibMotionService.SafeMove(_hardware.Axis_X1, WsFusionCalibParItem.MoveAxisPos);
_fusionCalibMotionService.MoveWsAvoidance(WsFusionCalibParItem.ApproachXPos, WsFusionCalibParItem.ApproachYPos);
}
catch (Exception ex)
{
}
});
}
public void btnApproachWSCalibPosition()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
//if (FindPointWSCircle())
{
WsFusionCalibParItem.ApproachXPos = _hardware.Axis_X2.State.ActualPos;
WsFusionCalibParItem.ApproachYPos = _hardware.Axis_Y1.State.ActualPos;
MessageBox.Show($"对齐值X2:{WsFusionCalibParItem.ApproachXPos},Y1:{WsFusionCalibParItem.ApproachYPos}");
}
//else
//{
// MessageBox.Show("对齐失败!");
//}
}
catch (Exception ex)
{
}
});
}
public void btnCalculateCameraPosition()
{
try
{
//起点覆盖标定范围起点往负方向6mm
int offset = -10;
double X2MinPos = 0;
_hardware.Axis_X2.GetSoftMel(ref X2MinPos);
var delx = WsFusionCalibParItem.ApproachXPos - X2MinPos + offset;
WsFusionCalibParItem.CameraAxisPos = WsFusionCalibParItem.MoveAxisPos - delx;
}
catch (Exception ex)
{
}
}
public void btnSetCameraPosition()
{
var result = MwMessageBox.Show("是否使用当前位置?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
try
{
WsFusionCalibParItem.CameraAxisPos = _hardware.Axis_X1.State.ActualPos;
}
catch (Exception ex)
{
}
}
}
public void btnMoveCameraPosition()
{
var result = MwMessageBox.Show("是否移动到该点?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
_fusionCalibMotionService.SafeMove(_hardware.Axis_X1, WsFusionCalibParItem.CameraAxisPos);
}
catch (Exception ex)
{
}
});
}
}
public void btnCalculateWSPosition()
{
try
{
//起点覆盖标定范围起点往负方向6mm
int offset = -10;
double X2MinPos = 0;
_hardware.Axis_X2.GetSoftMel(ref X2MinPos);
var delx = WsFusionCalibParItem.ApproachXPos - X2MinPos + offset;
WsFusionCalibParItem.CameraAxisPos = WsFusionCalibParItem.MoveAxisPos - delx;
double x11Offset = WsFusionCalibParItem.CameraAxisPos - WsFusionCalibParItem.MoveAxisPos;
WsFusionCalibParItem.StartX = WsFusionCalibParItem.ApproachXPos + x11Offset;
WsFusionCalibParItem.StartY = WsFusionCalibParItem.ApproachYPos;
}
catch (Exception ex)
{
}
}
public void btnSetWaferStartPosition()
{
var result = MwMessageBox.Show("是否使用当前位置?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
WsFusionCalibParItem.StartX = _hardware.Axis_X2.State.ActualPos;
WsFusionCalibParItem.StartY = _hardware.Axis_Y1.State.ActualPos;
}
catch (Exception ex)
{
}
});
}
}
public void btnMoveWaferStartPosition()
{
var result = MwMessageBox.Show("是否移动到该点?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
try
{
_fusionCalibMotionService.MoveWsAvoidance(WsFusionCalibParItem.StartX, WsFusionCalibParItem.StartY);
}
catch (Exception ex)
{
}
}
}
public void btnMoveToCenter()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
//if (ApproachPointWS(1))
//{
// CameraWaferFusionCalibrationItem.WaferStartVerifyX = _gs.Axis_X21.State.ActualPos;
// CameraWaferFusionCalibrationItem.WaferStartVerifyY = _gs.Axis_Y21.State.ActualPos;
// MwMessageBox.Show("中心对齐完成");
//}
//else
//{
// MwMessageBox.Show("逼近失败!");
//}
}
catch (Exception ex)
{
}
});
}
public void btnStart()
{
try
{
var result = MwMessageBox.Show("是否开始?", "确认开始", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
//CameraWaferFusionCalibrationItem.AxisAndPixelPointItemList.Clear();
FusionCalib(false);
}
}
catch (Exception ex)
{
}
}
public void btnStop()
{
try
{
var result = MwMessageBox.Show("是否开始?", "确认开始", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
Motion.Cancel();
_stopMotion = true;
}
}
catch (Exception ex)
{
}
}
private void FusionCalib(bool isRecheck, bool isAsyn = true)
{
Motion = (DelegateBase)TaskManager.Instance.GetIntance(typeof(FusionCalibTask));
FusionCalibTask cameraFusionCalibration = Motion as FusionCalibTask;
if (cameraFusionCalibration != null)
{
cameraFusionCalibration.Rectangle = GetRectangle();
}
TaskManager.Instance.AsyncStart(Motion, new object[] { });
}
private Rectangle1 GetRectangle()
{
var region = CameraAxisViewModelSevice.Regions;
Rectangle rectangle = null;
Rectangle1 r = null;
if (region != null && region.Count > 0)
{
RectRegion rect = region[0].Region as RectRegion;
double start_x = rect.CenterPoint.X - rect.Width / 2;
double start_y = rect.CenterPoint.Y - rect.Height / 2;
double end_x = rect.CenterPoint.X + rect.Width / 2;
double end_y = rect.CenterPoint.Y + rect.Height / 2;
rectangle = new Rectangle(new System.Windows.Point(start_y, start_x), new System.Windows.Point(end_y, end_x));
r = new Rectangle1();
r.Start_X = rectangle.StartPoint.X;
r.Start_Y = rectangle.StartPoint.Y;
r.End_X = rectangle.EndPoint.X;
r.End_Y = rectangle.EndPoint.Y;
}
else
{
throw new Exception("请添加矩形!");
}
return r;
}
}
}

View File

@@ -0,0 +1,269 @@
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Hardware;
using Maxwell.SemiFramework.DefaultConfig.Vision;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.ManagerService;
using SemiconductorVisionAlgorithm.SemiParams;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Point = SemiconductorVisionAlgorithm.SemiParams.Point;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class CameraFusionPadCalibVerifyViewModel:Screen,IPage
{
public string Name { get; set; } = "CameraFusionPadCalibVerify";
public string CameraName { get; set; }
private readonly FusionCalibMotionService _fusionCalibMotionService;
private HardwareManager _hardware;
private CameraAxisViewModel _cameraAxisViewModelSevice = new CameraAxisViewModel();
public CameraAxisViewModel CameraAxisViewModelSevice
{
get { return _cameraAxisViewModelSevice; }
set
{
_cameraAxisViewModelSevice = value;
OnPropertyChanged(nameof(CameraAxisViewModelSevice));
}
}
private FusionCalibVerifyParItem _padfusionCalibVerifyParItem = new FusionCalibVerifyParItem();
public FusionCalibVerifyParItem PadfusionCalibVerifyParItem
{
get { return _padfusionCalibVerifyParItem; }
set
{
_padfusionCalibVerifyParItem = value;
OnPropertyChanged(nameof(PadfusionCalibVerifyParItem));
}
}
private bool _stopMotion = false;
public CameraFusionPadCalibVerifyViewModel(HardwareManager hardware, FusionCalibMotionService fusionCalibMotionService)
{
_hardware = hardware;
_fusionCalibMotionService = fusionCalibMotionService ?? throw new ArgumentNullException(nameof(fusionCalibMotionService));
}
public static CameraFusionPadCalibVerifyViewModel Create(string name, string cameraName, List<HardwareDevice> device, HardwareManager hardware, FusionCalibMotionService motion)
{
var vm = new CameraFusionPadCalibVerifyViewModel(hardware, motion);
vm.Initialize(name, device);
return vm;
}
private void Initialize(string cameraName, List<HardwareDevice> device)
{
CameraName = cameraName;
Name = cameraName;
var paramList = IoC.Get<IParameterManager>() as IParamList;
}
public void btnMovePadCalibPosPosition()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
_fusionCalibMotionService.SafeMove(_hardware.Axis_X1, PadfusionCalibVerifyParItem.MoveAxisPos);
_fusionCalibMotionService.MoveWsAvoidance(PadfusionCalibVerifyParItem.ApproachXPos, PadfusionCalibVerifyParItem.ApproachYPos);
}
catch (Exception ex)
{
}
});
}
public void btnMoveToCenter()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
if (ApproachPointPad(1))
{
PadfusionCalibVerifyParItem.StartX = _hardware.Axis_X2.State.ActualPos;
PadfusionCalibVerifyParItem.StartY = _hardware.Axis_Y1.State.ActualPos;
MwMessageBox.Show("中心对齐完成");
}
else
{
MwMessageBox.Show("逼近失败!");
}
}
catch (Exception ex)
{
}
});
}
public void btnMoveToStartPoint()
{
_fusionCalibMotionService.MovePhsAvoidance(PadfusionCalibVerifyParItem.StartX, PadfusionCalibVerifyParItem.StartY);
}
public void btnMovePad()
{
//PadfusionCalibVerifyParItem.VerifyRealPts = new List<Point>();
//List<Point> points = new List<Point>();
//System.Threading.Tasks.Task.Run(() =>
//{
// for (var i = 0; i < PadfusionCalibVerifyParItem.CountY; i++)
// {
// for (var j = 0; j < PadfusionCalibVerifyParItem.CountX; j++)
// {
// if (_stopMotion)
// {
// _stopMotion = false;
// return;
// }
// double yPos = PadfusionCalibVerifyParItem.StartY + i * PadfusionCalibVerifyParItem.Step;
// double xPos;
// if (i % 2 == 0)
// {
// xPos = PadfusionCalibVerifyParItem.StartX + j * PadfusionCalibVerifyParItem.Step;
// }
// else
// {
// xPos = PadfusionCalibVerifyParItem.StartX + (PadfusionCalibVerifyParItem.CountX - j - 1) * PadfusionCalibVerifyParItem.Step;
// }
// points.Add(new Point(xPos, yPos));
// _fusionCalibMotionService.MovePhsAvoidance(xPos, yPos);
// if (ApproachPointPad())
// {
// //WsfusionCalibVerifyParItem.ApproachXPos = _hardware.Axis_X2.State.ActualPos;
// //CameraWaferFusionCalibrationItem.WSVerityApproachXpos = _gs.Axis_X21.State.ActualPos;
// //CameraWaferFusionCalibrationItem.WSVerityApproachYpos = _gs.Axis_Y21.State.ActualPos;
// Point ruler = new Point(_hardware.Axis_X1.State.ActualPos, _hardware.Axis_Y2.State.ActualPos);
// Point real = _visionOperation.GetRealByRuler(_hardware.Camera_ExtendWS.Id, ruler);
// PadfusionCalibVerifyParItem.VerifyRealPts.Add(new Point(real.X, real.Y));
// }
// else
// {
// MwMessageBox.Show("逼近失败!");
// }
// }
// }
// MwMessageBox.Show("飞拍结束");
//});
}
public void btnVerifyPad()
{
//if (PadfusionCalibVerifyParItem.VerifyRealPts != null && PadfusionCalibVerifyParItem.VerifyRealPts.Count > 0)
//{
// var result = MwMessageBox.Show("是否已移动到Pad位置确认开始验证?", "确认开始", MessageBoxButton.YesNo, MessageBoxImage.Information);
// if (result == MessageBoxResult.Yes)
// {
// System.Threading.Tasks.Task.Factory.StartNew(() =>
// {
// List<SemiconductorVisionAlgorithm.SemiParams.Point> PadrulerThen = new List<SemiconductorVisionAlgorithm.SemiParams.Point>();
// List<SemiconductorVisionAlgorithm.SemiParams.Point> PadrulerNow = new List<SemiconductorVisionAlgorithm.SemiParams.Point>();
// Point padReal = _visionOperation.GetRealByRuler(_gs.Camera_Extend.Id, new Point(_gs.Axis_X11.State.ActualPos, _gs.Axis_Y11.State.ActualPos));
// for (var i = 0; i < PadfusionCalibVerifyParItem.VerifyRealPts.Count; i++)
// {
// if (_stopMotion)
// {
// _stopMotion = false;
// return;
// }
// SemiconductorVisionAlgorithm.SemiParams.Point rulerPoint = new SemiconductorVisionAlgorithm.SemiParams.Point();
// JM1Vision.JM1Manager.Instance.get_ws_pos(_gs.Camera_Extend.Id, _gs.Camera_ExtendWS.Id, new SemiconductorVisionAlgorithm.SemiParams.Point(padReal.X, padReal.Y), new SemiconductorVisionAlgorithm.SemiParams.Point(CameraWaferFusionCalibrationItem.VerityrealPointRealPoints[i].X, CameraWaferFusionCalibrationItem.VerityrealPointRealPoints[i].Y), out rulerPoint);
// PadrulerThen.Add(new SemiconductorVisionAlgorithm.SemiParams.Point(rulerPoint.X, rulerPoint.Y));
// _fusionCalibMotionService.MovePhsAvoidance(rulerPoint.X, rulerPoint.Y);
// //_safeAxisMotion.SafeMoveWS(rulerPoint.X, rulerPoint.Y, isCalib: true);
// if (ApproachPointPad())
// {
// PadrulerNow.Add(new SemiconductorVisionAlgorithm.SemiParams.Point(_hardware.Axis_X2.State.ActualPos, _hardware.Axis_Y1.State.ActualPos));
// }
// else
// {
// MwMessageBox.Show("逼近失败!");
// }
// }
// string filePath = System.AppDomain.CurrentDomain.BaseDirectory + @"CalibData";
// JM1Vision.JM1Manager.Instance.pos_verify(filePath, PadrulerThen, PadrulerNow);
// MwMessageBox.Show("验证完成!");
// });
// }
//}
//else
//{
// MwMessageBox.Show("请先执行飞拍!");
// return;
//}
}
public void btnStopVerifyPad()
{
try
{
var result = MwMessageBox.Show("是否停止?", "确认停止", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
_stopMotion = true;
}
}
catch (Exception ex)
{
}
}
private bool ApproachPointPad(int approachTime = 10)
{
return false;
//string modelFileName = PadfusionCalibVerifyParItem.ModelPath;
//string path = System.AppDomain.CurrentDomain.BaseDirectory + modelFileName;
//Rectangle1 r = GetRectangle();
//VisionOperation visionOperation = IoC.Get<VisionOperation>();
////相机拍照
//return visionOperation.ApproachPointCircle(_hardware.Camera_ExtendWS, _hardware.Axis_X2, _hardware.Axis_Y1, path, r, out System.Windows.Point point, approachTime);
}
private Rectangle1 GetRectangle()
{
var region = CameraAxisViewModelSevice.Regions;
Rectangle rectangle = null;
Rectangle1 r = null;
if (region != null && region.Count > 0)
{
RectRegion rect = region[0].Region as RectRegion;
double start_x = rect.CenterPoint.X - rect.Width / 2;
double start_y = rect.CenterPoint.Y - rect.Height / 2;
double end_x = rect.CenterPoint.X + rect.Width / 2;
double end_y = rect.CenterPoint.Y + rect.Height / 2;
rectangle = new Rectangle(new System.Windows.Point(start_y, start_x), new System.Windows.Point(end_y, end_x));
r = new Rectangle1();
r.Start_X = rectangle.StartPoint.X;
r.Start_Y = rectangle.StartPoint.Y;
r.End_X = rectangle.EndPoint.X;
r.End_Y = rectangle.EndPoint.Y;
}
else
{
throw new Exception("请添加矩形!");
}
return r;
}
}
}

View File

@@ -0,0 +1,349 @@
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Filewritable;
using MainShell.Hardware;
using Maxwell.SemiFramework.WaferCalibration.ViewModel;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.SystemCalib;
using MwFramework.Controls.UIControl;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using CameraAxisViewModel = MainShell.Common.Display.ViewModel.CameraAxisViewModel;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class CameraFusionPadCalibViewModel:Screen,IPage
{
private bool _stopMotion = false;
public string Name { get; set; } = "CameraFusionPadCalib";
public string CameraName { get; set; }
private readonly FusionCalibMotionService _fusionCalibMotionService;
private HardwareManager _hardware;
private string _fileSaveDir = Paths.CalibSettingPath;
public string FileSaveDir
{
get { return _fileSaveDir; }
set { SetAndNotify(ref _fileSaveDir, value); }
}
private CameraAxisViewModel _cameraAxisViewModelSevice;
public CameraAxisViewModel CameraAxisViewModelSevice
{
get { return _cameraAxisViewModelSevice; }
set
{
_cameraAxisViewModelSevice = value;
OnPropertyChanged(nameof(CameraAxisViewModelSevice));
}
}
private FusionCalibParItem _fusionCalibParItem = new FusionCalibParItem();
public FusionCalibParItem FusionCalibParItem
{
get { return _fusionCalibParItem; }
set
{
_fusionCalibParItem = value;
OnPropertyChanged(nameof(FusionCalibParItem));
}
}
private FusionCalibParItem _inputParam;
public FusionCalibParItem InputParam
{
get { return _inputParam; }
set { SetAndNotify(ref _inputParam, value); }
}
private FusionCalibParItem _resultParam;
public FusionCalibParItem ResultParam
{
get { return _resultParam; }
set { SetAndNotify(ref _resultParam, value); }
}
//private CameraFusionPadCalibViewModel _service;
//public CameraFusionPadCalibViewModel Service
//{
// get { return _service; }
// set
// {
// _service = value;
// NotifyOfPropertyChange();
// }
//}
private bool _isInitialized=false;
public CameraFusionPadCalibViewModel()
{
}
public CameraFusionPadCalibViewModel(HardwareManager hardware, FusionCalibMotionService fusionCalibMotionService)
{
_hardware = hardware;
_fusionCalibMotionService = fusionCalibMotionService ?? throw new ArgumentNullException(nameof(fusionCalibMotionService));
}
/// <summary>
/// 弹窗专用工厂方法(新增):传入标定参数
/// </summary>
public static CameraFusionPadCalibViewModel Create(
string cameraName, string fileSaveName, List<HardwareDevice> device, FusionCalibParItem param, HardwareManager hardware, FusionCalibMotionService fusionCalibMotionService)
{
var vm = new CameraFusionPadCalibViewModel(hardware,fusionCalibMotionService);
vm.InputParam = param;
vm.ResultParam = param;
vm.Initialize(cameraName, fileSaveName, device, true);
return vm;
}
public void viewLoad()
{
base.OnViewLoaded();
if (_isInitialized)
{
return;
}
if (_hardware != null)
{
_cameraAxisViewModelSevice = new CameraAxisViewModel();
_cameraAxisViewModelSevice.CameraAxisDevices.HardwareDeviceList = _hardware.CameraAxisManager.TopPositionCameraAxisDevices;
NotifyOfPropertyChange(nameof(CameraAxisViewModelSevice));
}
//RebuildModuleViewModels();
_isInitialized = true;
}
public void viewUnLoad()
{
ResultParam = FusionCalibParItem;
}
private void Initialize(string cameraName, string fileSaveName, List<HardwareDevice> device, bool isSignUp)
{
//if (isSignUp)
//{
// IoC.Get<IEventAggregator>().Unsubscribe(this);
// IoC.Get<IEventAggregator>().Subscribe(this);
//}
//CameraName = cameraName;
Name = cameraName;
//FileSaveName = fileSaveName;
var paramList = IoC.Get<IParameterManager>() as IParamList;
//Service = new CameraFusionPadCalibViewModel();
FusionCalibParItem = InputParam;
//string pathSavePath = Path.Combine(FileSaveDir, FileSaveName + ".xml");
//Service.SetParaSavePath(pathSavePath);
//Service.IsShowSolidLine = true;
//Service.ShapeThickness = 1;
//Service.DrawInConcurrency = false;
//Service.IsAxisControlLDBVisible = Visibility.Visible;
}
public void btnMovePhsCalibPosPosition()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
_fusionCalibMotionService.SafeMove(_hardware.Axis_Y1, FusionCalibParItem.MoveAxisPos);
_fusionCalibMotionService.MovePhsAvoidance(FusionCalibParItem.ApproachXPos, FusionCalibParItem.ApproachYPos);
}
catch (Exception ex)
{
}
});
}
public void btnApproachPhsCalibPosition()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
//if (FindPointWSCircle())
//{
FusionCalibParItem.ApproachXPos = _hardware.Axis_X1.State.ActualPos;
FusionCalibParItem.ApproachYPos = _hardware.Axis_Y2.State.ActualPos;
//CameraWaferFusionCalibrationItem.CenterX = _gs.Axis_X21.State.ActualPos;
//CameraWaferFusionCalibrationItem.CenterY = _gs.Axis_Y21.State.ActualPos;
MessageBox.Show($"对齐值X1:{FusionCalibParItem.ApproachXPos},Y2:{FusionCalibParItem.ApproachYPos}");
//}
//else
//{
// MessageBox.Show("对齐失败!");
//}
}
catch (Exception ex)
{
}
});
}
public void btnCalculateCameraPosition()
{
try
{
//起点覆盖标定范围起点往负方向6mm
int offset = -10;
double Y2MinPos = 0;
_hardware.Axis_Y2.GetSoftMel(ref Y2MinPos);
var delx = FusionCalibParItem.ApproachYPos - Y2MinPos + offset;
FusionCalibParItem.CameraAxisPos = FusionCalibParItem.MoveAxisPos - delx;
}
catch (Exception ex)
{
}
}
public void btnSetCameraPosition()
{
var result = MwMessageBox.Show("是否使用当前位置?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
try
{
FusionCalibParItem.CameraAxisPos = _hardware.Axis_Y1.State.ActualPos;
}
catch (Exception ex)
{
}
}
}
public void btnMoveCameraPosition()
{
var result = MwMessageBox.Show("是否移动到该点?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
_fusionCalibMotionService.SafeMove(_hardware.Axis_Y1, FusionCalibParItem.CameraAxisPos);
}
catch (Exception ex)
{
}
});
}
}
public void btnCalculatePhsPosition()
{
try
{
//起点覆盖标定范围起点往负方向6mm
int offset = -10;
double Y2MinPos = 0;
_hardware.Axis_Y2.GetSoftMel(ref Y2MinPos);
var delx = FusionCalibParItem.ApproachXPos - Y2MinPos + offset;
FusionCalibParItem.CameraAxisPos = FusionCalibParItem.MoveAxisPos - delx;
double y11Offset = FusionCalibParItem.CameraAxisPos - FusionCalibParItem.MoveAxisPos;
FusionCalibParItem.StartX = FusionCalibParItem.ApproachXPos;
FusionCalibParItem.StartY = FusionCalibParItem.ApproachYPos + y11Offset;
}
catch (Exception ex)
{
}
}
public void btnSetStageStartPosition()
{
var result = MwMessageBox.Show("是否使用当前位置?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
FusionCalibParItem.StartX = _hardware.Axis_X1.State.ActualPos;
FusionCalibParItem.StartY = _hardware.Axis_Y2.State.ActualPos;
}
catch (Exception ex)
{
}
});
}
}
public void btnMoveStageStartPosition()
{
var result = MwMessageBox.Show("是否移动到该点?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Information);
if (result == MessageBoxResult.Yes)
{
try
{
_fusionCalibMotionService.MovePhsAvoidance(FusionCalibParItem.StartX, FusionCalibParItem.StartY);
}
catch (Exception ex)
{
}
}
}
public void btnMoveToCenter()
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
try
{
//if (ApproachPointWS(1))
//{
// CameraWaferFusionCalibrationItem.WaferStartVerifyX = _gs.Axis_X21.State.ActualPos;
// CameraWaferFusionCalibrationItem.WaferStartVerifyY = _gs.Axis_Y21.State.ActualPos;
// MwMessageBox.Show("中心对齐完成");
//}
//else
//{
// MwMessageBox.Show("逼近失败!");
//}
}
catch (Exception ex)
{
}
});
}
public void btnStart()
{
}
public void btnStop()
{
}
}
}

View File

@@ -0,0 +1,181 @@
using MainShell.Hardware;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.Components;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class DistortionCorrectionCalibContentsViewModel:Screen,IPage
{
private HardwareManager _hardware;
public ICommand PrevCommand { get; set; }
public ICommand NextCommand { get; set; }
private IParameterManager _parameterManager;
public string Name { get; set; } = "DistortionCorrectionCalibContents";
private bool _isInit = false;
private ObservableCollection<Screen> _screens;
/// <summary>
/// 需要显示的界面
/// </summary>
public ObservableCollection<Screen> Screens
{
get { return _screens; }
set
{ SetAndNotify(ref _screens, value); }
}
private Screen _showScreenVM;
/// <summary>
/// 当前显示界面的VM
/// </summary>
public Screen ShowScreenVM
{
get { return _showScreenVM; }
set
{ SetAndNotify(ref _showScreenVM, value); }
}
private string _currentCalibName;
/// <summary>
/// 当前标定名称供UI标定
/// </summary>
public string CurrentCalibName
{
get { return _currentCalibName; }
set { SetAndNotify(ref _currentCalibName, value); }
}
private string _progressText;
/// <summary>
/// 进度提示
/// </summary>
public string ProgressText
{
get { return _progressText; }
set { SetAndNotify(ref _progressText, value); }
}
private int _ShowIndex;
/// <summary>
/// 当前显示界面的下标
/// </summary>
public int ShowIndex
{
get { return _ShowIndex; }
set
{ SetAndNotify(ref _ShowIndex, value); }
}
private bool _isEnablePrev;
/// <summary>
/// 上一个按钮是否启用
/// </summary>
public bool IsEnablePrev
{
get { return _isEnablePrev; }
set
{ SetAndNotify(ref _isEnablePrev, value); }
}
private bool _isEnableNext = true;
/// <summary>
/// 下一个按钮是否启用
/// </summary>
public bool IsEnableNext
{
get { return _isEnableNext; }
set
{ SetAndNotify(ref _isEnableNext, value); }
}
public DistortionCorrectionCalibContentsViewModel(IParameterManager paraManager)
{
_hardware = IoC.Get<HardwareManager>();
this._parameterManager = paraManager;
PrevCommand = new DelegateCommand(() =>
{
OnPrev();
});
NextCommand = new DelegateCommand(() =>
{
OnNext();
});
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (!_isInit)
{
Screens = new ObservableCollection<Screen>();
// ====== 定义5组相机-轴配置 ======
var cameraConfigs = new List<(string Name, List<HardwareDevice> Device)>
{
("上相机畸变标定", _hardware.CameraAxisManager.TopCameraAxisDevices),
("上相机WS畸变标定", _hardware.CameraAxisManager.TopCameraWsAxisDevices),
("广角相机畸变标定", _hardware.CameraAxisManager.WideCameraAxisDevices),
("广角相机WS畸变标定", _hardware.CameraAxisManager.WideCameraWsAxisDevices),
("下相机畸变标定", _hardware.CameraAxisManager.BottomCameraAxisDevices),
};
foreach (var (name, device) in cameraConfigs)
{
Screens.Add(DistortionCorrectionCalibViewModel.Create(name, device));
}
// 默认显示第一个
ShowIndex = 0;
ShowScreenVM = Screens[0];
UpdateNavigationState();
_isInit = true;
}
}
public void viewUnLoad()
{
//_device.ChangeCameraMode(CameraTriggerMode.On);
//_pLCControlOperation.CyliderPress(true);
}
private void OnPrev()
{
if (ShowIndex <= 0) return;
ShowIndex--;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState(); // ← 加上这行
}
private void OnNext()
{
if (ShowIndex >= Screens.Count - 1) return;
ShowIndex++;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState(); // ← 加上这行
}
/// <summary>
/// 统一更新导航状态和当前相机信息
/// </summary>
private void UpdateNavigationState()
{
IsEnablePrev = ShowIndex > 0;
IsEnableNext = ShowIndex < Screens.Count - 1;
// 更新当前相机名称
if (ShowScreenVM is DistortionCorrectionCalibViewModel camVm)
{
CurrentCalibName = camVm.CalibName;
}
// 更新进度
ProgressText = $"{ShowIndex + 1} / {Screens.Count}";
}
}
}

View File

@@ -0,0 +1,71 @@
using MaxwellFramework.Core.Interfaces;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class DistortionCorrectionCalibViewModel:Screen,IPage
{
public string Name { get; set; } = "DistortionCorrectionCalib";
/// <summary>
/// 标定名称标识用于UI显示和逻辑判断
/// </summary>
public string CalibName { get; set; }
private MwFramework.Controls.SystemCalib.DistortionCorrectionCalibViewModel _service;
public MwFramework.Controls.SystemCalib.DistortionCorrectionCalibViewModel Service
{
get { return _service; }
set
{
_service = value;
NotifyOfPropertyChange();
}
}
/// <summary>
/// 无参构造函数
/// </summary>
public DistortionCorrectionCalibViewModel()
{
}
/// <summary>
/// 手动创建时使用的静态工厂方法
/// </summary>
/// <param name="calibName">相机名称,如 "BottomCamera"</param>
/// <param name="device">对应的硬件设备</param>
public static DistortionCorrectionCalibViewModel Create(string calibName, List<HardwareDevice> device)
{
var vm = new DistortionCorrectionCalibViewModel();
vm.Initialize(calibName, device);
return vm;
}
private void Initialize(string calibName, List<HardwareDevice> device)
{
CalibName = calibName;
var paramList = IoC.Get<IParameterManager>() as IParamList;
Service = new MwFramework.Controls.SystemCalib.DistortionCorrectionCalibViewModel(
device,
paramList,
true
);
Service.IsShowSolidLine = true;
Service.ShapeThickness = 1;
Service.DrawInConcurrency = false;
Service.IsAxisControlLDBVisible = Visibility.Visible;
}
}
}

View File

@@ -0,0 +1,448 @@
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Hardware;
using MainShell.Motion;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.ManagerService;
using SemiconductorVisionAlgorithm.SemiParams;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using SemiPoint = SemiconductorVisionAlgorithm.SemiParams.Point;
namespace MainShell.AlgorithmCalib.ViewModel
{
/// <summary>
/// 融合对位验证VM
/// 3步对位流程抓Pad点 → Die网格飞拍 → 选择Die点对位Pad
/// </summary>
public class FusionAlignVerifyViewModel : Screen, IPage
{
private readonly FusionCalibMotionService _motionService;
private readonly HardwareManager _hardware;
private FusionAlignProcessor _alignProcessor;
private CancellationTokenSource _cts;
private ApproachAlignmentService _approachAlignmentService;
public string Name { get; set; } = "FusionAlignVerify";
public string CalibName { get; set; } = "整体对位验证";
#region
private FusionCalibModuleItem _moduleItem = new FusionCalibModuleItem();
public FusionCalibModuleItem ModuleItem
{
get => _moduleItem;
set => SetAndNotify(ref _moduleItem, value);
}
// DieFusionCalibView 绑定
public FusionCalibModuleItem WsFusionCalibParItem
{
get => _moduleItem;
set { _moduleItem = value; NotifyOfPropertyChange(nameof(WsFusionCalibParItem)); }
}
// PadFusionCalibView 绑定
public FusionCalibModuleItem FusionCalibParItem
{
get => _moduleItem;
set { _moduleItem = value; NotifyOfPropertyChange(nameof(FusionCalibParItem)); }
}
private CameraAxisViewModel _cameraAxisViewModelSevice;
public CameraAxisViewModel CameraAxisViewModelSevice
{
get => _cameraAxisViewModelSevice;
set => SetAndNotify(ref _cameraAxisViewModelSevice, value);
}
private DelegateBase _motion = new DelegateBase();
public DelegateBase Motion
{
get => _motion;
set => SetAndNotify(ref _motion, value);
}
private string _progressMessage = "";
public string ProgressMessage
{
get => _progressMessage;
set => SetAndNotify(ref _progressMessage, value);
}
private bool _isRunning;
public bool IsRunning
{
get => _isRunning;
set => SetAndNotify(ref _isRunning, value);
}
private string _alignResultText = "";
public string AlignResultText
{
get => _alignResultText;
set => SetAndNotify(ref _alignResultText, value);
}
// ===== 步骤1Pad捕获 =====
private double _padCapturedX;
public double PadCapturedX
{
get => _padCapturedX;
set => SetAndNotify(ref _padCapturedX, value);
}
private double _padCapturedY;
public double PadCapturedY
{
get => _padCapturedY;
set => SetAndNotify(ref _padCapturedY, value);
}
// ===== 步骤2Die网格飞拍 =====
private double _dieFirstX;
public double DieFirstX
{
get => _dieFirstX;
set => SetAndNotify(ref _dieFirstX, value);
}
private double _dieFirstY;
public double DieFirstY
{
get => _dieFirstY;
set => SetAndNotify(ref _dieFirstY, value);
}
private int _flyScanXCount = 3;
public int FlyScanXCount
{
get => _flyScanXCount;
set => SetAndNotify(ref _flyScanXCount, value);
}
private int _flyScanYCount = 3;
public int FlyScanYCount
{
get => _flyScanYCount;
set => SetAndNotify(ref _flyScanYCount, value);
}
private double _flyScanStep = 1.0;
public double FlyScanStep
{
get => _flyScanStep;
set => SetAndNotify(ref _flyScanStep, value);
}
private string _flyScanResultText = "";
public string FlyScanResultText
{
get => _flyScanResultText;
set => SetAndNotify(ref _flyScanResultText, value);
}
private ObservableCollection<DieGridPointDisplay> _displayDieGridPoints = new ObservableCollection<DieGridPointDisplay>();
public ObservableCollection<DieGridPointDisplay> DisplayDieGridPoints
{
get => _displayDieGridPoints;
set => SetAndNotify(ref _displayDieGridPoints, value);
}
// ===== 步骤3对位选择 =====
private int _selectedRow;
public int SelectedRow
{
get => _selectedRow;
set => SetAndNotify(ref _selectedRow, value);
}
private int _selectedCol;
public int SelectedCol
{
get => _selectedCol;
set => SetAndNotify(ref _selectedCol, value);
}
#endregion
#region
public FusionAlignVerifyViewModel() { }
//public FusionAlignVerifyViewModel(List<HardwareDevice> device,
// HardwareManager hardware,
// FusionCalibMotionService motion)
//{
// _approachAlignmentService = IoC.Get<ApproachAlignmentService>();
// _cameraAxisViewModelSevice = IoC.Get<CameraAxisViewModel>();
// _cameraAxisViewModelSevice.CameraAxisDevices.HardwareDeviceList = device;
// _hardware = hardware;
// _motionService = motion;
// _alignProcessor= new FusionAlignProcessor(motion, hardware, ModuleItem, GetRoiRect);
// _alignProcessor.ProgressChanged += (s, e) =>
// {
// Application.Current.Dispatcher.Invoke(() =>
// {
// ProgressMessage = e.Message;
// });
// };
//}
public FusionAlignVerifyViewModel(HardwareManager hardware, FusionCalibMotionService motion)
{
_hardware = hardware;
_motionService = motion;
_approachAlignmentService = IoC.Get<ApproachAlignmentService>();
}
public static FusionAlignVerifyViewModel Create(
List<HardwareDevice> device,
HardwareManager hardware,
FusionCalibMotionService motion)
{
var vm = new FusionAlignVerifyViewModel(hardware, motion);
vm.CameraAxisViewModelSevice = IoC.Get<CameraAxisViewModel>();
vm.CameraAxisViewModelSevice.CameraAxisDevices.HardwareDeviceList = device;
vm._alignProcessor = new FusionAlignProcessor(motion, hardware, vm.ModuleItem, vm.GetRoiRect);
vm._alignProcessor.ProgressChanged += (s, e) =>
{
Application.Current.Dispatcher.Invoke(() =>
{
vm.ProgressMessage = e.Message;
});
};
return vm;
}
#endregion
#region
/// <summary>
/// 步骤1抓Pad点
/// 框选ROI区域识别Pad目标点记录pad坐标(X1,Y2)
/// </summary>
public void btnCapturePad()
{
if (MwMessageBox.Show("是否在当前位置框选ROI抓Pad点?", "确认", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
return;
_cts = new CancellationTokenSource();
Task.Run(async () =>
{
try
{
IsRunning = true;
await _alignProcessor.CapturePadPointAsync(_cts.Token);
Application.Current.Dispatcher.Invoke(() =>
{
PadCapturedX = _moduleItem.PadRulerX;
PadCapturedY = _moduleItem.PadRulerY;
});
MwMessageBox.Show($"抓Pad点完成\nPad: ({PadCapturedX:F4}, {PadCapturedY:F4})");
}
catch (OperationCanceledException) { MwMessageBox.Show("已取消"); }
catch (Exception ex) { MwMessageBox.Show($"抓Pad点失败{ex.Message}"); }
finally { IsRunning = false; }
});
}
/// <summary>
/// 步骤2a抓Die首点
/// 框选ROI区域识别Die首点坐标(X2,Y1)
/// </summary>
public void btnCaptureDieFirst()
{
if (MwMessageBox.Show("是否在当前位置框选ROI抓Die首点?", "确认", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
return;
_cts = new CancellationTokenSource();
Task.Run(async () =>
{
try
{
IsRunning = true;
await _alignProcessor.CaptureDieFirstPointAsync(_cts.Token);
Application.Current.Dispatcher.Invoke(() =>
{
DieFirstX = _moduleItem.DieRulerX;
DieFirstY = _moduleItem.DieRulerY;
});
MwMessageBox.Show($"抓Die首点完成\nDie: ({DieFirstX:F4}, {DieFirstY:F4})");
}
catch (OperationCanceledException) { MwMessageBox.Show("已取消"); }
catch (Exception ex) { MwMessageBox.Show($"抓Die首点失败{ex.Message}"); }
finally { IsRunning = false; }
});
}
/// <summary>
/// 步骤2b执行Die网格飞拍
/// 以Die首点为起点按step和count执行蛇形网格飞拍
/// </summary>
public void btnStartFlyScan()
{
if (FlyScanXCount <= 0 || FlyScanYCount <= 0)
{
MwMessageBox.Show("请设置有效的X Count和Y Count需大于0");
return;
}
if (FlyScanStep <= 0)
{
MwMessageBox.Show("请设置有效的Step需大于0");
return;
}
if (MwMessageBox.Show($"是否以首点({DieFirstX:F4}, {DieFirstY:F4})为起点开始飞拍?\n" +
$"网格:{FlyScanXCount}×{FlyScanYCount},步长:{FlyScanStep}mm",
"确认", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
return;
// 将飞拍参数同步到processor
_alignProcessor.SetFlyScanParams(DieFirstX, DieFirstY, FlyScanXCount, FlyScanYCount, FlyScanStep);
_cts = new CancellationTokenSource();
Task.Run(async () =>
{
try
{
IsRunning = true;
await _alignProcessor.ExecuteDieGridFlyScanAsync(_cts.Token);
Application.Current.Dispatcher.Invoke(() =>
{
FlyScanResultText = $"飞拍完成,共获取 {_moduleItem.DieGridPoints.Count} 个Die点";
DisplayDieGridPoints.Clear();
for (int i = 0; i < _moduleItem.DieGridPoints.Count; i++)
{
var pt = _moduleItem.DieGridPoints[i];
DisplayDieGridPoints.Add(new DieGridPointDisplay
{
Row = pt.Item1,
Col = pt.Item2,
X = pt.Item3.X,
Y = pt.Item3.Y
});
}
});
MwMessageBox.Show($"飞拍完成,共获取 {_moduleItem.DieGridPoints.Count} 个Die点");
}
catch (OperationCanceledException) { MwMessageBox.Show("已取消"); }
catch (Exception ex) { MwMessageBox.Show($"飞拍失败:{ex.Message}"); }
finally { IsRunning = false; }
});
}
/// <summary>
/// 步骤3执行对位
/// 用户选择网格中的一个Die点与Pad点执行对位
/// </summary>
public void btnExecuteAlign()
{
if (_moduleItem.DieGridPoints == null || _moduleItem.DieGridPoints.Count == 0)
{
MwMessageBox.Show("请先执行Die网格飞拍");
return;
}
if (SelectedRow < 0 || SelectedCol < 0)
{
MwMessageBox.Show("请输入有效的行列号从0开始");
return;
}
bool found = _moduleItem.DieGridPoints.Any(p => p.Item1 == SelectedRow && p.Item2 == SelectedCol);
if (!found)
{
MwMessageBox.Show($"未找到第{SelectedRow}行第{SelectedCol}列的Die点请检查输入");
return;
}
if (MwMessageBox.Show($"是否将Die[{SelectedRow},{SelectedCol}]与Pad({PadCapturedX:F4}, {PadCapturedY:F4})对位?",
"确认", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
return;
_cts = new CancellationTokenSource();
Task.Run(async () =>
{
try
{
IsRunning = true;
await _alignProcessor.ExecuteAlignAsync(_cts.Token, SelectedRow, SelectedCol);
Application.Current.Dispatcher.Invoke(() =>
{
AlignResultText = $"选择Die点: [{SelectedRow},{SelectedCol}]\n" +
$"Die坐标(X2,Y1): ({_moduleItem.DieRealX:F4}, {_moduleItem.DieRealY:F4})\n" +
$"Pad坐标(X1,Y2): ({_moduleItem.PadRealX:F4}, {_moduleItem.PadRealY:F4})\n" +
$"对齐后 X1: {_moduleItem.AlignResultX:F4}\n" +
$"对齐后 Y1: {_moduleItem.AlignResultY:F4}\n" +
$"X2补偿: {_moduleItem.AlignResultX2:F4}";
});
MwMessageBox.Show("对位完成!");
}
catch (OperationCanceledException) { MwMessageBox.Show("已取消"); }
catch (Exception ex) { MwMessageBox.Show($"对位失败:{ex.Message}"); }
finally { IsRunning = false; }
});
}
private Rectangle1 GetRoiRect()
{
try
{
var region = CameraAxisViewModelSevice?.Regions;
if (region?.Count > 0 && region[0].Region is RectRegion rect)
{
return new Rectangle1
{
Start_X = rect.CenterPoint.Y - rect.Height / 2,
Start_Y = rect.CenterPoint.X - rect.Width / 2,
End_X = rect.CenterPoint.Y + rect.Height / 2,
End_Y = rect.CenterPoint.X + rect.Width / 2
};
}
}
catch { }
return null;
}
/// <summary>
/// 停止
/// </summary>
public void btnStopAlign()
{
_cts?.Cancel();
}
#endregion
}
/// <summary>
/// Die网格点显示模型用于DataGrid绑定
/// </summary>
public class DieGridPointDisplay
{
public int Row { get; set; }
public int Col { get; set; }
public double X { get; set; }
public double Y { get; set; }
}
}

View File

@@ -0,0 +1,155 @@
using MainShell.AlgorithmCalib.Service;
using MainShell.Hardware;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.Components;
using MwFramework.ManagerService;
using Stylet;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
namespace MainShell.AlgorithmCalib.ViewModel
{
/// <summary>
/// 融合标定页面主VM对标 MotionCalibContentsViewModel
/// 管理 Die融合 和 Pad融合 两个子页面
/// </summary>
public class FusionCalibContentsViewModel : Screen, IPage
{
private readonly HardwareManager _hardware;
private readonly FusionCalibMotionService _fusionCalibMotionService;
private bool _isInit = false;
public string Name { get; set; } = "FusionCalib";
public ICommand PrevCommand { get; set; }
public ICommand NextCommand { get; set; }
private ObservableCollection<Screen> _screens;
public ObservableCollection<Screen> Screens
{
get => _screens;
set => SetAndNotify(ref _screens, value);
}
private Screen _showScreenVM;
public Screen ShowScreenVM
{
get => _showScreenVM;
set => SetAndNotify(ref _showScreenVM, value);
}
private string _currentCalibName;
public string CurrentCalibName
{
get => _currentCalibName;
set => SetAndNotify(ref _currentCalibName, value);
}
private string _progressText;
public string ProgressText
{
get => _progressText;
set => SetAndNotify(ref _progressText, value);
}
private int _showIndex;
public int ShowIndex
{
get => _showIndex;
set => SetAndNotify(ref _showIndex, value);
}
private bool _isEnablePrev;
public bool IsEnablePrev
{
get => _isEnablePrev;
set => SetAndNotify(ref _isEnablePrev, value);
}
private bool _isEnableNext = true;
public bool IsEnableNext
{
get => _isEnableNext;
set => SetAndNotify(ref _isEnableNext, value);
}
public FusionCalibContentsViewModel()
{
_hardware = IoC.Get<HardwareManager>();
_fusionCalibMotionService = IoC.Get<FusionCalibMotionService>();
PrevCommand = new DelegateCommand(() => OnPrev());
NextCommand = new DelegateCommand(() => OnNext());
}
protected override void OnViewLoaded()
{
if (_isInit) return;
Screens = new ObservableCollection<Screen>();
// Pad融合多区域打开子窗口
Screens.Add(FusionMultipleCalibViewModel.Create(
"Pad融合标定", "topCameraFusion",
_hardware.CameraAxisManager.TopCameraAxisDevices,
_hardware, _fusionCalibMotionService));
// Die融合单页面
Screens.Add(FusionCalibViewModel.Create(
"Die融合标定", "topWsCameraFusion",
_hardware.CameraAxisManager.TopCameraWsAxisDevices,
_hardware, _fusionCalibMotionService, isWaferCalib: true));
// 融合标定验证
Screens.Add(FusionAlignVerifyViewModel.Create(
_hardware.CameraAxisManager.TopCameraWsAxisDevices,
_hardware, _fusionCalibMotionService));
ShowIndex = 0;
ShowScreenVM = Screens[0];
UpdateNavigationState();
_isInit = true;
}
public void viewUnLoad()
{
if (Screens != null)
{
foreach (var screen in Screens)
{
if (screen is FusionMultipleCalibViewModel menuVm)
menuVm.CleanupCalibWindows();
}
}
}
private void OnPrev()
{
if (ShowIndex <= 0) return;
ShowIndex--;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState();
}
private void OnNext()
{
if (ShowIndex >= Screens.Count - 1) return;
ShowIndex++;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState();
}
private void UpdateNavigationState()
{
IsEnablePrev = ShowIndex > 0;
IsEnableNext = ShowIndex < Screens.Count - 1;
if (ShowScreenVM is FusionMultipleCalibViewModel multiVm)
CurrentCalibName = multiVm.CalibName;
if (ShowScreenVM is FusionCalibViewModel calibVm)
CurrentCalibName = calibVm.CalibName;
ProgressText = $"{ShowIndex + 1} / {Screens.Count}";
}
}
}

View File

@@ -0,0 +1,728 @@
using MainShell.AlgorithmCalib.Common;
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Hardware;
using MainShell.Motion;
using MainShell.PageCalib.OriginCalib.Service;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.Device;
using MwFramework.ManagerService;
using SemiconductorVisionAlgorithm.SemiParams;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using CameraAxisViewModel = MainShell.Common.Display.ViewModel.CameraAxisViewModel;
using SemiPoint = SemiconductorVisionAlgorithm.SemiParams.Point;
namespace MainShell.AlgorithmCalib.ViewModel
{
/// <summary>
/// 融合标定子窗口VMDie/Pad共用
/// 对标 MotionCalibViewModel
/// </summary>
public class FusionCalibViewModel : Screen, IPage
{
private readonly FusionCalibMotionService _fusionCalibMotionService;
private readonly HardwareManager _hardware;
private CancellationTokenSource _cts;
public string Name { get; set; } = "FusionCalib";
public string CalibName { get; set; }
public string CameraName { get; set; }
public string FileSaveDir { get; set; }
// 是否Wafer标定true=Die用WS轴, false=Pad用PHS轴
public bool IsWaferCalib { get; private set; }
#region
private FusionCalibModuleItem _moduleItem = new FusionCalibModuleItem();
public FusionCalibModuleItem ModuleItem
{
get => _moduleItem;
set => SetAndNotify(ref _moduleItem, value);
}
// PadFusionCalibView 绑定这个
public FusionCalibModuleItem FusionCalibParItem
{
get => _moduleItem;
set { _moduleItem = value; NotifyOfPropertyChange(nameof(FusionCalibParItem)); }
}
// DieFusionCalibView 绑定这个
public FusionCalibModuleItem WsFusionCalibParItem
{
get => _moduleItem;
set { _moduleItem = value; NotifyOfPropertyChange(nameof(WsFusionCalibParItem)); }
}
private CameraAxisViewModel _cameraAxisViewModelSevice;
public CameraAxisViewModel CameraAxisViewModelSevice
{
get => _cameraAxisViewModelSevice;
set => SetAndNotify(ref _cameraAxisViewModelSevice, value);
}
private DelegateBase _motion = new DelegateBase();
public DelegateBase Motion
{
get => _motion;
set => SetAndNotify(ref _motion, value);
}
private FusionCalibPostProcessor _postProcessor;
public FusionCalibPostProcessor PostProcessor
{
get => _postProcessor;
set => SetAndNotify(ref _postProcessor, value);
}
private ApproachAlignmentService _approachAlignmentService;
public ApproachAlignmentService ApproachAlignmentService
{
get => _approachAlignmentService;
set => SetAndNotify(ref _approachAlignmentService, value);
}
private bool _isCalibrating;
public bool IsCalibrating
{
get => _isCalibrating;
set { SetAndNotify(ref _isCalibrating, value); NotifyOfPropertyChange(nameof(CanStartCalib)); }
}
public bool CanStartCalib => !IsCalibrating;
#endregion
#region
private string _progressMessage = "";
public string ProgressMessage
{
get => _progressMessage;
set => SetAndNotify(ref _progressMessage, value);
}
private int _progressCurrent;
public int ProgressCurrent
{
get => _progressCurrent;
set => SetAndNotify(ref _progressCurrent, value);
}
private int _progressTotal;
public int ProgressTotal
{
get => _progressTotal;
set
{
SetAndNotify(ref _progressTotal, value);
NotifyOfPropertyChange(nameof(ProgressPercentage));
NotifyOfPropertyChange(nameof(ProgressText));
}
}
public double ProgressPercentage => ProgressTotal > 0 ? (double)ProgressCurrent / ProgressTotal * 100 : 0;
public string ProgressText => ProgressTotal > 0 ? $"{ProgressCurrent}/{ProgressTotal}" : "";
private bool _isProgressVisible;
public bool IsProgressVisible
{
get => _isProgressVisible;
set => SetAndNotify(ref _isProgressVisible, value);
}
#endregion
#region &
public FusionCalibViewModel() { }
public FusionCalibViewModel(HardwareManager hardware, FusionCalibMotionService motion)
{
_hardware = hardware;
_fusionCalibMotionService = motion;
_approachAlignmentService = IoC.Get<ApproachAlignmentService>();
}
/// <summary>
/// Die融合用单页面模式
/// </summary>
public static FusionCalibViewModel Create(string calibName, string saveFileName,
List<HardwareDevice> device, HardwareManager hardware,
FusionCalibMotionService motion, bool isWaferCalib)
{
var vm = new FusionCalibViewModel(hardware, motion);
vm.CalibName = calibName;
vm.FileSaveDir = saveFileName;
vm.IsWaferCalib = isWaferCalib;
vm.CameraAxisViewModelSevice = IoC.Get<CameraAxisViewModel>();
vm.CameraAxisViewModelSevice.CameraAxisDevices.HardwareDeviceList = device;
vm.ModuleItem = new FusionCalibModuleItem
{
ModuleName = isWaferCalib ? FusionCalibModuleNames.DieFusion : FusionCalibModuleNames.PadFusion,
Camera_ID = hardware.TopWsCameraExtend.Id ,
IsWaferCalib = isWaferCalib,
CalibStep = 0.4,
CalibCount = 10,
Count = 4,
Step = 5.0,
};
vm.PostProcessor = new FusionCalibPostProcessor(motion, hardware, vm.ModuleItem, vm.GetRoiRect);
vm.SubscribeToProgress();
return vm;
}
/// <summary>
/// Pad融合用子窗口模式
/// </summary>
public static FusionCalibViewModel Create(string calibName,
List<HardwareDevice> device, FusionCalibModuleItem param,
HardwareManager hardware, FusionCalibMotionService motion, bool isWaferCalib)
{
var vm = new FusionCalibViewModel(hardware, motion);
vm.CalibName = calibName;
vm.CameraName = hardware.TopCameraExtend.Id;
vm.IsWaferCalib = isWaferCalib;
vm.ModuleItem = param;
vm.CameraAxisViewModelSevice = IoC.Get<CameraAxisViewModel>();
vm.CameraAxisViewModelSevice.CameraAxisDevices.HardwareDeviceList = device;
vm.PostProcessor = new FusionCalibPostProcessor(motion, hardware, param, vm.GetRoiRect);
vm.SubscribeToProgress();
return vm;
}
#endregion
#region
private void SubscribeToProgress()
{
if (PostProcessor != null) PostProcessor.ProgressChanged += OnProgressChanged;
}
private void UnsubscribeFromProgress()
{
if (PostProcessor != null) PostProcessor.ProgressChanged -= OnProgressChanged;
}
private void OnProgressChanged(object sender, CalibrationProgressEventArgs e)
{
Application.Current.Dispatcher.Invoke(() =>
{
ProgressMessage = e.Message;
ProgressCurrent = e.Current;
ProgressTotal = e.Total;
NotifyOfPropertyChange(nameof(ProgressPercentage));
NotifyOfPropertyChange(nameof(ProgressText));
});
}
#endregion
#region Die/Pad共用逻辑XAML通过不同View绑定
public void btnMoveWSCalibPosPosition() => MoveToCalibPos();
public void btnMovePhsCalibPosPosition() => MoveToCalibPos();
private void MoveToCalibPos()
{
Task.Run(() =>
{
try
{
if (IsWaferCalib)
{
// Die: 移动X1到标定位置然后移动X2,Y1到逼近位置
_fusionCalibMotionService.SafeMove(_hardware.Axis_X1, ModuleItem.MoveAxisPos);
_fusionCalibMotionService.MoveWsAvoidance(ModuleItem.ApproachXPos, ModuleItem.ApproachYPos);
}
else
{
// Pad: 移动Y1到标定位置然后移动X1,Y2到逼近位置
_fusionCalibMotionService.SafeMove(_hardware.Axis_Y1, ModuleItem.MoveAxisPos);
_fusionCalibMotionService.MovePhsAvoidance(ModuleItem.ApproachXPos, ModuleItem.ApproachYPos);
}
}
catch (Exception ex) { MwMessageBox.Show($"移动失败:{ex.Message}"); }
});
}
public void btnApproachWSCalibPosition() => DoApproach();
public void btnApproachPhsCalibPosition() => DoApproach();
private void DoApproach()
{
Task.Run(async () =>
{
try
{
bool success = await FindPointCircleAsync(CancellationToken.None);
if (success)
{
if (IsWaferCalib)
{
// Die: 读取当前X2,Y1位置作为逼近值
ModuleItem.ApproachXPos = _hardware.Axis_X2.State.ActualPos;
ModuleItem.ApproachYPos = _hardware.Axis_Y1.State.ActualPos;
}
else
{
// Pad: 读取当前X1,Y2位置作为逼近值
ModuleItem.ApproachXPos = _hardware.Axis_X1.State.ActualPos;
ModuleItem.ApproachYPos = _hardware.Axis_Y2.State.ActualPos;
}
MwMessageBox.Show($"对齐值X:{ModuleItem.ApproachXPos},Y:{ModuleItem.ApproachYPos}");
}
else
{
MwMessageBox.Show("对齐失败!");
}
}
catch (Exception ex) { MwMessageBox.Show($"异常:{ex.Message}"); }
});
}
public void btnCalculateCameraPosition()
{
try
{
int offset = -10;
double minPos = 0;
if (IsWaferCalib)
{
// Die: 计算X1相机轴起始位置
_hardware.Axis_X2.GetSoftMel(ref minPos);
var delx = ModuleItem.ApproachXPos - minPos + offset;
ModuleItem.CameraAxisPos = ModuleItem.MoveAxisPos - delx;
}
else
{
// Pad: 计算Y1相机轴起始位置
_hardware.Axis_Y2.GetSoftMel(ref minPos);
var delx = ModuleItem.ApproachYPos - minPos + offset;
ModuleItem.CameraAxisPos = ModuleItem.MoveAxisPos - delx;
}
}
catch (Exception ex) { MwMessageBox.Show($"计算失败:{ex.Message}"); }
}
public void btnSetCameraPosition()
{
if (MwMessageBox.Show("是否使用当前位置?", "确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
// Die: 读取X1当前位置 Pad: 读取Y1当前位置
ModuleItem.CameraAxisPos = IsWaferCalib
? _hardware.Axis_X1.State.ActualPos
: _hardware.Axis_Y1.State.ActualPos;
}
}
public void btnMoveCameraPosition()
{
if (MwMessageBox.Show("是否移动到该点?", "确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Task.Run(() =>
{
try
{
// Die: 移动X1 Pad: 移动Y1
if (IsWaferCalib)
_fusionCalibMotionService.SafeMove(_hardware.Axis_X1, ModuleItem.CameraAxisPos);
else
_fusionCalibMotionService.SafeMove(_hardware.Axis_Y1, ModuleItem.CameraAxisPos);
}
catch (Exception ex) { MwMessageBox.Show($"移动失败:{ex.Message}"); }
});
}
}
public void btnCalculateWSPosition() => CalculateStartPositions();
public void btnCalculatePhsPosition() => CalculateStartPositions();
private void CalculateStartPositions()
{
try
{
int offset = -10;
double minPos = 0;
if (IsWaferCalib)
{
// Die: 计算WS起始位置
_hardware.Axis_X2.GetSoftMel(ref minPos);
var delx = ModuleItem.ApproachXPos - minPos + offset;
ModuleItem.CameraAxisPos = ModuleItem.MoveAxisPos - delx;
double x11Offset = ModuleItem.CameraAxisPos - ModuleItem.MoveAxisPos;
ModuleItem.StartX = ModuleItem.ApproachXPos + x11Offset;
ModuleItem.StartY = ModuleItem.ApproachYPos;
}
else
{
// Pad: 计算PHS起始位置
_hardware.Axis_Y2.GetSoftMel(ref minPos);
var delx = ModuleItem.ApproachYPos - minPos + offset;
ModuleItem.CameraAxisPos = ModuleItem.MoveAxisPos - delx;
double y11Offset = ModuleItem.CameraAxisPos - ModuleItem.MoveAxisPos;
ModuleItem.StartX = ModuleItem.ApproachXPos;
ModuleItem.StartY = ModuleItem.ApproachYPos + y11Offset;
}
}
catch (Exception ex) { MwMessageBox.Show($"计算失败:{ex.Message}"); }
}
public void btnSetWaferStartPosition() => SetCurrentPos();
public void btnSetStageStartPosition() => SetCurrentPos();
private void SetCurrentPos()
{
if (MwMessageBox.Show("是否使用当前位置?", "确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
Task.Run(() =>
{
if (IsWaferCalib)
{
// Die: 读取X2,Y1当前位置
ModuleItem.StartX = _hardware.Axis_X2.State.ActualPos;
ModuleItem.StartY = _hardware.Axis_Y1.State.ActualPos;
}
else
{
// Pad: 读取X1,Y2当前位置
ModuleItem.StartX = _hardware.Axis_X1.State.ActualPos;
ModuleItem.StartY = _hardware.Axis_Y2.State.ActualPos;
}
});
}
}
public void btnMoveWaferStartPosition() => MoveToStart();
public void btnMoveStageStartPosition() => MoveToStart();
private void MoveToStart()
{
if (MwMessageBox.Show("是否移动到该点?", "确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
if (IsWaferCalib)
_fusionCalibMotionService.MoveWsAvoidance(ModuleItem.StartX, ModuleItem.StartY);
else
_fusionCalibMotionService.MovePhsAvoidance(ModuleItem.StartX, ModuleItem.StartY);
}
}
public void btnMoveToCenter()
{
Task.Run(() =>
{
try
{
if (IsWaferCalib)
{
ModuleItem.WaferStartVerifyX = _hardware.Axis_X2.State.ActualPos;
ModuleItem.WaferStartVerifyY = _hardware.Axis_Y1.State.ActualPos;
}
else
{
ModuleItem.WaferStartVerifyX = _hardware.Axis_X1.State.ActualPos;
ModuleItem.WaferStartVerifyY = _hardware.Axis_Y2.State.ActualPos;
}
MwMessageBox.Show("设置起点完成");
}
catch (Exception ex) { MwMessageBox.Show($"设置失败:{ex.Message}"); }
});
}
#endregion
#region
private bool _stopVerifyMotion = false;
public void btnMoveToStartPoint()
{
if (MwMessageBox.Show("是否移动到验证起点?", "确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
try
{
if (IsWaferCalib)
_fusionCalibMotionService.MoveWsAvoidance(ModuleItem.WaferStartVerifyX, ModuleItem.WaferStartVerifyY);
else
_fusionCalibMotionService.MovePhsAvoidance(ModuleItem.WaferStartVerifyX, ModuleItem.WaferStartVerifyY);
}
catch (Exception ex) { MwMessageBox.Show($"移动失败:{ex.Message}"); }
}
}
public void btnMoveWafer()
{
ModuleItem.RulerPoints = new List<SemiPoint>();
ModuleItem.RealPoints = new List<SemiPoint>();
Task.Run(() =>
{
try
{
_stopVerifyMotion = false;
for (int i = 0; i < ModuleItem.WaferVerityCountY; i++)
{
for (int j = 0; j < ModuleItem.WaferVerityCountX; j++)
{
if (_stopVerifyMotion)
{
_stopVerifyMotion = false;
return;
}
double yPos = ModuleItem.WaferStartVerifyY + i * ModuleItem.WaferVerityStep;
double xPos;
if (i % 2 == 0)
xPos = ModuleItem.WaferStartVerifyX + j * ModuleItem.WaferVerityStep;
else
xPos = ModuleItem.WaferStartVerifyX + (ModuleItem.WaferVerityCountX - j - 1) *
ModuleItem.WaferVerityStep;
if (IsWaferCalib)
_fusionCalibMotionService.MoveWsAvoidance(xPos, yPos);
else
_fusionCalibMotionService.MovePhsAvoidance(xPos, yPos);
// TODO: 调用逼近识别 ApproachPointCircle
if (IsWaferCalib)
{
SemiPoint ruler = new SemiPoint(_hardware.Axis_X2.State.ActualPos,
_hardware.Axis_Y1.State.ActualPos);
ModuleItem.RulerPoints.Add(ruler);
}
else
{
SemiPoint ruler = new SemiPoint(_hardware.Axis_X1.State.ActualPos,
_hardware.Axis_Y2.State.ActualPos);
ModuleItem.RulerPoints.Add(ruler);
}
}
}
MwMessageBox.Show("飞拍结束");
}
catch (Exception ex) { MwMessageBox.Show($"飞拍失败:{ex.Message}"); }
});
}
public void btnVerityWafer()
{
if (ModuleItem.RulerPoints == null || ModuleItem.RulerPoints.Count == 0)
{
MwMessageBox.Show("请先执行飞拍!");
return;
}
if (MwMessageBox.Show("是否已移动到对应位置,确认开始验证?", "确认", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
return;
Task.Run(() =>
{
try
{
_stopVerifyMotion = false;
List<SemiPoint> rulerThenList = new List<SemiPoint>();
List<SemiPoint> rulerNowList = new List<SemiPoint>();
for (int i = 0; i < ModuleItem.RulerPoints.Count; i++)
{
if (_stopVerifyMotion)
{
_stopVerifyMotion = false;
return;
}
// TODO: 使用融合位置表计算期望坐标
SemiPoint rulerPoint = ModuleItem.RulerPoints[i]; // 暂时直接使用飞拍点
rulerThenList.Add(rulerPoint);
if (IsWaferCalib)
_fusionCalibMotionService.MoveWsAvoidance(rulerPoint.X, rulerPoint.Y);
else
_fusionCalibMotionService.MovePhsAvoidance(rulerPoint.X, rulerPoint.Y);
// TODO: 逼近识别,获取实际坐标
if (IsWaferCalib)
{
SemiPoint rulerNow = new SemiPoint(_hardware.Axis_X2.State.ActualPos, _hardware.Axis_Y1.State.ActualPos);
rulerNowList.Add(rulerNow);
}
else
{
SemiPoint rulerNow = new SemiPoint(_hardware.Axis_X1.State.ActualPos, _hardware.Axis_Y2.State.ActualPos);
rulerNowList.Add(rulerNow);
}
}
// TODO: 输出验证结果
// 对应旧项目: JM1Manager.Instance.pos_verify(filePath, rulerThenList, rulerNowList)
double totalError = 0;
for (int i = 0; i < rulerThenList.Count; i++)
{
double dx = rulerThenList[i].X - rulerNowList[i].X;
double dy = rulerThenList[i].Y - rulerNowList[i].Y;
totalError += Math.Sqrt(dx * dx + dy * dy);
}
double avgError = rulerThenList.Count > 0 ? totalError / rulerThenList.Count : 0;
MwMessageBox.Show($"验证完成!平均误差: {avgError:F4}mm");
}
catch (Exception ex) { MwMessageBox.Show($"验证失败:{ex.Message}"); }
});
}
public void btnStopVerityWafer()
{
var result = MwMessageBox.Show("是否停止验证?", "确认停止", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
_stopVerifyMotion = true;
}
}
#endregion
#region
public async Task StartCalib()
{
if (IsCalibrating) return;
IsCalibrating = true;
IsProgressVisible = true;
_cts = new CancellationTokenSource();
try
{
if (PostProcessor != null)
await PostProcessor.ExecuteAsync(null, _cts.Token);
MwMessageBox.Show("标定完成!");
}
catch (OperationCanceledException)
{
MwMessageBox.Show("标定已取消。");
}
catch (Exception ex)
{
MwMessageBox.Show($"标定失败:{ex.Message}");
}
finally
{
IsCalibrating = false;
IsProgressVisible = false;
}
}
public void btnStart() => _ = StartCalib();
public void btnStop() => _cts?.Cancel();
#endregion
#region ROI
private Rectangle1 GetRoiRect()
{
try
{
var region = CameraAxisViewModelSevice?.Regions;
if (region?.Count > 0 && region[0].Region is RectRegion rect)
{
return new Rectangle1
{
Start_X = rect.CenterPoint.Y - rect.Height / 2,
Start_Y = rect.CenterPoint.X - rect.Width / 2,
End_X = rect.CenterPoint.Y + rect.Height / 2,
End_Y = rect.CenterPoint.X + rect.Width / 2
};
}
}
catch { }
return null;
}
#endregion
private async Task<bool> FindPointCircleAsync(CancellationToken token)
{
var roiRect = GetRoiRect();
if (IsWaferCalib)
{
var request = new ApproachAlignmentRequest(
axes: new[]
{
new ApproachAlignmentAxis(_hardware.Axis_X2.Name, 0.005),
new ApproachAlignmentAxis(_hardware.Axis_Y1.Name, 0.005)
},
camera: CameraType.TopPositionCamera)
{
MaxIterations = 3,
RecognitionTimeoutMilliseconds = 5000,
RecognitionParameters = new CenterRecognitionParameters()
{
Type = CenterRecognitionType.EdgeCircle,
rectangle = roiRect
}
};
var result = await _approachAlignmentService
.ApproachAlignmentAsync(request, token);
return result.Succeeded;
}
else
{
var request = new ApproachAlignmentRequest(
axes: new[]
{
new ApproachAlignmentAxis(_hardware.Axis_X1.Name, 0.005),
new ApproachAlignmentAxis(_hardware.Axis_Y2.Name, 0.005)
},
camera: CameraType.TopPositionCamera)
{
MaxIterations = 3,
RecognitionTimeoutMilliseconds = 5000,
RecognitionParameters = new CenterRecognitionParameters()
{
Type = CenterRecognitionType.EdgeCircle,
rectangle = roiRect
}
};
var result = await _approachAlignmentService
.ApproachAlignmentAsync(request, token);
return result.Succeeded;
}
}
private static string GetAxisName(IAxis axis)
{
if (axis == null)
{
return string.Empty;
}
return axis.Name;
}
protected override void OnDeactivate()
{
UnsubscribeFromProgress();
base.OnDeactivate();
}
}
}

View File

@@ -0,0 +1,354 @@
using JM1.VisionModule;
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.AlgorithmCalib.View;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Filewritable;
using MainShell.Hardware;
using MaxwellFramework.Core.Interfaces;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.AlgorithmCalib.ViewModel
{
/// <summary>
/// Pad融合标定 - 多区域管理VM对标 MotionMultipleCalibViewModel
/// 管理4组标定区域每组可打开子窗口标定
/// </summary>
public class FusionMultipleCalibViewModel : Screen, IPage
{
#region &
private const int CalibAreaCount = 4;
private const string PointsFileExt = ".csv";
private const string AreaFilePrefix = "Fusion_Time";
private readonly HashSet<string> _forceCloseIds = new HashSet<string>();
private readonly Dictionary<string, Window> _calibWindows = new Dictionary<string, Window>();
private bool _isDataLoaded = false;
#endregion
#region
public string Name { get; set; } = "FusionMultipleCalib";
public string CalibName { get; set; }
public string FileSaveDir { get; set; }
private CameraAxisViewModel _cameraAxisViewModelService;
public CameraAxisViewModel CameraAxisViewModelService
{
get => _cameraAxisViewModelService;
set => SetAndNotify(ref _cameraAxisViewModelService, value);
}
private ObservableCollection<FusionCalibModuleItem> _calibItems = new
ObservableCollection<FusionCalibModuleItem>();
public ObservableCollection<FusionCalibModuleItem> CalibItems
{
get => _calibItems;
set => SetAndNotify(ref _calibItems, value);
}
private FusionCalibModuleItem _selectedItem;
public FusionCalibModuleItem SelectedItem
{
get => _selectedItem;
set => SetAndNotify(ref _selectedItem, value);
}
private FusionCalibSetting _setting;
private List<HardwareDevice> _hardwareDevice;
public List<HardwareDevice> HardwareDevice
{
get => _hardwareDevice;
set => SetAndNotify(ref _hardwareDevice, value);
}
private HardwareManager _hardware;
public HardwareManager Hardware
{
get => _hardware;
set => SetAndNotify(ref _hardware, value);
}
private FusionCalibMotionService _fusionCalibMotionService;
#endregion
#region &
public FusionMultipleCalibViewModel()
{
_cameraAxisViewModelService = IoC.Get<CameraAxisViewModel>();
}
public static FusionMultipleCalibViewModel Create(string calibName, string saveFileName,
List<HardwareDevice> device, HardwareManager hardware, FusionCalibMotionService motion)
{
var vm = new FusionMultipleCalibViewModel();
vm.Initialize(calibName, saveFileName, device, hardware, motion);
return vm;
}
#endregion
#region &
private void Initialize(string calibName, string saveFileName,
List<HardwareDevice> device, HardwareManager hardware, FusionCalibMotionService motion)
{
CalibName = calibName;
Hardware = hardware;
HardwareDevice = device;
_fusionCalibMotionService = motion;
FileSaveDir = Path.Combine(Paths.CalibSettingPath, "fusionCalib");
_setting = FusionCalibSetting.LoadOrCreate();
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (_hardware != null)
{
_cameraAxisViewModelService.CameraAxisDevices.HardwareDeviceList = HardwareDevice;
NotifyOfPropertyChange(nameof(CameraAxisViewModelService));
}
if (!_isDataLoaded)
{
ReadCalibData();
_isDataLoaded = true;
}
}
#endregion
#region
public void btnOpenCalibWindow()
{
if (SelectedItem == null)
{
MwMessageBox.Show("请先选择一组标定数据。", "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
string calibId = SelectedItem.Camera_ID;
if (_calibWindows.TryGetValue(calibId, out var existingWindow))
{
if (existingWindow.WindowState == WindowState.Minimized)
existingWindow.WindowState = WindowState.Normal;
existingWindow.Show();
existingWindow.Activate();
return;
}
// 创建子窗口 VMPad模式
var calibVM = FusionCalibViewModel.Create(
CalibName, HardwareDevice, SelectedItem, Hardware, _fusionCalibMotionService, isWaferCalib:
false);
calibVM.FileSaveDir = FileSaveDir;
var window = CreateCalibWindow(calibId, calibVM);
_calibWindows[calibId] = window;
window.Show();
}
private Window CreateCalibWindow(string calibId, FusionCalibViewModel calibVM)
{
var calibView = new PadFusionCalibView { DataContext = calibVM };
var window = new Window
{
Title = $"Pad融合标定 - {calibId}",
Width = 1100,
Height = 750,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = Application.Current.MainWindow,
ResizeMode = ResizeMode.CanResize,
Content = calibView
};
window.Closing += (s, args) =>
{
if (_forceCloseIds.Contains(calibId))
{
_forceCloseIds.Remove(calibId);
return;
}
args.Cancel = true;
SyncResultBack(calibVM);
window.Hide();
};
return window;
}
#endregion
#region
private void SyncResultBack(FusionCalibViewModel calibVM)
{
var source = calibVM.ModuleItem;
if (source == null) return;
var target = CalibItems.FirstOrDefault(p => p.Camera_ID == source.Camera_ID);
if (target == null) return;
target.MoveAxisPos = source.MoveAxisPos;
target.ApproachXPos = source.ApproachXPos;
target.ApproachYPos = source.ApproachYPos;
target.CameraAxisPos = source.CameraAxisPos;
target.StartX = source.StartX;
target.StartY = source.StartY;
target.Step = source.Step;
target.Count = source.Count;
target.CalibStep = source.CalibStep;
target.CalibCount = source.CalibCount;
target.RulerPoints = source.RulerPoints;
target.RealPoints = source.RealPoints;
OnPropertyChanged(nameof(CalibItems));
}
#endregion
#region &
public void btnReadCalibData()
{
var result = MwMessageBox.Show("确认读取本地标定数据?", "确认读取",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
if (!ReadCalibData())
MwMessageBox.Show("读取本地标定数据失败!", "提示", MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
public bool ReadCalibData()
{
try
{
var items = new ObservableCollection<FusionCalibModuleItem>();
for (int i = 0; i < CalibAreaCount; i++)
{
var module = i < _setting.PadModules.Count
? _setting.PadModules[i]
: new FusionCalibModuleItem
{
Index = i + 1,
Camera_ID = $"{AreaFilePrefix}{i + 1}",
IsWaferCalib = false,
};
var pointsPath = Path.Combine(FileSaveDir,$"{AreaFilePrefix}{module.Index}{PointsFileExt}");
if (File.Exists(pointsPath))
{
MotionCalibFileService.ReadCalibPointsFile(pointsPath, out var real, out var ruler);
module.RealPoints = real;
module.RulerPoints = ruler;
}
items.Add(module);
}
CalibItems = items;
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[FusionMultiple] ReadCalibData 失败: {ex}");
return false;
}
}
public void btnSaveCalibData()
{
var result = MwMessageBox.Show("确认保存当前全部标定数据?", "确认保存",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
if (SaveCalibData())
MwMessageBox.Show("保存成功!", "提示", MessageBoxButton.OK,
MessageBoxImage.Information);
else
MwMessageBox.Show("保存失败!", "提示", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private bool SaveCalibData()
{
try
{
Directory.CreateDirectory(FileSaveDir);
_setting.PadModules = new List<FusionCalibModuleItem>(CalibItems);
_setting.Write();
foreach (var par in CalibItems)
{
if (par.RealPoints?.Count > 0 && par.RulerPoints?.Count > 0)
{
var pointsPath = Path.Combine(FileSaveDir,$"{AreaFilePrefix}{par.Index}{PointsFileExt}");
MotionCalibFileService.SaveCalibPointsFile(pointsPath, par.RealPoints,par.RulerPoints);
}
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[FusionMultiple] SaveCalibData 失败: {ex}");
return false;
}
}
#endregion
#region
public void btnApplyCalibData()
{
if (CalibItems.Count != CalibAreaCount)
{
MwMessageBox.Show("标定数据不足!", "提示", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var result = MwMessageBox.Show("确认应用标定数据?", "确认应用",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
// TODO: 调用融合应用算法
MwMessageBox.Show("应用成功!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
#endregion
#region
public void CleanupCalibWindows()
{
foreach (var kvp in _calibWindows) _forceCloseIds.Add(kvp.Key);
foreach (var kvp in _calibWindows)
{
try { kvp.Value.Close(); }
catch { }
}
_calibWindows.Clear();
_forceCloseIds.Clear();
}
#endregion
}
}

View File

@@ -0,0 +1,183 @@
using MainShell.AlgorithmCalib.Service;
using MainShell.Hardware;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.Components;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class FusionCalibViewModel:Screen,IPage
{
private HardwareManager _hardware;
private FusionCalibMotionService _fusionCalibMotionService;
public ICommand PrevCommand { get; set; }
public ICommand NextCommand { get; set; }
private IParameterManager _parameterManager;
public string Name { get; set; } = "FusionCalib";
private bool _isInit = false;
private ObservableCollection<Screen> _Screens;
/// <summary>
/// 需要显示的界面
/// </summary>
public ObservableCollection<Screen> Screens
{
get { return _Screens; }
set
{ SetAndNotify(ref _Screens, value); }
}
private Screen _ShowScreenVM;
/// <summary>
/// 当前显示界面的VM
/// </summary>
public Screen ShowScreenVM
{
get { return _ShowScreenVM; }
set
{ SetAndNotify(ref _ShowScreenVM, value); }
}
private string _currentCalibName;
/// <summary>
/// 当前标定名称供UI标定
/// </summary>
public string CurrentCalibName
{
get { return _currentCalibName; }
set { SetAndNotify(ref _currentCalibName, value); }
}
private string _progressText;
/// <summary>
/// 进度提示
/// </summary>
public string ProgressText
{
get { return _progressText; }
set { SetAndNotify(ref _progressText, value); }
}
private int _showIndex;
/// <summary>
/// 当前显示界面的下标
/// </summary>
public int ShowIndex
{
get { return _showIndex; }
set
{ SetAndNotify(ref _showIndex, value); }
}
private bool _isEnablePrev;
/// <summary>
/// 上一个按钮是否启用
/// </summary>
public bool IsEnablePrev
{
get { return _isEnablePrev; }
set
{ SetAndNotify(ref _isEnablePrev, value); }
}
private bool _isEnableNext = true;
/// <summary>
/// 下一个按钮是否启用
/// </summary>
public bool IsEnableNext
{
get { return _isEnableNext; }
set
{ SetAndNotify(ref _isEnableNext, value); }
}
public FusionCalibViewModel(IParameterManager paraManager)
{
_hardware = IoC.Get<HardwareManager>();
_fusionCalibMotionService=IoC.Get<FusionCalibMotionService>();
this._parameterManager = paraManager;
PrevCommand = new DelegateCommand(() =>
{
OnPrev();
});
NextCommand = new DelegateCommand(() =>
{
OnNext();
});
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (!_isInit)
{
Screens = new ObservableCollection<Screen>();
var cameraConfigs = new List<(string Name, string FileSaveName, List<HardwareDevice> Device)>
{
("Die融合标定","topWsCameraFusion", _hardware.CameraAxisManager.TopCameraWsAxisDevices),
("Map相机融合标定","wideCameraFusion", _hardware.CameraAxisManager.WideCameraAxisDevices),
};
Screens.Add(FusionMenuCalibViewModel.Create("Pad融合标定", "topCameraFusion", _hardware.CameraAxisManager.TopCameraAxisDevices,_hardware,_fusionCalibMotionService));
foreach (var (name, fileSaveName, device) in cameraConfigs)
{
Screens.Add(CameraFusionCalibViewModel.Create(name, fileSaveName, device, _hardware, _fusionCalibMotionService));
}
// 默认显示第一个
ShowIndex = 0;
ShowScreenVM = Screens[0];
UpdateNavigationState();
_isInit = true;
}
}
public void viewUnLoad()
{
//_device.ChangeCameraMode(CameraTriggerMode.On);
//_pLCControlOperation.CyliderPress(true);
}
private void OnPrev()
{
if (ShowIndex <= 0) return;
ShowIndex--;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState(); // ← 加上这行
}
private void OnNext()
{
if (ShowIndex >= Screens.Count - 1) return;
ShowIndex++;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState(); // ← 加上这行
}
/// <summary>
/// 统一更新导航状态和当前相机信息
/// </summary>
private void UpdateNavigationState()
{
IsEnablePrev = ShowIndex > 0;
IsEnableNext = ShowIndex < Screens.Count - 1;
// 更新当前相机名称
if (ShowScreenVM is CameraFusionCalibViewModel camVm)
{
CurrentCalibName = camVm.CalibName;
}
if (ShowScreenVM is FusionMenuCalibViewModel Vm)
{
CurrentCalibName = Vm.CalibName;
}
// 更新进度
ProgressText = $"{ShowIndex + 1} / {Screens.Count}";
}
}
}

View File

@@ -0,0 +1,398 @@
using JM1.VisionModule;
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.AlgorithmCalib.View;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Filewritable;
using MainShell.Hardware;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.SystemCalib;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml;
using Point = SemiconductorVisionAlgorithm.SemiParams.Point;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class FusionMenuCalibViewModel : Screen, IPage
{
private readonly Dictionary<string, Window> _calibWindows = new Dictionary<string, Window>();
private List<Point> _calibResultRealPoint = new List<Point>();
private List<Point> _calibResultAxisPoint = new List<Point>();
private MotionFusion _motionFusion = new MotionFusion();
private FusionCalibMotionService _fusionCalibMotionService;
public string Name { get; set; } = "FusionMenuCalib";
public string CalibName { get; set; }
public string FileSaveDir { get; set; }
private bool _isDataLoaded = false;
private CameraAxisViewModel _cameraAxisViewModelSevice = new CameraAxisViewModel();
public CameraAxisViewModel CameraAxisViewModelSevice
{
get { return _cameraAxisViewModelSevice; }
set
{
_cameraAxisViewModelSevice = value;
OnPropertyChanged(nameof(CameraAxisViewModelSevice));
}
}
private ObservableCollection<FusionCalibParItem> _fusionCalibParItem = new ObservableCollection<FusionCalibParItem>();
public ObservableCollection<FusionCalibParItem> FusionCalibParItem
{
get { return _fusionCalibParItem; }
set
{
_fusionCalibParItem = value;
OnPropertyChanged(nameof(FusionCalibParItem));
}
}
private FusionCalibParItem _selectedCalibPar;
public FusionCalibParItem SelectedCalibPar
{
get { return _selectedCalibPar; }
set
{
_selectedCalibPar = value;
OnPropertyChanged(nameof(SelectedCalibPar));
}
}
private FusionCalibParItem _inputParam;
public FusionCalibParItem InputParam
{
get { return _inputParam; }
set { SetAndNotify(ref _inputParam, value); }
}
private FusionCalibParItem _resultParam;
public FusionCalibParItem ResultParam
{
get { return _resultParam; }
set { SetAndNotify(ref _resultParam, value); }
}
private List<HardwareDevice> _hardwareDevice;
public List<HardwareDevice> HardwareDevice
{
get { return _hardwareDevice; }
set
{
_hardwareDevice = value;
NotifyOfPropertyChange();
}
}
private HardwareManager _hardware;
public HardwareManager Hardware
{
get { return _hardware; }
set
{
_hardware = value;
NotifyOfPropertyChange();
}
}
private bool _isCalibFinished;
public bool IsCalibFinished
{
get { return _isCalibFinished; }
set { SetAndNotify(ref _isCalibFinished, value); }
}
/// <summary>
/// 无参构造函数
/// </summary>
public FusionMenuCalibViewModel()
{
}
/// <summary>
/// 手动创建时使用的静态工厂方法
/// </summary>
/// <param name="cameraName">相机名称,如 "BottomCamera"</param>
/// <param name="device">对应的硬件设备</param>
public static FusionMenuCalibViewModel Create(string calibName, string fileSaveName, List<HardwareDevice> device, HardwareManager hardware, FusionCalibMotionService motion)
{
var vm = new FusionMenuCalibViewModel();
vm.Initialize(calibName, fileSaveName, device, hardware, motion);
return vm;
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (_hardware != null)
{
_cameraAxisViewModelSevice = new CameraAxisViewModel();
_cameraAxisViewModelSevice.CameraAxisDevices.HardwareDeviceList = HardwareDevice;
NotifyOfPropertyChange(nameof(CameraAxisViewModel));
}
if (!_isDataLoaded)
{
ReadCalibData();
_isDataLoaded = true;
}
}
private void Initialize(string calibName, string fileSaveName, List<HardwareDevice> device, HardwareManager hardware, FusionCalibMotionService motion)
{
CalibName = calibName;
Hardware = hardware;
HardwareDevice = device;
_fusionCalibMotionService = motion;
FileSaveDir = Path.Combine(Paths.CalibSettingPath, "fusionCalib");
var paramList = IoC.Get<IParameterManager>() as IParamList;
}
public void btnOpenCalibWindow()
{
if (SelectedCalibPar == null)
{
MwMessageBox.Show("请先选择一组标定数据。", "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
string cameraId = SelectedCalibPar.Camera_ID;
// ── 如果已有缓存窗口,直接激活显示 ──
if (_calibWindows.TryGetValue(cameraId, out var existingWindow))
{
if (existingWindow.WindowState == WindowState.Minimized)
existingWindow.WindowState = WindowState.Normal;
existingWindow.Show();
existingWindow.Activate();
return;
}
var calibVM = CameraFusionPadCalibViewModel.Create("", CalibName, HardwareDevice, SelectedCalibPar, Hardware, _fusionCalibMotionService);
calibVM.FileSaveDir = this.FileSaveDir;
// ② 创建视图
var calibView = new CameraFusionPadCalibView
{
DataContext = calibVM,
};
// 4. 创建 Window 包裹这个 UserControl
var window = new Window
{
Title = $"融合标定-{cameraId}",
Width = 1100,
Height = 750,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = Application.Current.MainWindow,
ResizeMode = ResizeMode.CanResize,
Content = calibView
};
// ── 拦截关闭:改为隐藏,保留内存状态 ──
window.Closing += (s, args) =>
{
args.Cancel = true; // 取消真正的关闭
window.Hide(); // 仅隐藏VM 和 View 状态全部保留
};
// ── 用 Unloaded 代替 Closed 做数据同步 ──
// 每次隐藏时都把 Service 最新数据写回选中行
window.IsVisibleChanged += (s, args) =>
{
if (!(bool)args.NewValue) // 变为不可见Hide
{
SyncCalibResultBack(calibVM);
}
};
// 缓存
_calibWindows[cameraId] = window;
window.Show();
window.Closed += (s, args) =>
{
var innerData = calibVM.FusionCalibParItem;
if (innerData != null)
{
SelectedCalibPar.Index = innerData.Index;
SelectedCalibPar.MoveAxisPos = innerData.MoveAxisPos;
SelectedCalibPar.ApproachXPos = innerData.ApproachXPos;
SelectedCalibPar.ApproachYPos = innerData.ApproachYPos;
SelectedCalibPar.StartX = innerData.StartX;
SelectedCalibPar.StartY = innerData.StartY;
SelectedCalibPar.Step = innerData.Step;
SelectedCalibPar.Count = innerData.Count;
SelectedCalibPar.RulerPoints = innerData.RulerPoints;
SelectedCalibPar.RealPoints = innerData.RealPoints;
// 刷新 DataGrid
OnPropertyChanged(nameof(FusionCalibParItem));
}
};
}
/// <summary>
/// 将内部标定窗口的最新数据同步回外部 DataGrid 选中行
/// </summary>
private void SyncCalibResultBack(CameraFusionPadCalibViewModel calibVM)
{
var innerData = calibVM.ResultParam;
if (innerData != null) return;
var target = FusionCalibParItem.FirstOrDefault(p => p.Camera_ID == calibVM.CameraName);
if(target == null) return;
target.Index = innerData.Index;
target.MoveAxisPos = innerData.MoveAxisPos;
target.ApproachXPos = innerData.ApproachXPos;
target.ApproachYPos = innerData.ApproachYPos;
target.StartX = innerData.StartX;
target.StartY = innerData.StartY;
target.Step = innerData.Step;
target.Count = innerData.Count;
target.RulerPoints = innerData.RulerPoints;
target.RealPoints = innerData.RealPoints;
// 刷新 DataGrid
OnPropertyChanged(nameof(FusionCalibParItem));
}
public void btnReadCalibData()
{
var result = MwMessageBox.Show($"确认读取本地标定数据?", "确认读取", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
if (!ReadCalibData())
{
MwMessageBox.Show("读取本地标定数据失败!", "提示", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
public bool ReadCalibData()
{
try
{
var paramList = IoC.Get<IParameterManager>() as IParamList;
//FusionCalibParItem.CalibParList = new ObservableCollection<FusionSysDataItem>();
for (int i = 0; i < 4; i++)
{
var filePath = Path.Combine(FileSaveDir, $"Fusion_Time{i + 1}.xml");
var pointsFilePath = Path.Combine(FileSaveDir, $"Fusion_Time{i + 1}.txt");
bool hasXml = File.Exists(filePath);
bool hasTxt = File.Exists(pointsFilePath);
double moveAxisPos = 0;
double approachXPos = 0;
double approachYPos = 0;
double startX = 0;
double startY = 0;
double step = 0;
int count = 0;
List<Point> rulerPoints = new List<Point>();
List<Point> realPoints = new List<Point>();
// ① 有 XML → 读取参数
if (hasXml)
{
//var uiData = UIDataManager.Instance.GetUIData<FusionCalibParItem>(paramList);
//uiData.Read(filePath);
//var xmlData=XmlReader(filePath);
//if (xmlData.Datas != null && xmlData.Datas.Count > 0)
//{
// var item = xmlData.Datas[0]; // 每个文件只有一组数据
// index=item.Index;
// moveAxisPos = item.MoveAxisPos;
// approachXPos = item.ApproachXPos;
// approachYPos = item.ApproachYPos;
// startX = item.StartX;
// startY = item.StartY;
// step = item.Step;
// count = item.Count;
//}
}
// ② 有 TXT → 读取标定点位
if (hasTxt)
{
MotionCalibFileService.ReadCalibPointsFile(
pointsFilePath, out realPoints, out rulerPoints);
rulerPoints = rulerPoints ?? new List<Point>();
realPoints = realPoints ?? new List<Point>();
}
// ③ 无论什么情况,都加到列表(保证 5 组都在)
var par = new FusionCalibParItem
{
Index = i + 1,
Camera_ID = "Fusion_Time" + (i + 1),
MoveAxisPos = moveAxisPos,
ApproachXPos = approachXPos,
ApproachYPos = approachYPos,
StartX = startX,
StartY = startY,
Step = step,
Count = count,
RulerPoints = rulerPoints,
RealPoints = realPoints,
};
FusionCalibParItem.Add(par);
}
return true;
}
catch
{
return false;
}
}
public void btnApplyCalibData()
{
//if (FusionCalibParItem.CalibParList.Count != 5)
//{
// MwMessageBox.Show("标定数据不足,请完成全部标定!", "提示", MessageBoxButton.OK, MessageBoxImage.Error);
// return;
//}
//var result = MwMessageBox.Show($"确认应用标定数据?", "确认应用", MessageBoxButton.YesNo, MessageBoxImage.Warning);
//if (result == MessageBoxResult.Yes)
//{
// if (true) //
// {
// MwMessageBox.Show("应用标定数据成功!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
// }
//}
}
public void ApplayCalibData()
{
//List<string> cameraIdList = new List<string>();
//for (int i = 0; i < 5; i++)
//{
// var resultPointsList = FusionCalibParItem.CalibParList[i];
// _motionFusion.Set(i, resultPointsList.ResultRealPoints, resultPointsList.ResultAxisPoints);
// cameraIdList.Add(resultPointsList.CameraId);
//}
//_motionFusion.AutoSelectPt(cameraIdList[0], cameraIdList[1], cameraIdList[2], cameraIdList[3], out _calibResultRealPoint, out _calibResultAxisPoint);
//MwAlgorithmHelper.Instance.RulerToRealMaritex("topCameraPad", _calibResultAxisPoint, _calibResultRealPoint, 1, 1, 1, 1, UIDataManager.CalibDataDir);
}
public void XmlReader(string xmlFilePath)
{
}
}
}

View File

@@ -0,0 +1,190 @@
using MainShell.Hardware;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.Components;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xml.Linq;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class MotionCalibContentsViewModel : Screen, IPage
{
private HardwareManager _hardware;
public ICommand PrevCommand { get; set; }
public ICommand NextCommand { get; set; }
private IParameterManager _parameterManager;
public string Name { get; set; } = "MotionCalib";
private bool _isInit = false;
private ObservableCollection<Screen> _screens;
/// <summary>
/// 需要显示的界面
/// </summary>
public ObservableCollection<Screen> Screens
{
get { return _screens; }
set
{ SetAndNotify(ref _screens, value); }
}
private Screen _showScreenVM;
/// <summary>
/// 当前显示界面的VM
/// </summary>
public Screen ShowScreenVM
{
get { return _showScreenVM; }
set
{ SetAndNotify(ref _showScreenVM, value); }
}
private string _currentCalibName;
/// <summary>
/// 当前标定名称供UI标定
/// </summary>
public string CurrentCalibName
{
get { return _currentCalibName; }
set { SetAndNotify(ref _currentCalibName, value); }
}
private string _progressText;
/// <summary>
/// 进度提示
/// </summary>
public string ProgressText
{
get { return _progressText; }
set { SetAndNotify(ref _progressText, value); }
}
private int _ShowIndex;
/// <summary>
/// 当前显示界面的下标
/// </summary>
public int ShowIndex
{
get { return _ShowIndex; }
set
{ SetAndNotify(ref _ShowIndex, value); }
}
private bool _IsEnablePrev;
/// <summary>
/// 上一个按钮是否启用
/// </summary>
public bool IsEnablePrev
{
get { return _IsEnablePrev; }
set
{ SetAndNotify(ref _IsEnablePrev, value); }
}
private bool _IsEnableNext = true;
/// <summary>
/// 下一个按钮是否启用
/// </summary>
public bool IsEnableNext
{
get { return _IsEnableNext; }
set
{ SetAndNotify(ref _IsEnableNext, value); }
}
public MotionCalibContentsViewModel(IParameterManager paraManager)
{
_hardware = IoC.Get<HardwareManager>();
this._parameterManager = paraManager;
PrevCommand = new DelegateCommand(() =>
{
OnPrev();
});
NextCommand = new DelegateCommand(() =>
{
OnNext();
});
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (!_isInit)
{
Screens = new ObservableCollection<Screen>();
Screens.Add(MotionMultipleCalibViewModel.Create("上相机运动系标定", "topCameraMotion", _hardware.CameraAxisManager.TopCameraAxisDevices));
var cameraConfigs = new List<(string Name, string FileSaveName, List<HardwareDevice> Device)>
{
("上相机WS运动系标定","topWsCameraMotion", _hardware.CameraAxisManager.TopCameraWsAxisDevices),
("广角相机运动系标定","wideCameraMotion", _hardware.CameraAxisManager.WideCameraAxisDevices),
("广角相机WS运动系标定","wideWsCameraMotion", _hardware.CameraAxisManager.WideCameraWsAxisDevices),
("下相机运动系标定","bottomCameraMotion", _hardware.CameraAxisManager.BottomCameraAxisDevices),
};
foreach (var (name, fileSaveName, device) in cameraConfigs)
{
Screens.Add(MotionCalibViewModel.Create(name, fileSaveName, device));
}
ShowIndex = 0;
ShowScreenVM = Screens[0];
UpdateNavigationState();
_isInit = true;
}
}
public void viewUnLoad()
{
// 关闭所有缓存的标定子窗口
if (Screens != null)
{
foreach (var screen in Screens)
{
if (screen is MotionMultipleCalibViewModel menuVm)
{
menuVm.CleanupCalibWindows();
}
}
}
}
private void OnPrev()
{
if (ShowIndex <= 0) return;
ShowIndex--;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState(); // ← 加上这行
}
private void OnNext()
{
if (ShowIndex >= Screens.Count - 1) return;
ShowIndex++;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState(); // ← 加上这行
}
/// <summary>
/// 统一更新导航状态和当前相机信息
/// </summary>
private void UpdateNavigationState()
{
IsEnablePrev = ShowIndex > 0;
IsEnableNext = ShowIndex < Screens.Count - 1;
// 更新当前相机名称
if (ShowScreenVM is MotionMultipleCalibViewModel camVm)
{
CurrentCalibName = camVm.CalibName;
}
if (ShowScreenVM is MotionCalibViewModel Vm)
{
CurrentCalibName = Vm.CalibName;
}
// 更新进度
ProgressText = $"{ShowIndex + 1} / {Screens.Count}";
}
}
}

View File

@@ -0,0 +1,164 @@
using MainShell.Filewritable;
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.SystemCalib;
using MwFramework.ManagerService;
using SemiconductorVisionAlgorithm.SemiParams;
using Stylet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Point = SemiconductorVisionAlgorithm.SemiParams.Point;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class MotionCalibViewModel : Screen, IPage, IHandle<MotionCalibFinishEventArgs>
{
public string Name { get; set; } = "CameraMotionCalib";
public string CalibName { get; private set; }
public string CalibId { get; private set; }
public string FileSaveName { get; private set; }
private MotionSysDataItem _inputParam;
public MotionSysDataItem InputParam
{
get => _inputParam;
set => SetAndNotify(ref _inputParam, value);
}
private MotionSysDataItem _resultParam;
public MotionSysDataItem ResultParam
{
get => _resultParam;
set => SetAndNotify(ref _resultParam, value);
}
private MotionSysDataItem _motionSysDataItem;
public MotionSysDataItem MotionSysDataItem
{
get => _motionSysDataItem;
set => SetAndNotify(ref _motionSysDataItem, value);
}
private MotionSysCalibViewModel _service;
public MotionSysCalibViewModel Service
{
get => _service;
set => SetAndNotify(ref _service, value);
}
private List<Point> _calibAxisPoints;
public List<Point> CalibAxisPoints
{
get => _calibAxisPoints;
set => SetAndNotify(ref _calibAxisPoints, value);
}
private List<Point> _calibRealPoints;
public List<Point> CalibRealPoints
{
get => _calibRealPoints;
set => SetAndNotify(ref _calibRealPoints, value);
}
private string _fileSaveDir = Paths.CalibSettingPath;
public string FileSaveDir
{
get => _fileSaveDir;
set => SetAndNotify(ref _fileSaveDir, value);
}
private bool _isCalibFinished;
public bool IsCalibFinished
{
get => _isCalibFinished;
set => SetAndNotify(ref _isCalibFinished, value);
}
public MotionCalibViewModel() { }
/// <summary>
/// 普通创建不订阅事件不使用用户ID
/// </summary>
public static MotionCalibViewModel Create(
string calibName, string fileSaveName, List<HardwareDevice> device)
{
var vm = new MotionCalibViewModel();
vm.Initialize(calibName, fileSaveName, device,
subscribeEvents: false, calibId: "", isUseUserId: false);
return vm;
}
/// <summary>
/// 弹窗专用传入标定参数订阅事件使用用户ID
/// </summary>
public static MotionCalibViewModel Create(
string calibName, string fileSaveName, List<HardwareDevice> device,
MotionSysDataItem param, string calibId = "")
{
var vm = new MotionCalibViewModel();
vm.Initialize(calibName, fileSaveName, device,
subscribeEvents: true, calibId: calibId, isUseUserId: true);
vm.InputParam = param;
return vm;
}
private void Initialize(
string calibName, string fileSaveName, List<HardwareDevice> device,
bool subscribeEvents, string calibId = "", bool isUseUserId = false)
{
if (subscribeEvents)
{
IoC.Get<IEventAggregator>().Unsubscribe(this);
IoC.Get<IEventAggregator>().Subscribe(this);
}
CalibName = calibName;
CalibId = calibId;
FileSaveName = fileSaveName;
var paramList = IoC.Get<IParameterManager>() as IParamList;
Service = isUseUserId
? new MotionSysCalibViewModel(device, paramList, true, true, calibId)
: new MotionSysCalibViewModel(device, paramList, true);
string pathSavePath = Path.Combine(FileSaveDir, FileSaveName + ".xml");
Service.SetParaSavePath(pathSavePath);
Service.IsShowSolidLine = true;
Service.ShapeThickness = 1;
Service.DrawInConcurrency = false;
Service.IsAxisControlLDBVisible = Visibility.Visible;
}
public void FetchCalibResult()
{
if (Service == null || !Service.IsCalibTaskCompleted)
return;
var (axisPoints, realPoints) = Service.GetCalibResult();
CalibAxisPoints = axisPoints;
CalibRealPoints = realPoints;
var filePath = Path.Combine(FileSaveDir, $"{FileSaveName}.csv");
MotionCalibFileService.SaveCalibPointsFile(filePath, CalibRealPoints, CalibAxisPoints);
}
public void Handle(MotionCalibFinishEventArgs message)
{
FetchCalibResult();
}
// 修复:页面关闭时取消事件订阅,防止内存泄漏
protected override void OnDeactivate()
{
IoC.Get<IEventAggregator>().Unsubscribe(this);
base.OnDeactivate();
}
}
}

View File

@@ -0,0 +1,603 @@
using JM1.VisionModule;
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.AlgorithmCalib.View;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Filewritable;
using MainShell.Hardware;
using MainShell.Recipe.Models;
using MainShell.Recipe.ViewModel;
using Maxwell.SemiFramework.WaferCalibration.ViewModel;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.SystemCalib;
using MwFramework.Controls.UIControl;
using MwFramework.ManagerService;
using SemiconductorVisionAlgorithm.SemiParams;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows;
using CameraAxisViewModel = MainShell.Common.Display.ViewModel.CameraAxisViewModel;
using Point = SemiconductorVisionAlgorithm.SemiParams.Point;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class MotionMultipleCalibViewModel:Screen,IPage
{
#region
private const int CalibAreaCount = 5;
private const string PointsFileExt = ".csv";
private const string AreaFilePrefix = "Camera_Area";
#endregion
#region
private readonly HashSet<string> _forceCloseIds = new HashSet<string>();
private readonly Dictionary<string, Window> _calibWindows = new Dictionary<string, Window>();
private readonly MotionFusion _motionFusion = new MotionFusion();
private List<Point> _calibResultRealPoint = new List<Point>();
private List<Point> _calibResultAxisPoint = new List<Point>();
private bool _isDataLoaded = false;
#endregion
#region
public string Name { get; set; } = "MotionMenuCalib";
public string CalibName { get; set; }
public string FileSaveDir { get; set; }
private CameraAxisViewModel _cameraAxisViewModelService;
public CameraAxisViewModel CameraAxisViewModelService
{
get => _cameraAxisViewModelService;
set => SetAndNotify(ref _cameraAxisViewModelService, value);
}
private DelegateBase _motion = new DelegateBase();
public DelegateBase Motion
{
get => _motion;
set => SetAndNotify(ref _motion, value);
}
private ObservableCollection<MotionSysDataItem> _motionCalibParItem = new ObservableCollection<MotionSysDataItem>();
public ObservableCollection<MotionSysDataItem> MotionCalibParItem
{
get => _motionCalibParItem;
set => SetAndNotify(ref _motionCalibParItem, value);
}
private MotionSysDataItem _selectedCalibPar;
public MotionSysDataItem SelectedCalibPar
{
get => _selectedCalibPar;
set => SetAndNotify(ref _selectedCalibPar, value);
}
private List<HardwareDevice> _hardware;
public List<HardwareDevice> Hardware
{
get => _hardware;
set
{
SetAndNotify(ref _hardware, value);
NotifyOfPropertyChange();
}
}
private MotionSysCalibViewModel _service;
public MotionSysCalibViewModel Service
{
get => _service;
set => SetAndNotify(ref _service, value);
}
private bool _isCalibFinished;
public bool IsCalibFinished
{
get => _isCalibFinished;
set => SetAndNotify(ref _isCalibFinished, value);
}
#endregion
#region &
public MotionMultipleCalibViewModel()
{
_cameraAxisViewModelService = IoC.Get<CameraAxisViewModel>();
}
public static MotionMultipleCalibViewModel Create(string calibName, string saveFileName, List<HardwareDevice> device)
{
var vm = new MotionMultipleCalibViewModel();
vm.Initialize(calibName, saveFileName, device);
return vm;
}
#endregion
#region &
private void Initialize(string calibName, string saveFileName, List<HardwareDevice> device)
{
CalibName = calibName;
Hardware = device;
FileSaveDir = Path.Combine(Paths.CalibSettingPath, "motionCalib");
var paramList = IoC.Get<IParameterManager>() as IParamList;
Service = new MotionSysCalibViewModel(device, paramList, true)
{
IsShowSolidLine = true,
ShapeThickness = 1,
DrawInConcurrency = false,
IsAxisControlLDBVisible = Visibility.Visible
};
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (_hardware != null)
{
_cameraAxisViewModelService.CameraAxisDevices.HardwareDeviceList = Hardware;
NotifyOfPropertyChange(nameof(CameraAxisViewModelService));
}
if (!_isDataLoaded)
{
ReadCalibData();
_isDataLoaded = true;
}
}
#endregion
#region
private string GetXmlPath(int areaIndex) =>
Path.Combine(FileSaveDir, $"{AreaFilePrefix}{areaIndex}.xml");
private string GetPointsPath(int areaIndex) =>
Path.Combine(FileSaveDir, $"{AreaFilePrefix}{areaIndex}{PointsFileExt}");
#endregion
#region
public void btnOpenCalibWindow()
{
if (SelectedCalibPar == null)
{
MwMessageBox.Show("请先选择一组标定数据。", "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
string calibId = SelectedCalibPar.CalibId;
// 已有缓存窗口 → 激活显示
if (_calibWindows.TryGetValue(calibId, out var existingWindow))
{
if (existingWindow.WindowState == WindowState.Minimized)
existingWindow.WindowState = WindowState.Normal;
existingWindow.Show();
existingWindow.Activate();
return;
}
// 首次打开:创建子窗口 VM
var calibVM = MotionCalibViewModel.Create(
CalibName, SelectedCalibPar.CameraId, Hardware,
SelectedCalibPar, SelectedCalibPar.CalibId);
calibVM.FileSaveDir = FileSaveDir;
var window = CreateCalibWindow(calibId, calibVM);
// 写入当前行数据到子窗口
SyncFields(SelectedCalibPar, calibVM.Service?.SelectedSingleUIData);
_calibWindows[calibId] = window;
window.Show();
}
private Window CreateCalibWindow(string calibId, MotionCalibViewModel calibVM)
{
var calibView = new MotionCalibView { DataContext = calibVM };
var window = new Window
{
Title = $"运动系标定 - {calibId}",
Width = 1100,
Height = 750,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = Application.Current.MainWindow,
ResizeMode = ResizeMode.CanResize,
Content = calibView
};
// 拦截关闭:改为隐藏,保留内存状态
window.Closing += (s, args) =>
{
if (_forceCloseIds.Contains(calibId))
{
_forceCloseIds.Remove(calibId);
return; // 允许真正关闭
}
args.Cancel = true;
SyncResultBack(calibVM);
window.Hide();
};
return window;
}
#endregion
#region
/// <summary>
/// 共享的字段映射:将 source 的参数写入 target单向
/// </summary>
private void SyncFields(MotionSysDataItem source, MotionSysDataItem target)
{
if (source == null || target == null) return;
target.StartAxisX = source.StartAxisX;
target.StartAxisY = source.StartAxisY;
target.StartRealX = source.StartRealX;
target.StartRealY = source.StartRealY;
target.Rows = source.Rows;
target.RowStep = source.RowStep;
target.Columns = source.Columns;
target.ColumnStep = source.ColumnStep;
target.XDirSpace = source.XDirSpace;
target.CameraZPos = source.CameraZPos;
target.Threshold = source.Threshold;
// 标定点位用深拷贝,避免子窗口和主页面共享同一引用
if (source.ResultRealPoints != null && source.ResultRealPoints.Count > 0)
target.ResultRealPoints = new List<Point>(source.ResultRealPoints);
if (source.ResultAxisPoints != null && source.ResultAxisPoints.Count > 0)
target.ResultAxisPoints = new List<Point>(source.ResultAxisPoints);
}
/// <summary>
/// 子窗口关闭时:子窗口 → 主页面对应行
/// </summary>
private void SyncResultBack(MotionCalibViewModel calibVM)
{
var innerData = calibVM.Service?.SelectedSingleUIData;
if (innerData == null) return;
var target = MotionCalibParItem.FirstOrDefault(p => p.CalibId == calibVM.CalibId);
if (target == null) return;
SyncFields(innerData, target);
OnPropertyChanged(nameof(MotionCalibParItem));
}
#endregion
#region
public void btnSaveCalibData()
{
var result = MwMessageBox.Show("确认保存当前全部标定数据?", "确认保存",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
if (SaveCalibData())
{
MwMessageBox.Show("保存标定数据成功!", "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
}
else
{
MwMessageBox.Show("保存标定数据失败!详见调试日志。", "提示",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
private bool SaveCalibData()
{
try
{
var paramList = IoC.Get<IParameterManager>() as IParamList;
Directory.CreateDirectory(FileSaveDir);
for (int i = 0; i < MotionCalibParItem.Count; i++)
{
int areaIndex = i + 1;
var par = MotionCalibParItem[i];
var xmlPath = GetXmlPath(areaIndex);
var pointsPath = GetPointsPath(areaIndex);
// ① 保存 XML 参数
SaveXmlParam(paramList, xmlPath, par);
// ② 保存标定点位
if (HasCalibPoints(par))
{
MotionCalibFileService.SaveCalibPointsFile(
pointsPath, par.ResultRealPoints, par.ResultAxisPoints);
}
}
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[MotionMenuCalib] SaveCalibData 失败: {ex}");
return false;
}
}
private void SaveXmlParam(IParamList paramList, string xmlPath, MotionSysDataItem par)
{
var uiData = UIDataManager.Instance.GetUIData<MotionSysData>(paramList);
// 文件已存在 → 先读取保留完整结构,再回写
if (File.Exists(xmlPath))
uiData.Read(xmlPath);
if (uiData.Datas == null)
uiData.Datas = new List<MotionSysDataItem>();
MotionSysDataItem item;
if (uiData.Datas.Count > 0)
{
item = uiData.Datas[0];
}
else
{
item = new MotionSysDataItem { Id = par.Id, CameraId = par.CameraId };
uiData.Datas.Add(item);
}
CopyParamFields(par, item);
uiData.IsManualCreated = true;
uiData.Write(xmlPath);
}
#endregion
#region
public void btnReadCalibData()
{
var result = MwMessageBox.Show("确认读取本地标定数据?", "确认读取",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
if (!ReadCalibData())
{
MwMessageBox.Show("读取本地标定数据失败!详见调试日志。", "提示",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
public bool ReadCalibData()
{
try
{
var paramList = IoC.Get<IParameterManager>() as IParamList;
var items = new ObservableCollection<MotionSysDataItem>();
for (int i = 1; i <= CalibAreaCount; i++)
{
var par = ReadSingleArea(paramList, i);
items.Add(par);
}
MotionCalibParItem = items;
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[MotionMenuCalib] ReadCalibData 失败: {ex}");
return false;
}
}
private MotionSysDataItem ReadSingleArea(IParamList paramList, int areaIndex)
{
var xmlPath = GetXmlPath(areaIndex);
var pointsPath = GetPointsPath(areaIndex);
// 默认值
double? startAxisX = 0, startAxisY = 0;
double? startRealX = 0, startRealY = 0;
int? rows = 0, columns = 0;
double? rowStep = 0, columnStep = 0;
List<Point> axisPoints = new List<Point>();
List<Point> realPoints = new List<Point>();
// ① 读 XML 参数
if (File.Exists(xmlPath))
{
var uiData = UIDataManager.Instance.GetUIData<MotionSysData>(paramList);
uiData.Read(xmlPath);
if (uiData.Datas != null && uiData.Datas.Count > 0)
{
var item = uiData.Datas[0];
startAxisX = item.StartAxisX;
startAxisY = item.StartAxisY;
startRealX = item.StartRealX;
startRealY = item.StartRealY;
rows = item.Rows;
columns = item.Columns;
rowStep = item.RowStep;
columnStep = item.ColumnStep;
}
}
// ② 读标定点位
if (File.Exists(pointsPath))
{
MotionCalibFileService.ReadCalibPointsFile(pointsPath, out realPoints, out axisPoints);
axisPoints = axisPoints ?? new List<Point>();
realPoints = realPoints ?? new List<Point>();
}
return new MotionSysDataItem
{
Id = areaIndex.ToString(),
CameraId = Hardware[0].Camera.Id,
CalibId = $"{AreaFilePrefix}{areaIndex}",
StartAxisX = startAxisX,
StartAxisY = startAxisY,
StartRealX = startRealX,
StartRealY = startRealY,
Rows = rows,
RowStep = rowStep,
Columns = columns,
ColumnStep = columnStep,
ResultAxisPoints = axisPoints,
ResultRealPoints = realPoints
};
}
#endregion
#region
public void btnApplyCalibData()
{
if (MotionCalibParItem.Count != CalibAreaCount)
{
MwMessageBox.Show("标定数据不足,请完成全部标定!", "提示",
MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
var result = MwMessageBox.Show("确认应用标定数据?", "确认应用",
MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
if (ApplyCalibData())
{
MwMessageBox.Show("应用标定数据成功!", "提示",
MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
private bool ApplyCalibData()
{
try
{
var cameraIdList = new List<string>();
for (int i = 0; i < CalibAreaCount; i++)
{
var par = MotionCalibParItem[i];
// Set 内部使用 1-based 区域编号
_motionFusion.Set(i + 1, par.ResultRealPoints, par.ResultAxisPoints);
cameraIdList.Add(par.CameraId);
}
// AutoSelectPt 需要 4 个 CameraId四象限融合
_motionFusion.AutoSelectPt(
cameraIdList[0], cameraIdList[1],
cameraIdList[2], cameraIdList[3],
out _calibResultAxisPoint, out _calibResultRealPoint);
MwAlgorithmHelper.Instance.RulerToRealMaritex(
Hardware[0].Camera.Id,
_calibResultAxisPoint, _calibResultRealPoint,
1, 1, 1, 1,
UIDataManager.CalibDataDir);
return true;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[MotionMenuCalib] ApplyCalibData 失败: {ex}");
MwMessageBox.Show($"应用失败:{ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}
#endregion
#region
/// <summary>
/// 将 source 的参数字段拷贝到 target不包含 ResultRealPoints / ResultAxisPoints
/// </summary>
private static void CopyParamFields(MotionSysDataItem source, MotionSysDataItem target)
{
target.StartAxisX = source.StartAxisX;
target.StartAxisY = source.StartAxisY;
target.StartRealX = source.StartRealX;
target.StartRealY = source.StartRealY;
target.Rows = source.Rows;
target.RowStep = source.RowStep;
target.Columns = source.Columns;
target.ColumnStep = source.ColumnStep;
target.XDirSpace = source.XDirSpace;
target.CameraZPos = source.CameraZPos;
target.Threshold = source.Threshold;
}
private static bool HasCalibPoints(MotionSysDataItem par) =>
par.ResultRealPoints != null && par.ResultRealPoints.Count > 0 &&
par.ResultAxisPoints != null && par.ResultAxisPoints.Count > 0;
#endregion
#region
/// <summary>
/// 主页面卸载时彻底关闭所有缓存窗口
/// </summary>
public void CleanupCalibWindows()
{
// 先标记所有需要强制关闭的 calibId
foreach (var kvp in _calibWindows)
_forceCloseIds.Add(kvp.Key);
// 逐个关闭
foreach (var kvp in _calibWindows)
{
try
{
kvp.Value.Close();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(
$"[MotionMenuCalib] 关闭窗口 {kvp.Key} 失败: {ex.Message}");
}
}
_calibWindows.Clear();
_forceCloseIds.Clear();
}
#endregion
}
}

View File

@@ -0,0 +1,185 @@
using MainShell.Hardware;
using MaxwellFramework.Core.Common;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.Components;
using MwFramework.Device;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class PixRatioCalibContentsViewModel:Screen,IPage
{
private HardwareManager _hardware;
public ICommand PrevCommand { get; set; }
public ICommand NextCommand { get; set; }
private IParameterManager _parameterManager;
public string Name { get; set; } = "PixRatioCalib";
private bool _isInit = false;
private ObservableCollection<Screen> _screens;
/// <summary>
/// 需要显示的界面
/// </summary>
public ObservableCollection<Screen> Screens
{
get { return _screens; }
set
{ SetAndNotify(ref _screens, value); }
}
private Screen _showScreenVM;
/// <summary>
/// 当前显示界面的VM
/// </summary>
public Screen ShowScreenVM
{
get { return _showScreenVM; }
set
{ SetAndNotify(ref _showScreenVM, value); }
}
private string _currentCalibName;
/// <summary>
/// 当前标定名称供UI标定
/// </summary>
public string CurrentCalibName
{
get { return _currentCalibName; }
set { SetAndNotify(ref _currentCalibName, value); }
}
private string _progressText;
/// <summary>
/// 进度提示
/// </summary>
public string ProgressText
{
get { return _progressText; }
set { SetAndNotify(ref _progressText, value); }
}
private int _showIndex;
/// <summary>
/// 当前显示界面的下标
/// </summary>
public int ShowIndex
{
get { return _showIndex; }
set
{ SetAndNotify(ref _showIndex, value); }
}
private bool _isEnablePrev;
/// <summary>
/// 上一个按钮是否启用
/// </summary>
public bool IsEnablePrev
{
get { return _isEnablePrev; }
set
{ SetAndNotify(ref _isEnablePrev, value); }
}
private bool _isEnableNext = true;
/// <summary>
/// 下一个按钮是否启用
/// </summary>
public bool IsEnableNext
{
get { return _isEnableNext; }
set
{ SetAndNotify(ref _isEnableNext, value); }
}
public PixRatioCalibContentsViewModel(IParameterManager paraManager)
{
this._hardware = IoC.Get<HardwareManager>();
this._parameterManager = paraManager;
PrevCommand = new DelegateCommand(() =>
{
OnPrev();
});
NextCommand = new DelegateCommand(() =>
{
OnNext();
});
}
#region view加载
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (!_isInit)
{
Screens = new ObservableCollection<Screen>();
// ====== 定义5组相机-轴配置 ======
var cameraConfigs = new List<(string Name, List<HardwareDevice> Device)>
{
("上相机像素比标定", _hardware.CameraAxisManager.TopCameraAxisDevices),
("上相机WS像素比标定", _hardware.CameraAxisManager.TopCameraWsAxisDevices),
("广角相机像素比标定", _hardware.CameraAxisManager.WideCameraAxisDevices),
("广角相机WS像素比标定", _hardware.CameraAxisManager.WideCameraWsAxisDevices),
("下相机像素比标定", _hardware.CameraAxisManager.BottomCameraAxisDevices),
};
foreach (var (name, device) in cameraConfigs)
{
Screens.Add(PixRatioCalibViewModel.Create(name, device));
}
// 默认显示第一个
ShowIndex = 0;
ShowScreenVM = Screens[0];
UpdateNavigationState();
_isInit = true;
}
}
public void viewUnLoad()
{
}
#endregion
#region
private void OnPrev()
{
if (ShowIndex <= 0) return;
ShowIndex--;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState();
}
private void OnNext()
{
if (ShowIndex >= Screens.Count - 1) return;
ShowIndex++;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState();
}
#endregion
#region
/// <summary>
/// 统一更新导航状态和当前标定信息
/// </summary>
private void UpdateNavigationState()
{
IsEnablePrev = ShowIndex > 0;
IsEnableNext = ShowIndex < Screens.Count - 1;
// 更新当前标定名称
if (ShowScreenVM is PixRatioCalibViewModel camVm)
{
CurrentCalibName = camVm.CalibName;
}
// 更新进度
ProgressText = $"{ShowIndex + 1} / {Screens.Count}";
}
#endregion
}
}

View File

@@ -0,0 +1,74 @@
using MainShell.Hardware;
using MaxwellFramework.Core.Common;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.SystemCalib;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class PixRatioCalibViewModel:Screen,IPage
{
public string Name { get; set; } = "CameraPixRatioCalib";
public string CalibName { get; set; }
private CameraParaCalibViewModel _service;
public CameraParaCalibViewModel Service
{
get { return _service; }
set
{
_service = value;
NotifyOfPropertyChange();
}
}
/// <summary>
/// 标定名称标识用于UI显示和逻辑判断
/// </summary>
/// <summary>
/// 无参构造函数
/// </summary>
public PixRatioCalibViewModel()
{
}
/// <summary>
/// 手动创建时使用的静态工厂方法
/// </summary>
/// <param name="calibName">标定名称,如 "上相机像素比标定"</param>
/// <param name="device">对应的硬件设备</param>
public static PixRatioCalibViewModel Create(string calibName, List<HardwareDevice> device)
{
var vm = new PixRatioCalibViewModel();
vm.Initialize(calibName, device);
return vm;
}
private void Initialize(string calibName, List<HardwareDevice> device)
{
CalibName = calibName;
var paramList = IoC.Get<IParameterManager>() as IParamList;
Service = new CameraParaCalibViewModel(
device,
paramList,
true
);
Service.IsShowSolidLine = true;
Service.ShapeThickness = 1;
Service.DrawInConcurrency = false;
Service.IsAxisControlLDBVisible = Visibility.Visible;
}
}
}

View File

@@ -0,0 +1,180 @@
using MainShell.Hardware;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.Components;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class RotateCenterCalibContentsViewModel:Screen,IPage
{
private HardwareManager _hardware;
public ICommand PrevCommand { get; set; }
public ICommand NextCommand { get; set; }
private IParameterManager _parameterManager;
public string Name { get; set; } = "RotateCenterCalib";
private ObservableCollection<Screen> _screens;
/// <summary>
/// 需要显示的界面
/// </summary>
public ObservableCollection<Screen> Screens
{
get { return _screens; }
set
{ SetAndNotify(ref _screens, value); }
}
private Screen _showScreenVM;
/// <summary>
/// 当前显示界面的VM
/// </summary>
public Screen ShowScreenVM
{
get { return _showScreenVM; }
set
{ SetAndNotify(ref _showScreenVM, value); }
}
private string _currentCalibName;
/// <summary>
/// 当前标定名称供UI标定
/// </summary>
public string CurrentCalibName
{
get { return _currentCalibName; }
set { SetAndNotify(ref _currentCalibName, value); }
}
private string _progressText;
/// <summary>
/// 进度提示
/// </summary>
public string ProgressText
{
get { return _progressText; }
set { SetAndNotify(ref _progressText, value); }
}
private int _showIndex;
/// <summary>
/// 当前显示界面的下标
/// </summary>
public int ShowIndex
{
get { return _showIndex; }
set
{ SetAndNotify(ref _showIndex, value); }
}
private bool _isEnablePrev;
/// <summary>
/// 上一个按钮是否启用
/// </summary>
public bool IsEnablePrev
{
get { return _isEnablePrev; }
set
{ SetAndNotify(ref _isEnablePrev, value); }
}
private bool _isEnableNext = true;
/// <summary>
/// 下一个按钮是否启用
/// </summary>
public bool IsEnableNext
{
get { return _isEnableNext; }
set
{ SetAndNotify(ref _isEnableNext, value); }
}
public RotateCenterCalibContentsViewModel(IParameterManager paraManager)
{
_hardware = IoC.Get<HardwareManager>();
this._parameterManager = paraManager;
PrevCommand = new DelegateCommand(() =>
{
OnPrev();
});
NextCommand = new DelegateCommand(() =>
{
OnNext();
});
}
private bool _isInit = false;
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (!_isInit)
{
Screens = new ObservableCollection<Screen>();
// ====== 定义5组相机-轴配置 ======
var cameraConfigs = new List<(string Name, List<HardwareDevice> Device)>
{
("上相机旋转中心标定", _hardware.CameraAxisManager.TopCameraAxisDevices),
("广角相机旋转中心标定", _hardware.CameraAxisManager.WideCameraAxisDevices),
("下相机旋转中心标定", _hardware.CameraAxisManager.BottomCameraAxisDevices),
};
foreach (var (name, device) in cameraConfigs)
{
Screens.Add(RotateCenterCalibViewModel.Create(name, device, _hardware.Axis_WS_R));
}
// 默认显示第一个
ShowIndex = 0;
ShowScreenVM = Screens[0];
UpdateNavigationState();
_isInit = true;
}
}
public void viewUnLoad()
{
//_device.ChangeCameraMode(CameraTriggerMode.On);
//_pLCControlOperation.CyliderPress(true);
}
private void OnPrev()
{
if (ShowIndex <= 0) return;
ShowIndex--;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState();
}
private void OnNext()
{
if (ShowIndex >= Screens.Count - 1) return;
ShowIndex++;
ShowScreenVM = Screens[ShowIndex];
UpdateNavigationState();
}
/// <summary>
/// 统一更新导航状态和当前相机信息
/// </summary>
private void UpdateNavigationState()
{
IsEnablePrev = ShowIndex > 0;
IsEnableNext = ShowIndex < Screens.Count - 1;
// 更新当前相机名称
if (ShowScreenVM is RotateCenterCalibViewModel camVm)
{
CurrentCalibName = camVm.CalibName;
}
// 更新进度
ProgressText = $"{ShowIndex + 1} / {Screens.Count}";
}
}
}

View File

@@ -0,0 +1,66 @@
using MaxwellFramework.Core.Interfaces;
using MwFramework.Device;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.AlgorithmCalib.ViewModel
{
public class RotateCenterCalibViewModel:Screen,IPage
{
public string Name { get; set; } = "CameraRotateCenterCalib";
public string CalibName { get; set; }
private MwFramework.Controls.SystemCalib.RotateCenterSimpleViewModel _service;
public MwFramework.Controls.SystemCalib.RotateCenterSimpleViewModel Service
{
get { return _service; }
set
{
_service = value;
NotifyOfPropertyChange();
}
}
/// <summary>
/// 无参构造函数
/// </summary>
public RotateCenterCalibViewModel()
{
}
/// <summary>
/// 手动创建时使用的静态工厂方法
/// </summary>
/// <param name="cameraName">相机名称,如 "BottomCamera"</param>
/// <param name="device">对应的硬件设备</param>
public static RotateCenterCalibViewModel Create(string calibName, List<HardwareDevice> device, IAxis rotateAxis)
{
var vm = new RotateCenterCalibViewModel();
vm.Initialize(calibName, device, rotateAxis);
return vm;
}
private void Initialize(string calibName, List<HardwareDevice> device, IAxis rotateAxis)
{
CalibName = calibName;
var paramList = IoC.Get<IParameterManager>() as IParamList;
Service = new MwFramework.Controls.SystemCalib.RotateCenterSimpleViewModel(
device, rotateAxis, paramList);
//这里参数站位
Service.IsShowSolidLine = true;
Service.ShapeThickness = 1;
Service.DrawInConcurrency = false;
Service.IsAxisControlLDBVisible = Visibility.Visible;
}
}
}

View File

@@ -0,0 +1,179 @@
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.Motion;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Controls.ControlCanvas.Model;
using SemiconductorVisionAlgorithm.SemiParams;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.AlgorithmCalib.ViewModel
{
/// <summary>
/// 对标 OriginCalibViewModel页面主VM
/// 原 RotateFormCalibViewModel 中页面级逻辑搬到这里
/// </summary>
public class RotateMatixFormCalibViewModel : Screen, IPage
{
public string Name { get; set; } = "RotateFormCalib";
private readonly RotateMatixFormCalibSetting _setting;
private readonly HardwareManager _hardware;
private readonly RotateMatixFormCalibMotionService _motionService;
private readonly ApproachAlignmentService _approachAlignmentService;
private bool _isInitialized;
private RotateMatixFormModuleViewModel _module;
public RotateMatixFormModuleViewModel Module
{
get { return _module; }
private set { SetAndNotify(ref _module, value); }
}
private CameraAxisViewModel _cameraAxisViewModel;
public CameraAxisViewModel CameraAxisViewModel
{
get { return _cameraAxisViewModel; }
private set { SetAndNotify(ref _cameraAxisViewModel, value); }
}
public RotateMatixFormCalibViewModel(
HardwareManager hardware,
RotateMatixFormCalibMotionService motionService,
ApproachAlignmentService approachAlignmentService)
{
_hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
_motionService = motionService ?? throw new ArgumentNullException(nameof(motionService));
_setting = RotateMatixFormCalibSetting.LoadOrCreate();
_approachAlignmentService = approachAlignmentService;
}
protected override void OnViewLoaded()
{
// TODO: 对标 OriginCalibViewModel.OnViewLoaded()
if (_isInitialized) return;
// 1. 初始化相机
CameraAxisViewModel = IoC.Get<CameraAxisViewModel>();
CameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = _hardware.CameraAxisManager.TopCameraAxisDevices;
// 2. 初始化后处理器
InitializePostProcessors();
// 3. 构建模块 ViewModel
RebuildModuleViewModels();
_isInitialized = true;
}
/// <summary>
/// 对标 OriginCalibViewModel.InitializePostProcessors()
/// 为每个模块注入 RotateFormCalibPostProcessor
/// </summary>
private void InitializePostProcessors()
{
foreach (var module in _setting.Modules)
{
module.PostProcessor = new RotateMatixFormCalibPostProcessor(
_motionService,
_hardware,
module,
_approachAlignmentService,
GetRoiRect
);
}
}
/// <summary>
/// 对标 OriginCalibViewModel.RebuildModuleViewModels()
/// </summary>
private void RebuildModuleViewModels()
{
Module = null;
if (_setting.Modules.Count > 0)
{
Module = new RotateMatixFormModuleViewModel(
_setting.Modules[0],
_hardware,
_motionService,
_approachAlignmentService,
GetRoiRect);
Module.Index = 1;
NotifyOfPropertyChange(nameof(Module));
}
}
/// <summary>
/// 对标 OriginCalibViewModel.SaveConfig()
/// </summary>
public void SaveConfig()
{
// 填入:
try
{
_setting.Write();
MwMessageBox.Show("标定数据已保存。", "旋转表标定",
MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MwMessageBox.Show($"保存失败:{ex.Message}", "旋转表标定",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 对标 OriginCalibViewModel.ResetAll()
/// </summary>
public void ResetAll()
{
// 填入:
Module?.ResetModule();
}
/// <summary>
/// 获取当前 ROI 区域
/// </summary>
private Rectangle1 GetRoiRect()
{
try
{
var region = CameraAxisViewModel?.Regions;
if (region != null && region.Count > 0)
{
RectRegion rect = region[0].Region as RectRegion;
if (rect != null)
{
double start_x = rect.CenterPoint.X - rect.Width / 2;
double start_y = rect.CenterPoint.Y - rect.Height / 2;
double end_x = rect.CenterPoint.X + rect.Width / 2;
double end_y = rect.CenterPoint.Y + rect.Height / 2;
return new Rectangle1
{
Start_X = start_y,
Start_Y = start_x,
End_X = end_y,
End_Y = end_x
};
}
}
}
catch (Exception ex)
{
$"获取Region失败{ex.Message}".LogSysError();
}
return null;
}
}
}

View File

@@ -0,0 +1,471 @@
using MainShell.AlgorithmCalib.Common;
using MainShell.AlgorithmCalib.Model;
using MainShell.AlgorithmCalib.Service;
using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Hardware;
using MainShell.Motion;
using Maxwell.SemiFramework.DefaultConfig.Vision;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.Device;
using SemiconductorVisionAlgorithm.SemiParams;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.AlgorithmCalib.ViewModel
{
/// <summary>
/// 对标 OriginCalibModuleViewModel模块卡片VM
/// 原 RotateFormCalibViewModel 中模块级逻辑 + 原 Task 的编排逻辑搬到这里
/// </summary>
public class RotateMatixFormModuleViewModel : PropertyChangedBase
{
private const string CalibrationDialogCaption = "旋转表标定";
private readonly HardwareManager _hardware;
private readonly RotateMatixFormCalibMotionService _motionService;
private readonly RotateMatixFormCalibModuleItem _item;
private readonly ApproachAlignmentService _approachAlignmentService;
private readonly Func<Rectangle1> _roiRectProvider; // ROI 提供者委托
private int _index;
private bool _isCalibrating;
private bool _isWritten;
private CancellationTokenSource _cts;
public RotateMatixFormCalibModuleItem Item => _item;
#region OriginCalibModuleViewModel
public int Index
{
get { return _index; }
set { SetAndNotify(ref _index, value); }
}
public string ModuleName
{
get { return _item.ModuleName; }
set { _item.ModuleName = value; NotifyOfPropertyChange(nameof(ModuleName)); }
}
public bool IsCalibrating
{
get { return _isCalibrating; }
set
{
SetAndNotify(ref _isCalibrating, value);
NotifyOfPropertyChange(nameof(CanStartCalib));
}
}
public bool IsWritten
{
get { return _isWritten; }
set
{
SetAndNotify(ref _isWritten, value);
}
}
public bool CanStartCalib => !IsCalibrating;
private bool _isCalibrated;
public bool IsCalibrated
{
get { return _isCalibrated; }
set
{
SetAndNotify(ref _isCalibrated, value);
NotifyOfPropertyChange(nameof(CanWriteToController));
}
}
public bool CanWriteToController => !IsCalibrating && _isCalibrated;
#endregion
#region View
public double X1AvoidancePosition
{
get { return _item.X1AvoidancePosition; }
set { _item.X1AvoidancePosition = value; NotifyOfPropertyChange(nameof(X1AvoidancePosition)); }
}
public double RotateStep
{
get { return _item.RotateStep; }
set { _item.RotateStep = value; NotifyOfPropertyChange(nameof(RotateStep)); }
}
public double WaferStep
{
get { return _item.WaferStep; }
set { _item.WaferStep = value; NotifyOfPropertyChange(nameof(WaferStep)); }
}
public int RowCol
{
get { return _item.RowCol; }
set { _item.RowCol = value; NotifyOfPropertyChange(nameof(RowCol)); }
}
public int VerifyRowCol
{
get { return _item.VerifyRowCol; }
set { _item.VerifyRowCol = value; NotifyOfPropertyChange(nameof(VerifyRowCol)); }
}
public double VerifyWaferStep
{
get { return _item.VerifyWaferStep; }
set { _item.VerifyWaferStep = value; NotifyOfPropertyChange(nameof(VerifyWaferStep)); }
}
public double VerifyRotateRange
{
get { return _item.VerifyRotateRange; }
set { _item.VerifyRotateRange = value; NotifyOfPropertyChange(nameof(VerifyRotateRange)); }
}
public double StartX
{
get { return _item.StartX; }
set { _item.StartX = value; NotifyOfPropertyChange(nameof(StartX)); }
}
public double StartY
{
get { return _item.StartY; }
set { _item.StartY = value; NotifyOfPropertyChange(nameof(StartY)); }
}
public double VerifyStartX
{
get { return _item.VerifyStartX; }
set { _item.VerifyStartX = value; NotifyOfPropertyChange(nameof(VerifyStartX)); }
}
public double VerifyStartY
{
get { return _item.VerifyStartY; }
set { _item.VerifyStartY = value; NotifyOfPropertyChange(nameof(VerifyStartY)); }
}
#endregion
#region
private string _progressMessage = "";
public string ProgressMessage
{
get { return _progressMessage; }
set { SetAndNotify(ref _progressMessage, value); }
}
private int _progressCurrent;
public int ProgressCurrent
{
get { return _progressCurrent; }
set { SetAndNotify(ref _progressCurrent, value); }
}
private int _progressTotal;
public int ProgressTotal
{
get { return _progressTotal; }
set
{
SetAndNotify(ref _progressTotal, value);
NotifyOfPropertyChange(nameof(ProgressPercentage));
NotifyOfPropertyChange(nameof(ProgressText));
}
}
public double ProgressPercentage => ProgressTotal > 0
? (double)ProgressCurrent / ProgressTotal * 100
: 0;
public string ProgressText => ProgressTotal > 0
? $"{ProgressCurrent}/{ProgressTotal}"
: "";
private bool _isProgressVisible;
public bool IsProgressVisible
{
get { return _isProgressVisible; }
set { SetAndNotify(ref _isProgressVisible, value); }
}
#endregion
public RotateMatixFormModuleViewModel(
RotateMatixFormCalibModuleItem item,
HardwareManager hardware,
RotateMatixFormCalibMotionService motionService,
ApproachAlignmentService approachAlignmentService,
Func<Rectangle1> roiRectProvider)
{
_item = item ?? throw new ArgumentNullException(nameof(item));
_hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
_motionService = motionService ?? throw new ArgumentNullException(nameof(motionService));
_approachAlignmentService = approachAlignmentService ?? throw new ArgumentNullException(nameof(approachAlignmentService));
_roiRectProvider = roiRectProvider;
SubscribeToProgress();
}
private void SubscribeToProgress()
{
if (_item.PostProcessor is RotateMatixFormCalibPostProcessor processor)
{
processor.ProgressChanged += OnProgressChanged;
}
}
private void UnsubscribeFromProgress()
{
if (_item.PostProcessor is RotateMatixFormCalibPostProcessor processor)
{
processor.ProgressChanged -= OnProgressChanged;
}
}
private void OnProgressChanged(object sender, CalibrationProgressEventArgs e)
{
// 切换到 UI 线程更新
System.Windows.Application.Current.Dispatcher.Invoke(() =>
{
ProgressMessage = e.Message;
ProgressCurrent = e.Current;
ProgressTotal = e.Total;
NotifyOfPropertyChange(nameof(ProgressPercentage));
NotifyOfPropertyChange(nameof(ProgressText));
});
}
#region OriginCalibModuleViewModel
public void ResetModule()
{
IsCalibrated = false;
IsWritten = false;
NotifyOfPropertyChange(nameof(CanWriteToController));
}
#endregion
#region ViewModel
/// <summary>
/// 原 btnMoveToPosPosition()
/// 移动 X1 到避让位 + 移动 WS 到起始位置
/// </summary>
public void MoveToPosition()
{
Task.Run(() =>
{
try
{
_motionService.SafeMove(_hardware.Axis_PHS_X1, X1AvoidancePosition);
_motionService.MoveWsAvoidance(StartX, StartY);
}
catch (Exception ex)
{
MwMessageBox.Show($"移动失败:{ex.Message}", CalibrationDialogCaption,
MessageBoxButton.OK, MessageBoxImage.Error);
}
});
}
/// <summary>
/// 原 btnMoveToCenterPoint()
/// 中心对齐,读取并设置 StartX, StartY
/// </summary>
public void MoveToCenterPoint()
{
Task.Run(async () =>
{
try
{
bool success = await FindPointCircleAsync(CancellationToken.None);
if (success)
{
StartX = _hardware.Axis_PHS_X2.State.ActualPos;
StartY = _hardware.Axis_PHS_Y1.State.ActualPos;
MwMessageBox.Show($"对齐值X2:{StartX},Y1:{StartY}");
}
else
{
MwMessageBox.Show("对齐失败!");
}
}
catch (Exception ex)
{
MwMessageBox.Show($"异常:{ex.Message}");
}
});
}
/// <summary>
/// 原 btnVerifyApproachCenter()
/// 验证对齐
/// </summary>
public void VerifyApproachCenter()
{
Task.Run(async () =>
{
try
{
bool success = await FindPointCircleAsync(CancellationToken.None);
if (success)
{
VerifyStartX = _hardware.Axis_PHS_X2.State.ActualPos;
VerifyStartY = _hardware.Axis_PHS_Y1.State.ActualPos;
MwMessageBox.Show($"对齐值X2:{StartX},Y1:{StartY}");
}
else
{
MwMessageBox.Show("对齐失败!");
}
}
catch (Exception ex)
{
MwMessageBox.Show($"异常:{ex.Message}");
}
});
}
#endregion
#region StartCalib
/// <summary>
/// 对标 OriginCalibModuleViewModel.StartCalib()
/// 原 btnStart() + RotateFormCalibTask.RotateCalib() 的编排逻辑
/// </summary>
public async Task StartCalib()
{
if (IsCalibrating) return;
IsCalibrating = true;
IsProgressVisible = true; // 显示进度
_cts = new CancellationTokenSource();
try
{
// 标定流程(原 RotateFormCalibTask.RotateCalib 的编排)
if (_item.PostProcessor != null)
{
await ((RotateMatixFormCalibPostProcessor)_item.PostProcessor)
.ExecuteAsync(null, _cts.Token);
}
IsWritten = false;
}
catch (OperationCanceledException)
{
MwMessageBox.Show("标定已取消。", CalibrationDialogCaption,
MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch (Exception ex)
{
MwMessageBox.Show($"标定失败:{ex.Message}", CalibrationDialogCaption,
MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
IsCalibrating = false;
IsProgressVisible = false; // 隐藏进度
}
}
/// <summary>
/// 原 btnVerifyStart() + RotateFormCalibTask.RotateCalibVerify()
/// </summary>
public async Task StartVerify()
{
if (IsCalibrating) return;
IsCalibrating = true;
IsProgressVisible = true; // 显示进度
_cts = new CancellationTokenSource();
try
{
if (_item.PostProcessor != null)
{
await ((RotateMatixFormCalibPostProcessor)_item.PostProcessor)
.VerifyAsync(_cts.Token);
}
}
catch (OperationCanceledException)
{
MwMessageBox.Show("验证已取消。", CalibrationDialogCaption,
MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch (Exception ex)
{
MwMessageBox.Show($"验证失败:{ex.Message}", CalibrationDialogCaption,
MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
IsCalibrating = false;
IsProgressVisible = false; // 隐藏进度
}
}
/// <summary>
/// 原 btnStop() / btnVerifyStop()
/// </summary>
public void Stop()
{
_cts?.Cancel();
}
#endregion
#region
/// <summary>
/// 原 FindPointCircle()
/// </summary>
private async Task<bool> FindPointCircleAsync(CancellationToken token)
{
Rectangle1 roiRect = _roiRectProvider?.Invoke();
var request = new ApproachAlignmentRequest(
axes: new[]
{
new ApproachAlignmentAxis(GetAxisName(_hardware.Axis_X2), 0.005),
new ApproachAlignmentAxis(GetAxisName(_hardware.Axis_Y1), 0.005)
},
camera: CameraType.TopPositionCamera)
{
MaxIterations = 3,
RecognitionTimeoutMilliseconds = 5000,
RecognitionParameters = new CenterRecognitionParameters()
{
Type = CenterRecognitionType.EdgeCircle,
rectangle = roiRect
}
};
var result = await _approachAlignmentService
.ApproachAlignmentAsync(request, token);
return result.Succeeded;
}
private static string GetAxisName(IAxis axis)
{
if (axis == null)
{
return string.Empty;
}
return axis.Name;
}
#endregion
}
}