修复编译冲突;新增当运行完CNC,实时图像区应该显示最后一个节点的输出

This commit is contained in:
zhengxuan.zhang
2026-04-29 09:42:19 +08:00
parent 130f39db49
commit 822d31665d
5 changed files with 61 additions and 94 deletions
@@ -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<BitmapSource> 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<ViewModels.PipelineNodeViewModel> 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;
}
/// <summary>
/// 将 JSON 反序列化后的 JsonElement 转换为参数所需的实际类型。
/// </summary>
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();
@@ -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
/// <summary>
/// Progress report for a single CNC node execution.
/// ResultImage is non-null when an InspectionModuleNode produces output.
/// </summary>
public record CncNodeExecutionProgress(Guid NodeId, NodeExecutionState State);
public record CncNodeExecutionProgress(Guid NodeId, NodeExecutionState State, BitmapSource ResultImage = null);
}
@@ -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;
@@ -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;
/// <summary>执行后缓存的流水线输出图像(仅 InspectionModuleNode</summary>
public BitmapSource ResultImage { get; set; }
public CncNodeViewModel(CncNode model, Action<CncNodeViewModel, CncNode> modelChangedCallback)
{
_model = model ?? throw new ArgumentNullException(nameof(model));
+12 -88
View File
@@ -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<object> 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
}
}