From 822d31665d381c9d2d11c6a5f7c597ac60526f93 Mon Sep 17 00:00:00 2001 From: "zhengxuan.zhang" Date: Wed, 29 Apr 2026 09:42:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=AF=91=E5=86=B2?= =?UTF-8?q?=E7=AA=81=EF=BC=9B=E6=96=B0=E5=A2=9E=E5=BD=93=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E5=AE=8CCNC=EF=BC=8C=E5=AE=9E=E6=97=B6=E5=9B=BE=E5=83=8F?= =?UTF-8?q?=E5=8C=BA=E5=BA=94=E8=AF=A5=E6=98=BE=E7=A4=BA=E6=9C=80=E5=90=8E?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E7=9A=84=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Cnc/CncExecutionService.cs | 41 ++++++- .../Services/Cnc/ICncExecutionService.cs | 4 +- .../ViewModels/Cnc/CncEditorViewModel.cs | 6 ++ .../ViewModels/Cnc/CncNodeViewModel.cs | 4 + XplorePlane/ViewModels/Main/MainViewModel.cs | 100 +++--------------- 5 files changed, 61 insertions(+), 94 deletions(-) diff --git a/XplorePlane/Services/Cnc/CncExecutionService.cs b/XplorePlane/Services/Cnc/CncExecutionService.cs index 33d1782..1284ac4 100644 --- a/XplorePlane/Services/Cnc/CncExecutionService.cs +++ b/XplorePlane/Services/Cnc/CncExecutionService.cs @@ -86,6 +86,7 @@ namespace XplorePlane.Services.Cnc bool cancelled = false; bool allSucceeded = true; + BitmapSource lastResultImage = null; foreach (var node in program.Nodes.OrderBy(n => n.Index)) { @@ -124,7 +125,8 @@ namespace XplorePlane.Services.Cnc case InspectionModuleNode inspectionNode: try { - await ExecuteInspectionNodeAsync(runId, inspectionNode, sourceImage, cancellationToken); + var img = await ExecuteInspectionNodeAsync(runId, inspectionNode, sourceImage, cancellationToken); + if (img != null) lastResultImage = img; } catch (Exception ex) { @@ -157,8 +159,10 @@ namespace XplorePlane.Services.Cnc break; } + // InspectionModuleNode 完成时携带结果图像,供 ViewModel 缓存到节点上 + var nodeResultImage = (node is InspectionModuleNode) ? lastResultImage : null; var finalState = nodeSucceeded ? NodeExecutionState.Succeeded : NodeExecutionState.Failed; - progress?.Report(new CncNodeExecutionProgress(node.Id, finalState)); + progress?.Report(new CncNodeExecutionProgress(node.Id, finalState, nodeResultImage)); if (!nodeSucceeded) allSucceeded = false; @@ -179,7 +183,7 @@ namespace XplorePlane.Services.Cnc } } - private async Task ExecuteInspectionNodeAsync( + private async Task ExecuteInspectionNodeAsync( Guid runId, InspectionModuleNode inspectionNode, BitmapSource sourceImage, @@ -224,12 +228,13 @@ namespace XplorePlane.Services.Cnc } // result_overlay.bmp — 执行流水线后的结果图像 + BitmapSource resultImage = null; if (_pipelineExecutionService != null && inspectionNode.Pipeline?.Nodes?.Count > 0 && sourceImage != null) { try { var pipelineNodes = BuildPipelineNodeViewModels(inspectionNode.Pipeline); - var resultImage = await _pipelineExecutionService.ExecutePipelineAsync( + resultImage = await _pipelineExecutionService.ExecutePipelineAsync( pipelineNodes, sourceImage, null, cancellationToken); if (resultImage != null) @@ -243,6 +248,9 @@ namespace XplorePlane.Services.Cnc Height = resultImage.PixelHeight }); nodeResult.Status = InspectionNodeStatus.Succeeded; + + // 执行完立即更新主视口 + _mainViewportService?.SetManualImage(resultImage, $"CNC节点:{inspectionNode.Name}"); } } catch (Exception ex) @@ -254,6 +262,7 @@ namespace XplorePlane.Services.Cnc } await _store.AppendNodeResultAsync(nodeResult, pipelineSnapshot: pipelineSnapshot, assets: assets); + return resultImage; } private System.Collections.Generic.IEnumerable BuildPipelineNodeViewModels(PipelineModel pipeline) @@ -280,7 +289,7 @@ namespace XplorePlane.Services.Cnc { var paramVm = new ViewModels.ProcessorParameterVM(def); if (nodeModel.Parameters != null && nodeModel.Parameters.TryGetValue(def.Name, out var saved)) - paramVm.Value = saved; + paramVm.Value = ConvertSavedValue(saved, def.ValueType); vm.Parameters.Add(paramVm); } } @@ -291,6 +300,28 @@ namespace XplorePlane.Services.Cnc return nodes; } + /// + /// 将 JSON 反序列化后的 JsonElement 转换为参数所需的实际类型。 + /// + private static object ConvertSavedValue(object savedValue, Type targetType) + { + if (savedValue is not System.Text.Json.JsonElement jsonElement) + return savedValue; + + try + { + if (targetType == typeof(int)) return jsonElement.GetInt32(); + if (targetType == typeof(double)) return jsonElement.GetDouble(); + if (targetType == typeof(bool)) return jsonElement.GetBoolean(); + if (targetType == typeof(string)) return jsonElement.GetString() ?? string.Empty; + return jsonElement.ToString(); + } + catch + { + return jsonElement.ToString(); + } + } + private static byte[] EncodeBitmapToBmp(BitmapSource bitmap) { using var ms = new MemoryStream(); diff --git a/XplorePlane/Services/Cnc/ICncExecutionService.cs b/XplorePlane/Services/Cnc/ICncExecutionService.cs index 6a8328a..504c60b 100644 --- a/XplorePlane/Services/Cnc/ICncExecutionService.cs +++ b/XplorePlane/Services/Cnc/ICncExecutionService.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using System.Windows.Media.Imaging; using XplorePlane.Models; namespace XplorePlane.Services.Cnc @@ -15,6 +16,7 @@ namespace XplorePlane.Services.Cnc /// /// Progress report for a single CNC node execution. + /// ResultImage is non-null when an InspectionModuleNode produces output. /// - public record CncNodeExecutionProgress(Guid NodeId, NodeExecutionState State); + public record CncNodeExecutionProgress(Guid NodeId, NodeExecutionState State, BitmapSource ResultImage = null); } diff --git a/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs b/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs index 0ccd17b..a3427b5 100644 --- a/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs +++ b/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs @@ -501,6 +501,12 @@ namespace XplorePlane.ViewModels.Cnc nodeVm.ExecutionState = progress.State; if (progress.State == NodeExecutionState.Running) StatusMessage = $"正在执行节点:{nodeVm.Name}({nodeVm.Index + 1}/{_currentProgram?.Nodes?.Count ?? 0})"; + else if (progress.State == NodeExecutionState.Succeeded) + { + // 缓存结果图像到节点,供切换节点时使用 + if (progress.ResultImage != null) + nodeVm.ResultImage = progress.ResultImage; + } else if (progress.State == NodeExecutionState.Failed) { HasExecutionError = true; diff --git a/XplorePlane/ViewModels/Cnc/CncNodeViewModel.cs b/XplorePlane/ViewModels/Cnc/CncNodeViewModel.cs index fef97b5..fb8d83f 100644 --- a/XplorePlane/ViewModels/Cnc/CncNodeViewModel.cs +++ b/XplorePlane/ViewModels/Cnc/CncNodeViewModel.cs @@ -1,6 +1,7 @@ using Prism.Mvvm; using System; using System.Collections.ObjectModel; +using System.Windows.Media.Imaging; using XplorePlane.Models; namespace XplorePlane.ViewModels.Cnc @@ -16,6 +17,9 @@ namespace XplorePlane.ViewModels.Cnc private bool _isExpanded = true; private NodeExecutionState _executionState = NodeExecutionState.Idle; + /// 执行后缓存的流水线输出图像(仅 InspectionModuleNode) + public BitmapSource ResultImage { get; set; } + public CncNodeViewModel(CncNode model, Action modelChangedCallback) { _model = model ?? throw new ArgumentNullException(nameof(model)); diff --git a/XplorePlane/ViewModels/Main/MainViewModel.cs b/XplorePlane/ViewModels/Main/MainViewModel.cs index 88a747c..22d3cae 100644 --- a/XplorePlane/ViewModels/Main/MainViewModel.cs +++ b/XplorePlane/ViewModels/Main/MainViewModel.cs @@ -76,6 +76,8 @@ namespace XplorePlane.ViewModels public DelegateCommand InsertSaveNodeCommand { get; } public DelegateCommand InsertPauseDialogCommand { get; } public DelegateCommand InsertWaitDelayCommand { get; } + public DelegateCommand RunCncCommand { get; } + public DelegateCommand StopCncCommand { get; } // 硬件命令 public DelegateCommand AxisResetCommand { get; } @@ -176,6 +178,15 @@ namespace XplorePlane.ViewModels RunCncCommand.RaiseCanExecuteChanged(); StopCncCommand.RaiseCanExecuteChanged(); } + else if (e.PropertyName == nameof(CncEditorViewModel.SelectedNode)) + { + var node = _cncEditorViewModel.SelectedNode; + if (node?.ResultImage != null) + { + _logger.Info("[图像链路] 切换到节点 [{Name}],显示缓存结果图像", node.Name); + _mainViewportService.SetManualImage(node.ResultImage, $"CNC节点:{node.Name}"); + } + } }; _cncEditorViewModel.RunCncCommand.CanExecuteChanged += (s, e) => RunCncCommand.RaiseCanExecuteChanged(); _cncEditorViewModel.StopCncCommand.CanExecuteChanged += (s, e) => StopCncCommand.RaiseCanExecuteChanged(); @@ -249,97 +260,9 @@ namespace XplorePlane.ViewModels _logger.Info("MainViewModel 已初始化"); } - public string LicenseInfo - { - get => _licenseInfo; - set => SetProperty(ref _licenseInfo, value); - } - public string CncStatusMessage => _cncEditorViewModel.StatusMessage; public bool CncHasExecutionError => _cncEditorViewModel.HasExecutionError; - public ObservableCollection NavigationTree { get; set; } - - public DelegateCommand NavigateHomeCommand { get; set; } - public DelegateCommand NavigateInspectCommand { get; set; } - public DelegateCommand OpenFileCommand { get; set; } - public DelegateCommand ExportCommand { get; set; } - public DelegateCommand ClearCommand { get; set; } - public DelegateCommand EditPropertiesCommand { get; set; } - - public DelegateCommand OpenImageProcessingCommand { get; } - public DelegateCommand LoadImageCommand { get; } - public DelegateCommand OpenPipelineEditorCommand { get; } - public DelegateCommand OpenCncEditorCommand { get; } - public DelegateCommand OpenMatrixEditorCommand { get; } - public DelegateCommand OpenToolboxCommand { get; } - public DelegateCommand OpenLibraryVersionsCommand { get; } - public DelegateCommand OpenUserManualCommand { get; } - public DelegateCommand OpenCameraSettingsCommand { get; } - public DelegateCommand NewCncProgramCommand { get; } - public DelegateCommand SaveCncProgramCommand { get; } - public DelegateCommand LoadCncProgramCommand { get; } - public DelegateCommand InsertReferencePointCommand { get; } - public DelegateCommand InsertSavePositionCommand { get; } - public DelegateCommand InsertCompleteProgramCommand { get; } - public DelegateCommand InsertInspectionMarkerCommand { get; } - public DelegateCommand InsertInspectionModuleCommand { get; } - public DelegateCommand InsertSaveNodeCommand { get; } - public DelegateCommand InsertPauseDialogCommand { get; } - public DelegateCommand InsertWaitDelayCommand { get; } - public DelegateCommand RunCncCommand { get; } - public DelegateCommand StopCncCommand { get; } - - public DelegateCommand AxisResetCommand { get; } - public DelegateCommand OpenDetectorConfigCommand { get; } - public DelegateCommand OpenMotionDebugCommand { get; } - public DelegateCommand OpenPlcAddrConfigCommand { get; } - public DelegateCommand OpenRaySourceConfigCommand { get; } - public DelegateCommand WarmUpCommand { get; } - - public DelegateCommand PointDistanceMeasureCommand { get; } - public DelegateCommand PointLineDistanceMeasureCommand { get; } - public DelegateCommand AngleMeasureCommand { get; } - public DelegateCommand ThroughHoleFillRateMeasureCommand { get; } - public DelegateCommand ToggleCrosshairCommand { get; } - - public DelegateCommand OpenLanguageSwitcherCommand { get; } - public DelegateCommand OpenRealTimeLogViewerCommand { get; } - public DelegateCommand UseLiveDetectorSourceCommand { get; } - - public bool IsMainViewportRealtimeEnabled - { - get => _mainViewportService.IsRealtimeDisplayEnabled; - set - { - if (_mainViewportService.IsRealtimeDisplayEnabled == value) - return; - - _mainViewportService.SetRealtimeDisplayEnabled(value); - RaisePropertyChanged(); - } - } - - public bool IsUsingLiveDetectorSource => _mainViewportService.CurrentSourceMode == MainViewportSourceMode.LiveDetector; - - public object ImagePanelContent - { - get => _imagePanelContent; - set => SetProperty(ref _imagePanelContent, value); - } - - public GridLength ImagePanelWidth - { - get => _imagePanelWidth; - set => SetProperty(ref _imagePanelWidth, value); - } - - public GridLength ViewportPanelWidth - { - get => _viewportPanelWidth; - set => SetProperty(ref _viewportPanelWidth, value); - } - private void ShowWindow(Window window, string name) { window.Owner = Application.Current.MainWindow; @@ -761,5 +684,6 @@ namespace XplorePlane.ViewModels _logger.Info("[图像链路] OnPipelinePreviewUpdated:收到流水线结果图像,推送到 MainViewportService"); _mainViewportService.SetManualImage(payload.Image, string.Empty); } + #endregion } }