添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user