修复编译冲突;新增当运行完CNC,实时图像区应该显示最后一个节点的输出
This commit is contained in:
@@ -86,6 +86,7 @@ namespace XplorePlane.Services.Cnc
|
|||||||
|
|
||||||
bool cancelled = false;
|
bool cancelled = false;
|
||||||
bool allSucceeded = true;
|
bool allSucceeded = true;
|
||||||
|
BitmapSource lastResultImage = null;
|
||||||
|
|
||||||
foreach (var node in program.Nodes.OrderBy(n => n.Index))
|
foreach (var node in program.Nodes.OrderBy(n => n.Index))
|
||||||
{
|
{
|
||||||
@@ -124,7 +125,8 @@ namespace XplorePlane.Services.Cnc
|
|||||||
case InspectionModuleNode inspectionNode:
|
case InspectionModuleNode inspectionNode:
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ExecuteInspectionNodeAsync(runId, inspectionNode, sourceImage, cancellationToken);
|
var img = await ExecuteInspectionNodeAsync(runId, inspectionNode, sourceImage, cancellationToken);
|
||||||
|
if (img != null) lastResultImage = img;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -157,8 +159,10 @@ namespace XplorePlane.Services.Cnc
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InspectionModuleNode 完成时携带结果图像,供 ViewModel 缓存到节点上
|
||||||
|
var nodeResultImage = (node is InspectionModuleNode) ? lastResultImage : null;
|
||||||
var finalState = nodeSucceeded ? NodeExecutionState.Succeeded : NodeExecutionState.Failed;
|
var finalState = nodeSucceeded ? NodeExecutionState.Succeeded : NodeExecutionState.Failed;
|
||||||
progress?.Report(new CncNodeExecutionProgress(node.Id, finalState));
|
progress?.Report(new CncNodeExecutionProgress(node.Id, finalState, nodeResultImage));
|
||||||
|
|
||||||
if (!nodeSucceeded)
|
if (!nodeSucceeded)
|
||||||
allSucceeded = false;
|
allSucceeded = false;
|
||||||
@@ -179,7 +183,7 @@ namespace XplorePlane.Services.Cnc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExecuteInspectionNodeAsync(
|
private async Task<BitmapSource> ExecuteInspectionNodeAsync(
|
||||||
Guid runId,
|
Guid runId,
|
||||||
InspectionModuleNode inspectionNode,
|
InspectionModuleNode inspectionNode,
|
||||||
BitmapSource sourceImage,
|
BitmapSource sourceImage,
|
||||||
@@ -224,12 +228,13 @@ namespace XplorePlane.Services.Cnc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// result_overlay.bmp — 执行流水线后的结果图像
|
// result_overlay.bmp — 执行流水线后的结果图像
|
||||||
|
BitmapSource resultImage = null;
|
||||||
if (_pipelineExecutionService != null && inspectionNode.Pipeline?.Nodes?.Count > 0 && sourceImage != null)
|
if (_pipelineExecutionService != null && inspectionNode.Pipeline?.Nodes?.Count > 0 && sourceImage != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var pipelineNodes = BuildPipelineNodeViewModels(inspectionNode.Pipeline);
|
var pipelineNodes = BuildPipelineNodeViewModels(inspectionNode.Pipeline);
|
||||||
var resultImage = await _pipelineExecutionService.ExecutePipelineAsync(
|
resultImage = await _pipelineExecutionService.ExecutePipelineAsync(
|
||||||
pipelineNodes, sourceImage, null, cancellationToken);
|
pipelineNodes, sourceImage, null, cancellationToken);
|
||||||
|
|
||||||
if (resultImage != null)
|
if (resultImage != null)
|
||||||
@@ -243,6 +248,9 @@ namespace XplorePlane.Services.Cnc
|
|||||||
Height = resultImage.PixelHeight
|
Height = resultImage.PixelHeight
|
||||||
});
|
});
|
||||||
nodeResult.Status = InspectionNodeStatus.Succeeded;
|
nodeResult.Status = InspectionNodeStatus.Succeeded;
|
||||||
|
|
||||||
|
// 执行完立即更新主视口
|
||||||
|
_mainViewportService?.SetManualImage(resultImage, $"CNC节点:{inspectionNode.Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -254,6 +262,7 @@ namespace XplorePlane.Services.Cnc
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _store.AppendNodeResultAsync(nodeResult, pipelineSnapshot: pipelineSnapshot, assets: assets);
|
await _store.AppendNodeResultAsync(nodeResult, pipelineSnapshot: pipelineSnapshot, assets: assets);
|
||||||
|
return resultImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private System.Collections.Generic.IEnumerable<ViewModels.PipelineNodeViewModel> BuildPipelineNodeViewModels(PipelineModel pipeline)
|
private System.Collections.Generic.IEnumerable<ViewModels.PipelineNodeViewModel> BuildPipelineNodeViewModels(PipelineModel pipeline)
|
||||||
@@ -280,7 +289,7 @@ namespace XplorePlane.Services.Cnc
|
|||||||
{
|
{
|
||||||
var paramVm = new ViewModels.ProcessorParameterVM(def);
|
var paramVm = new ViewModels.ProcessorParameterVM(def);
|
||||||
if (nodeModel.Parameters != null && nodeModel.Parameters.TryGetValue(def.Name, out var saved))
|
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);
|
vm.Parameters.Add(paramVm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,6 +300,28 @@ namespace XplorePlane.Services.Cnc
|
|||||||
return nodes;
|
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)
|
private static byte[] EncodeBitmapToBmp(BitmapSource bitmap)
|
||||||
{
|
{
|
||||||
using var ms = new MemoryStream();
|
using var ms = new MemoryStream();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
|
|
||||||
namespace XplorePlane.Services.Cnc
|
namespace XplorePlane.Services.Cnc
|
||||||
@@ -15,6 +16,7 @@ namespace XplorePlane.Services.Cnc
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Progress report for a single CNC node execution.
|
/// Progress report for a single CNC node execution.
|
||||||
|
/// ResultImage is non-null when an InspectionModuleNode produces output.
|
||||||
/// </summary>
|
/// </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;
|
nodeVm.ExecutionState = progress.State;
|
||||||
if (progress.State == NodeExecutionState.Running)
|
if (progress.State == NodeExecutionState.Running)
|
||||||
StatusMessage = $"正在执行节点:{nodeVm.Name}({nodeVm.Index + 1}/{_currentProgram?.Nodes?.Count ?? 0})";
|
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)
|
else if (progress.State == NodeExecutionState.Failed)
|
||||||
{
|
{
|
||||||
HasExecutionError = true;
|
HasExecutionError = true;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Prism.Mvvm;
|
using Prism.Mvvm;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
|
|
||||||
namespace XplorePlane.ViewModels.Cnc
|
namespace XplorePlane.ViewModels.Cnc
|
||||||
@@ -16,6 +17,9 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
private bool _isExpanded = true;
|
private bool _isExpanded = true;
|
||||||
private NodeExecutionState _executionState = NodeExecutionState.Idle;
|
private NodeExecutionState _executionState = NodeExecutionState.Idle;
|
||||||
|
|
||||||
|
/// <summary>执行后缓存的流水线输出图像(仅 InspectionModuleNode)</summary>
|
||||||
|
public BitmapSource ResultImage { get; set; }
|
||||||
|
|
||||||
public CncNodeViewModel(CncNode model, Action<CncNodeViewModel, CncNode> modelChangedCallback)
|
public CncNodeViewModel(CncNode model, Action<CncNodeViewModel, CncNode> modelChangedCallback)
|
||||||
{
|
{
|
||||||
_model = model ?? throw new ArgumentNullException(nameof(model));
|
_model = model ?? throw new ArgumentNullException(nameof(model));
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ namespace XplorePlane.ViewModels
|
|||||||
public DelegateCommand InsertSaveNodeCommand { get; }
|
public DelegateCommand InsertSaveNodeCommand { get; }
|
||||||
public DelegateCommand InsertPauseDialogCommand { get; }
|
public DelegateCommand InsertPauseDialogCommand { get; }
|
||||||
public DelegateCommand InsertWaitDelayCommand { get; }
|
public DelegateCommand InsertWaitDelayCommand { get; }
|
||||||
|
public DelegateCommand RunCncCommand { get; }
|
||||||
|
public DelegateCommand StopCncCommand { get; }
|
||||||
|
|
||||||
// 硬件命令
|
// 硬件命令
|
||||||
public DelegateCommand AxisResetCommand { get; }
|
public DelegateCommand AxisResetCommand { get; }
|
||||||
@@ -176,6 +178,15 @@ namespace XplorePlane.ViewModels
|
|||||||
RunCncCommand.RaiseCanExecuteChanged();
|
RunCncCommand.RaiseCanExecuteChanged();
|
||||||
StopCncCommand.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.RunCncCommand.CanExecuteChanged += (s, e) => RunCncCommand.RaiseCanExecuteChanged();
|
||||||
_cncEditorViewModel.StopCncCommand.CanExecuteChanged += (s, e) => StopCncCommand.RaiseCanExecuteChanged();
|
_cncEditorViewModel.StopCncCommand.CanExecuteChanged += (s, e) => StopCncCommand.RaiseCanExecuteChanged();
|
||||||
@@ -249,97 +260,9 @@ namespace XplorePlane.ViewModels
|
|||||||
_logger.Info("MainViewModel 已初始化");
|
_logger.Info("MainViewModel 已初始化");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string LicenseInfo
|
|
||||||
{
|
|
||||||
get => _licenseInfo;
|
|
||||||
set => SetProperty(ref _licenseInfo, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string CncStatusMessage => _cncEditorViewModel.StatusMessage;
|
public string CncStatusMessage => _cncEditorViewModel.StatusMessage;
|
||||||
public bool CncHasExecutionError => _cncEditorViewModel.HasExecutionError;
|
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)
|
private void ShowWindow(Window window, string name)
|
||||||
{
|
{
|
||||||
window.Owner = Application.Current.MainWindow;
|
window.Owner = Application.Current.MainWindow;
|
||||||
@@ -761,5 +684,6 @@ namespace XplorePlane.ViewModels
|
|||||||
_logger.Info("[图像链路] OnPipelinePreviewUpdated:收到流水线结果图像,推送到 MainViewportService");
|
_logger.Info("[图像链路] OnPipelinePreviewUpdated:收到流水线结果图像,推送到 MainViewportService");
|
||||||
_mainViewportService.SetManualImage(payload.Image, string.Empty);
|
_mainViewportService.SetManualImage(payload.Image, string.Empty);
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user