新增CNC执行数据源的生成

This commit is contained in:
zhengxuan.zhang
2026-04-28 12:45:23 +08:00
parent 514eace979
commit 80fddc45dd
2 changed files with 159 additions and 20 deletions
+1
View File
@@ -62,3 +62,4 @@ ExternalLibraries/Models/
# 排除测试目录 # 排除测试目录
XplorePlane/Tests/ XplorePlane/Tests/
ExternalLibraries/Telerik/ ExternalLibraries/Telerik/
build_out.txt
+158 -20
View File
@@ -1,12 +1,17 @@
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Media.Imaging;
using XP.Common.GeneralForm.Views; using XP.Common.GeneralForm.Views;
using XP.Common.Logging.Interfaces; using XP.Common.Logging.Interfaces;
using XplorePlane.Models; using XplorePlane.Models;
using XplorePlane.Services.InspectionResults; using XplorePlane.Services.InspectionResults;
using XplorePlane.Services.MainViewport;
using XplorePlane.ViewModels;
namespace XplorePlane.Services.Cnc namespace XplorePlane.Services.Cnc
{ {
@@ -17,11 +22,22 @@ namespace XplorePlane.Services.Cnc
{ {
private readonly IInspectionResultStore _store; private readonly IInspectionResultStore _store;
private readonly ILoggerService _logger; 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)); _store = store ?? throw new ArgumentNullException(nameof(store));
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); _logger = logger ?? throw new ArgumentNullException(nameof(logger));
_mainViewportService = mainViewportService;
_pipelineExecutionService = pipelineExecutionService;
_imageProcessingService = imageProcessingService;
} }
public async Task ExecuteAsync(CncProgram program, IProgress<CncNodeExecutionProgress> progress, CancellationToken cancellationToken) public async Task ExecuteAsync(CncProgram program, IProgress<CncNodeExecutionProgress> progress, CancellationToken cancellationToken)
@@ -32,6 +48,10 @@ namespace XplorePlane.Services.Cnc
int inspectionNodeCount = program.Nodes.OfType<InspectionModuleNode>().Count(); int inspectionNodeCount = program.Nodes.OfType<InspectionModuleNode>().Count();
// 获取当前源图像(用于 run/source.bmp
var sourceImage = _mainViewportService?.LatestManualImage as BitmapSource
?? _mainViewportService?.CurrentDisplayImage as BitmapSource;
Guid runId; Guid runId;
try try
{ {
@@ -41,7 +61,21 @@ namespace XplorePlane.Services.Cnc
NodeCount = inspectionNodeCount, NodeCount = inspectionNodeCount,
StartedAt = DateTime.UtcNow 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; runId = runRecord.RunId;
} }
catch (Exception ex) catch (Exception ex)
@@ -90,24 +124,7 @@ namespace XplorePlane.Services.Cnc
case InspectionModuleNode inspectionNode: case InspectionModuleNode inspectionNode:
try try
{ {
var nodeResult = new InspectionNodeResult await ExecuteInspectionNodeAsync(runId, inspectionNode, sourceImage, cancellationToken);
{
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);
} }
catch (Exception ex) 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<InspectionAssetWriteRequest>();
// 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<CncExecutionService>().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<ViewModels.PipelineNodeViewModel> BuildPipelineNodeViewModels(PipelineModel pipeline)
{
var nodes = new System.Collections.Generic.List<ViewModels.PipelineNodeViewModel>();
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) private static async Task ExecuteWaitDelayWithProgressAsync(WaitDelayNode waitNode, CancellationToken cancellationToken)
{ {
int totalMs = waitNode.DelayMilliseconds; int totalMs = waitNode.DelayMilliseconds;