diff --git a/XplorePlane/ViewModels/ImageProcessing/BgaDetectionViewModel.cs b/XplorePlane/ViewModels/ImageProcessing/BgaDetectionViewModel.cs index 5d6b31f..55885f3 100644 --- a/XplorePlane/ViewModels/ImageProcessing/BgaDetectionViewModel.cs +++ b/XplorePlane/ViewModels/ImageProcessing/BgaDetectionViewModel.cs @@ -14,24 +14,35 @@ using Prism.Mvvm; using XP.ImageProcessing.Processors; using XP.ImageProcessing.RoiControl.Controls; using XP.ImageProcessing.RoiControl.Models; +using XplorePlane.Models; using XplorePlane.Services.MainViewport; +using XplorePlane.ViewModels.Cnc; namespace XplorePlane.ViewModels.ImageProcessing { public class BgaDetectionViewModel : BindableBase { private readonly IMainViewportService _viewportService; + private CncEditorViewModel _cncEditorViewModel; private BitmapSource _originalImage; private System.Threading.CancellationTokenSource _debounceCts; private const int DebounceMs = 300; + private const string BgaVoidRateOperatorKey = "BgaVoidRate"; public BgaDetectionViewModel(IMainViewportService viewportService) { _viewportService = viewportService; ExecuteCommand = new DelegateCommand(Execute); + InsertToCncCommand = new DelegateCommand(ExecuteInsertToCnc); PropertyChanged += OnAnyPropertyChanged; } + /// 设置 CNC 编辑器 ViewModel 引用,用于插入参数到激活的 CNC 位置节点 + public void SetCncEditorViewModel(CncEditorViewModel cncEditorViewModel) + { + _cncEditorViewModel = cncEditorViewModel; + } + private void OnAnyPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { // 排除结果属性和ROI开关,只监听参数变化 @@ -265,6 +276,7 @@ namespace XplorePlane.ViewModels.ImageProcessing public System.Collections.ObjectModel.ObservableCollection Results { get; } = new(); public DelegateCommand ExecuteCommand { get; } + public DelegateCommand InsertToCncCommand { get; } private void Execute() { @@ -361,6 +373,119 @@ namespace XplorePlane.ViewModels.ImageProcessing } } + /// + /// 将当前 ROI 和算子参数插入到激活的 CNC 位置节点的检测模块中 BGA空洞模块的参数 + /// + private void ExecuteInsertToCnc() + { + if (_cncEditorViewModel == null) + { + MessageBox.Show("CNC 编辑器未就绪,无法插入参数。", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + // 查找当前激活的检测模块节点(SelectedNode 本身是 InspectionModule,或其父节点是 SavePosition) + var selectedNode = _cncEditorViewModel.SelectedNode; + CncNodeViewModel targetModuleNode = null; + + if (selectedNode == null) + { + MessageBox.Show("请先在 CNC 编辑器中选择一个位置节点或检测模块。", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + if (selectedNode.IsInspectionModule) + { + // 直接选中的是检测模块 + targetModuleNode = selectedNode; + } + else if (selectedNode.IsSavePosition) + { + // 选中的是位置节点,查找其子节点中的检测模块 + targetModuleNode = selectedNode.Children.FirstOrDefault(c => c.IsInspectionModule); + } + else + { + // 尝试在 Nodes 中找到当前选中节点所属的 SavePosition 的检测模块 + var allNodes = _cncEditorViewModel.Nodes; + // 向前查找最近的 SavePosition + CncNodeViewModel ownerPosition = null; + foreach (var node in allNodes) + { + if (node.IsSavePosition) + ownerPosition = node; + if (node.Id == selectedNode.Id) + break; + } + if (ownerPosition != null) + targetModuleNode = ownerPosition.Children.FirstOrDefault(c => c.IsInspectionModule); + } + + if (targetModuleNode == null) + { + MessageBox.Show("未找到激活的检测模块节点。\n请在 CNC 编辑器中选择一个包含检测模块的位置节点。", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); + return; + } + + // 获取或创建 Pipeline + var pipeline = targetModuleNode.Pipeline ?? new PipelineModel { Name = targetModuleNode.Name }; + + // 查找已有的 BgaVoidRate 算子节点 + var bgaNode = pipeline.Nodes.FirstOrDefault(n => + string.Equals(n.OperatorKey, BgaVoidRateOperatorKey, StringComparison.OrdinalIgnoreCase)); + + if (bgaNode == null) + { + // 不存在则新建一个 BgaVoidRate 节点并添加到流水线末尾 + bgaNode = new PipelineNodeModel + { + Id = Guid.NewGuid(), + OperatorKey = BgaVoidRateOperatorKey, + Order = pipeline.Nodes.Count, + IsEnabled = true, + Parameters = new Dictionary() + }; + pipeline.Nodes.Add(bgaNode); + } + + // 写入当前参数 + var parameters = bgaNode.Parameters; + parameters["BgaMinArea"] = BgaMinArea; + parameters["BgaMaxArea"] = BgaMaxArea; + parameters["BgaBlurSize"] = BlurSize; + parameters["BgaCircularity"] = Circularity; + parameters["MinThreshold"] = MinThreshold; + parameters["MaxThreshold"] = MaxThreshold; + parameters["MinVoidArea"] = MinVoidArea; + parameters["VoidLimit"] = VoidLimit; + + // 写入 ROI 参数 + if (RoiEnabled && _roiShape != null && _roiShape.Points.Count >= 3) + { + parameters["RoiMode"] = "Polygon"; + int count = Math.Min(_roiShape.Points.Count, 32); + parameters["PolyCount"] = count; + for (int i = 0; i < count; i++) + { + parameters[$"PolyX{i}"] = (int)_roiShape.Points[i].X; + parameters[$"PolyY{i}"] = (int)_roiShape.Points[i].Y; + } + } + else + { + parameters["RoiMode"] = "None"; + parameters["PolyCount"] = 0; + } + + // 更新 Pipeline 到节点 + pipeline.UpdatedAt = DateTime.UtcNow; + targetModuleNode.Pipeline = pipeline; + + MessageBox.Show( + $"已将 BGA 检测参数插入到检测模块「{targetModuleNode.Name}」。", + "插入成功", MessageBoxButton.OK, MessageBoxImage.Information); + } + private BitmapSource RenderResults(Image grayImage, IDictionary output) { if (!output.ContainsKey("BgaVoidResult")) return null; diff --git a/XplorePlane/Views/ImageProcessing/BgaDetectionPanel.xaml b/XplorePlane/Views/ImageProcessing/BgaDetectionPanel.xaml index 58f5366..884d4df 100644 --- a/XplorePlane/Views/ImageProcessing/BgaDetectionPanel.xaml +++ b/XplorePlane/Views/ImageProcessing/BgaDetectionPanel.xaml @@ -76,6 +76,9 @@ + diff --git a/XplorePlane/Views/ImageProcessing/BgaDetectionPanel.xaml.cs b/XplorePlane/Views/ImageProcessing/BgaDetectionPanel.xaml.cs index 5eb0174..86e335e 100644 --- a/XplorePlane/Views/ImageProcessing/BgaDetectionPanel.xaml.cs +++ b/XplorePlane/Views/ImageProcessing/BgaDetectionPanel.xaml.cs @@ -2,6 +2,7 @@ using System.Windows; using Prism.Ioc; using XP.ImageProcessing.RoiControl.Controls; using XplorePlane.Services.MainViewport; +using XplorePlane.ViewModels.Cnc; using XplorePlane.ViewModels.ImageProcessing; namespace XplorePlane.Views.ImageProcessing @@ -24,6 +25,17 @@ namespace XplorePlane.Views.ImageProcessing if (DataContext is BgaDetectionViewModel vm) vm.SetCanvas(canvas); } + + // 从 MainViewModel 获取 CncEditorViewModel 引用 + if (DataContext is BgaDetectionViewModel bgaVm && Owner?.DataContext is ViewModels.MainViewModel mainVm) + { + var cncEditorField = mainVm.GetType().GetField("_cncEditorViewModel", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + if (cncEditorField?.GetValue(mainVm) is CncEditorViewModel cncEditor) + { + bgaVm.SetCncEditorViewModel(cncEditor); + } + } }; Closed += (s, e) =>