diff --git a/XP.Hardware.Detector/Config/SimulatedDetectorConfig.cs b/XP.Hardware.Detector/Config/SimulatedDetectorConfig.cs
index 8650451..5587742 100644
--- a/XP.Hardware.Detector/Config/SimulatedDetectorConfig.cs
+++ b/XP.Hardware.Detector/Config/SimulatedDetectorConfig.cs
@@ -15,12 +15,12 @@ namespace XP.Hardware.Detector.Config
///
/// 合成帧宽度(像素)| Synthetic frame width (pixels)
///
- public int Width { get; set; } = 256;
+ public int Width { get; set; } = 2800;
///
/// 合成帧高度(像素)| Synthetic frame height (pixels)
///
- public int Height { get; set; } = 256;
+ public int Height { get; set; } = 2800;
///
/// 模拟帧率(帧/秒)| Simulated frame rate (fps)
diff --git a/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs b/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs
index 5e4ae4d..13a55ea 100644
--- a/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs
+++ b/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs
@@ -32,6 +32,7 @@ namespace XplorePlane.ViewModels.Cnc
private readonly ICncExecutionService _cncExecutionService;
private readonly IXpDataPathService _dataPathService;
private readonly IPipelinePersistenceService _pipelinePersistenceService;
+ private readonly IImageProcessingService _imageProcessingService;
private CncProgram _currentProgram;
private ObservableCollection _nodes;
@@ -57,7 +58,8 @@ namespace XplorePlane.ViewModels.Cnc
ILoggerService logger,
ICncExecutionService cncExecutionService,
IXpDataPathService dataPathService,
- IPipelinePersistenceService pipelinePersistenceService)
+ IPipelinePersistenceService pipelinePersistenceService,
+ IImageProcessingService imageProcessingService = null)
{
_cncProgramService = cncProgramService ?? throw new ArgumentNullException(nameof(cncProgramService));
ArgumentNullException.ThrowIfNull(appStateService);
@@ -66,6 +68,7 @@ namespace XplorePlane.ViewModels.Cnc
_cncExecutionService = cncExecutionService ?? throw new ArgumentNullException(nameof(cncExecutionService));
_dataPathService = dataPathService ?? throw new ArgumentNullException(nameof(dataPathService));
_pipelinePersistenceService = pipelinePersistenceService ?? throw new ArgumentNullException(nameof(pipelinePersistenceService));
+ _imageProcessingService = imageProcessingService; // optional — used for pipeline step display names
_nodes = new ObservableCollection();
_treeNodes = new ObservableCollection();
@@ -661,6 +664,12 @@ namespace XplorePlane.ViewModels.Cnc
IsExpanded = expansionState.TryGetValue(node.Id, out var isExpanded) ? isExpanded : true
};
+ // 为检测模块节点填充流水线步骤(三级树节点)
+ if (node is InspectionModuleNode imNode)
+ {
+ vm.SyncPipelineSteps(imNode.Pipeline, _imageProcessingService);
+ }
+
flatNodes.Add(vm);
if (vm.IsSavePosition)
diff --git a/XplorePlane/ViewModels/Cnc/CncNodeViewModel.cs b/XplorePlane/ViewModels/Cnc/CncNodeViewModel.cs
index 7ee7d2d..f3d49cb 100644
--- a/XplorePlane/ViewModels/Cnc/CncNodeViewModel.cs
+++ b/XplorePlane/ViewModels/Cnc/CncNodeViewModel.cs
@@ -3,6 +3,7 @@ using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.ObjectModel;
+using System.Linq;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using XplorePlane.Models;
@@ -30,10 +31,18 @@ namespace XplorePlane.ViewModels.Cnc
_modelChangedCallback = modelChangedCallback ?? throw new ArgumentNullException(nameof(modelChangedCallback));
_icon = GetIconForNodeType(model.NodeType);
Children = new ObservableCollection();
+ PipelineSteps = new ObservableCollection();
}
public ObservableCollection Children { get; }
+ ///
+ /// 检测模块的流水线步骤列表(三级节点)。
+ /// 仅 IsInspectionModule == true 时有内容。
+ /// Pipeline steps for InspectionModule nodes (3rd-level tree nodes).
+ ///
+ public ObservableCollection PipelineSteps { get; }
+
public CncNode Model => _model;
public Guid Id => _model.Id;
@@ -415,6 +424,7 @@ namespace XplorePlane.ViewModels.Cnc
if (_model is InspectionModuleNode im)
{
UpdateModel(im with { Pipeline = value ?? new PipelineModel() });
+ SyncPipelineSteps(value);
}
}
}
@@ -684,6 +694,7 @@ namespace XplorePlane.ViewModels.Cnc
RaisePropertyChanged(nameof(ManualImagePath));
RaisePropertyChanged(nameof(Pipeline));
RaisePropertyChanged(nameof(PipelineName));
+ RaisePropertyChanged(nameof(PipelineSteps));
RaisePropertyChanged(nameof(MarkerType));
RaisePropertyChanged(nameof(MarkerX));
RaisePropertyChanged(nameof(MarkerY));
@@ -699,6 +710,38 @@ namespace XplorePlane.ViewModels.Cnc
RaisePropertyChanged(nameof(ExecutionProgressText));
}
+ ///
+ /// 树形子节点集合:
+ /// - 对于检测模块节点,返回 PipelineSteps(流水线步骤作为三级节点)
+ /// - 对于其他节点,返回 Children(CNC 子节点)
+ /// Tree children: PipelineSteps for InspectionModule nodes, Children otherwise.
+ ///
+ public System.Collections.IEnumerable TreeChildren =>
+ IsInspectionModule ? (System.Collections.IEnumerable)PipelineSteps : Children;
+
+ ///
+ /// 将 PipelineModel 的节点同步到 PipelineSteps 集合(三级树节点)。
+ /// Syncs PipelineModel nodes into the PipelineSteps collection (3rd-level tree nodes).
+ ///
+ public void SyncPipelineSteps(PipelineModel pipeline, Services.IImageProcessingService imageProcessingService = null)
+ {
+ PipelineSteps.Clear();
+ if (pipeline?.Nodes == null)
+ return;
+
+ foreach (var node in pipeline.Nodes.OrderBy(n => n.Order))
+ {
+ // 优先使用服务获取中文显示名,回退到 OperatorKey
+ var displayName = imageProcessingService?.GetProcessorDisplayName(node.OperatorKey)
+ ?? node.OperatorKey;
+ var icon = Services.ProcessorUiMetadata.GetOperatorIcon(node.OperatorKey);
+ PipelineSteps.Add(new CncPipelineStepViewModel(
+ displayName: displayName,
+ isEnabled: node.IsEnabled,
+ iconPath: icon));
+ }
+ }
+
private enum MotionAxis
{
StageX,
diff --git a/XplorePlane/ViewModels/Cnc/CncPipelineStepViewModel.cs b/XplorePlane/ViewModels/Cnc/CncPipelineStepViewModel.cs
new file mode 100644
index 0000000..3537862
--- /dev/null
+++ b/XplorePlane/ViewModels/Cnc/CncPipelineStepViewModel.cs
@@ -0,0 +1,51 @@
+using Prism.Mvvm;
+
+namespace XplorePlane.ViewModels.Cnc
+{
+ ///
+ /// 检测模块流水线步骤的轻量 ViewModel,用于在 CNC 树形结构中作为三级节点显示。
+ /// Lightweight ViewModel for a pipeline step inside an InspectionModule node,
+ /// displayed as a 3rd-level node in the CNC tree.
+ ///
+ public class CncPipelineStepViewModel : BindableBase
+ {
+ private string _displayName;
+ private bool _isEnabled;
+ private string _iconPath;
+
+ public CncPipelineStepViewModel(string displayName, bool isEnabled, string iconPath = null)
+ {
+ _displayName = displayName;
+ _isEnabled = isEnabled;
+ _iconPath = iconPath ?? string.Empty;
+ }
+
+ /// 算子显示名称(中文)| Operator display name
+ public string DisplayName
+ {
+ get => _displayName;
+ set => SetProperty(ref _displayName, value);
+ }
+
+ /// 是否启用 | Whether the step is enabled
+ public bool IsEnabled
+ {
+ get => _isEnabled;
+ set
+ {
+ if (SetProperty(ref _isEnabled, value))
+ RaisePropertyChanged(nameof(StateText));
+ }
+ }
+
+ /// 图标路径 | Icon path
+ public string IconPath
+ {
+ get => _iconPath;
+ set => SetProperty(ref _iconPath, value);
+ }
+
+ /// 状态文字(已启用 / 已停用)| State text
+ public string StateText => _isEnabled ? "已启用" : "已停用";
+ }
+}
diff --git a/XplorePlane/ViewModels/Main/MainViewModel.cs b/XplorePlane/ViewModels/Main/MainViewModel.cs
index b2648b8..7cab4ef 100644
--- a/XplorePlane/ViewModels/Main/MainViewModel.cs
+++ b/XplorePlane/ViewModels/Main/MainViewModel.cs
@@ -48,7 +48,7 @@ namespace XplorePlane.ViewModels
{
public class MainViewModel : BindableBase
{
- private const double CncEditorHostWidth = 452d;
+ private const double CncEditorHostWidth = 560d;
private readonly ILoggerService _logger;
private readonly IContainerProvider _containerProvider;
private readonly IEventAggregator _eventAggregator;
diff --git a/XplorePlane/Views/Cnc/CncPageView.xaml b/XplorePlane/Views/Cnc/CncPageView.xaml
index 09cf789..91487ce 100644
--- a/XplorePlane/Views/Cnc/CncPageView.xaml
+++ b/XplorePlane/Views/Cnc/CncPageView.xaml
@@ -114,8 +114,8 @@
-
+
@@ -141,6 +141,16 @@
BorderBrush="{StaticResource SeparatorBrush}"
BorderThickness="0,0,0,1">
+
+