From 80fddc45dda06113a5e1e32802f1cf29061f2db4 Mon Sep 17 00:00:00 2001 From: "zhengxuan.zhang" Date: Tue, 28 Apr 2026 12:45:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9ECNC=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=BA=90=E7=9A=84=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../Services/Cnc/CncExecutionService.cs | 178 ++++++++++++++++-- 2 files changed, 159 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 0b8391a..cdfade7 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,4 @@ ExternalLibraries/Models/ # 排除测试目录 XplorePlane/Tests/ ExternalLibraries/Telerik/ +build_out.txt diff --git a/XplorePlane/Services/Cnc/CncExecutionService.cs b/XplorePlane/Services/Cnc/CncExecutionService.cs index fa23c8c..33d1782 100644 --- a/XplorePlane/Services/Cnc/CncExecutionService.cs +++ b/XplorePlane/Services/Cnc/CncExecutionService.cs @@ -1,12 +1,17 @@ using System; +using System.IO; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Windows; +using System.Windows.Media.Imaging; using XP.Common.GeneralForm.Views; using XP.Common.Logging.Interfaces; using XplorePlane.Models; using XplorePlane.Services.InspectionResults; +using XplorePlane.Services.MainViewport; +using XplorePlane.ViewModels; namespace XplorePlane.Services.Cnc { @@ -17,11 +22,22 @@ namespace XplorePlane.Services.Cnc { private readonly IInspectionResultStore _store; private readonly ILoggerService _logger; + private readonly IMainViewportService _mainViewportService; + private readonly IPipelineExecutionService _pipelineExecutionService; + private readonly IImageProcessingService _imageProcessingService; - public CncExecutionService(IInspectionResultStore store, ILoggerService logger) + public CncExecutionService( + IInspectionResultStore store, + ILoggerService logger, + IMainViewportService mainViewportService, + IPipelineExecutionService pipelineExecutionService, + IImageProcessingService imageProcessingService) { _store = store ?? throw new ArgumentNullException(nameof(store)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _mainViewportService = mainViewportService; + _pipelineExecutionService = pipelineExecutionService; + _imageProcessingService = imageProcessingService; } public async Task ExecuteAsync(CncProgram program, IProgress progress, CancellationToken cancellationToken) @@ -32,6 +48,10 @@ namespace XplorePlane.Services.Cnc int inspectionNodeCount = program.Nodes.OfType().Count(); + // 获取当前源图像(用于 run/source.bmp) + var sourceImage = _mainViewportService?.LatestManualImage as BitmapSource + ?? _mainViewportService?.CurrentDisplayImage as BitmapSource; + Guid runId; try { @@ -41,7 +61,21 @@ namespace XplorePlane.Services.Cnc NodeCount = inspectionNodeCount, StartedAt = DateTime.UtcNow }; - await _store.BeginRunAsync(runRecord); + + InspectionAssetWriteRequest sourceAsset = null; + if (sourceImage != null) + { + sourceAsset = new InspectionAssetWriteRequest + { + AssetType = InspectionAssetType.RunSourceImage, + Content = EncodeBitmapToBmp(sourceImage), + FileFormat = "bmp", + Width = sourceImage.PixelWidth, + Height = sourceImage.PixelHeight + }; + } + + await _store.BeginRunAsync(runRecord, sourceAsset); runId = runRecord.RunId; } catch (Exception ex) @@ -90,24 +124,7 @@ namespace XplorePlane.Services.Cnc case InspectionModuleNode inspectionNode: try { - var nodeResult = new InspectionNodeResult - { - RunId = runId, - NodeId = inspectionNode.Id, - NodeIndex = inspectionNode.Index, - NodeName = inspectionNode.Name - }; - - PipelineExecutionSnapshot pipelineSnapshot = inspectionNode.Pipeline != null - ? new PipelineExecutionSnapshot - { - RunId = runId, - NodeId = inspectionNode.Id, - PipelineName = inspectionNode.Pipeline.Name - } - : null; - - await _store.AppendNodeResultAsync(nodeResult, pipelineSnapshot: pipelineSnapshot); + await ExecuteInspectionNodeAsync(runId, inspectionNode, sourceImage, cancellationToken); } catch (Exception ex) { @@ -162,6 +179,127 @@ namespace XplorePlane.Services.Cnc } } + private async Task ExecuteInspectionNodeAsync( + Guid runId, + InspectionModuleNode inspectionNode, + BitmapSource sourceImage, + CancellationToken cancellationToken) + { + var nodeResult = new InspectionNodeResult + { + RunId = runId, + NodeId = inspectionNode.Id, + NodeIndex = inspectionNode.Index, + NodeName = inspectionNode.Name, + PipelineName = inspectionNode.Pipeline?.Name ?? string.Empty + }; + + PipelineExecutionSnapshot pipelineSnapshot = null; + if (inspectionNode.Pipeline != null) + { + var pipelineJson = JsonSerializer.Serialize(inspectionNode.Pipeline); + pipelineSnapshot = new PipelineExecutionSnapshot + { + RunId = runId, + NodeId = inspectionNode.Id, + PipelineName = inspectionNode.Pipeline.Name, + PipelineDefinitionJson = pipelineJson + }; + } + + // 构建资产列表 + var assets = new System.Collections.Generic.List(); + + // input.bmp — 当前源图像 + if (sourceImage != null) + { + assets.Add(new InspectionAssetWriteRequest + { + AssetType = InspectionAssetType.NodeInputImage, + Content = EncodeBitmapToBmp(sourceImage), + FileFormat = "bmp", + Width = sourceImage.PixelWidth, + Height = sourceImage.PixelHeight + }); + } + + // result_overlay.bmp — 执行流水线后的结果图像 + if (_pipelineExecutionService != null && inspectionNode.Pipeline?.Nodes?.Count > 0 && sourceImage != null) + { + try + { + var pipelineNodes = BuildPipelineNodeViewModels(inspectionNode.Pipeline); + var resultImage = await _pipelineExecutionService.ExecutePipelineAsync( + pipelineNodes, sourceImage, null, cancellationToken); + + if (resultImage != null) + { + assets.Add(new InspectionAssetWriteRequest + { + AssetType = InspectionAssetType.NodeResultImage, + Content = EncodeBitmapToBmp(resultImage), + FileFormat = "bmp", + Width = resultImage.PixelWidth, + Height = resultImage.PixelHeight + }); + nodeResult.Status = InspectionNodeStatus.Succeeded; + } + } + catch (Exception ex) + { + _logger.ForModule().Warn( + "Pipeline execution failed for node '{0}': {1}", inspectionNode.Name, ex.Message); + nodeResult.Status = InspectionNodeStatus.Failed; + } + } + + await _store.AppendNodeResultAsync(nodeResult, pipelineSnapshot: pipelineSnapshot, assets: assets); + } + + private System.Collections.Generic.IEnumerable BuildPipelineNodeViewModels(PipelineModel pipeline) + { + var nodes = new System.Collections.Generic.List(); + if (pipeline?.Nodes == null) return nodes; + + foreach (var nodeModel in pipeline.Nodes.OrderBy(n => n.Order)) + { + var displayName = _imageProcessingService?.GetProcessorDisplayName(nodeModel.OperatorKey) ?? nodeModel.OperatorKey; + var vm = new ViewModels.PipelineNodeViewModel(nodeModel.OperatorKey, displayName, string.Empty) + { + Order = nodeModel.Order, + IsEnabled = nodeModel.IsEnabled + }; + + // 加载参数定义并恢复保存的值 + if (_imageProcessingService != null) + { + var paramDefs = _imageProcessingService.GetProcessorParameters(nodeModel.OperatorKey); + if (paramDefs != null) + { + foreach (var def in paramDefs) + { + var paramVm = new ViewModels.ProcessorParameterVM(def); + if (nodeModel.Parameters != null && nodeModel.Parameters.TryGetValue(def.Name, out var saved)) + paramVm.Value = saved; + vm.Parameters.Add(paramVm); + } + } + } + + nodes.Add(vm); + } + return nodes; + } + + private static byte[] EncodeBitmapToBmp(BitmapSource bitmap) + { + using var ms = new MemoryStream(); + var encoder = new BmpBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(bitmap)); + encoder.Save(ms); + return ms.ToArray(); + } + private static async Task ExecuteWaitDelayWithProgressAsync(WaitDelayNode waitNode, CancellationToken cancellationToken) { int totalMs = waitNode.DelayMilliseconds;