添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
// ===== 步骤1:Pad捕获 =====
|
||||
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);
|
||||
}
|
||||
|
||||
// ===== 步骤2:Die网格飞拍 =====
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
/// 融合标定子窗口VM(Die/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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// 创建子窗口 VM(Pad模式)
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user