#初步的cnc编辑功能,实现插入参考点的流程

This commit is contained in:
zhengxuan.zhang
2026-04-21 01:49:09 +08:00
parent c91b55785e
commit 3b4b794dd0
6 changed files with 94 additions and 15 deletions
+3 -2
View File
@@ -46,7 +46,8 @@ namespace XplorePlane.Models
/// <summary>参考点节点 | Reference point node</summary>
public record ReferencePointNode(
Guid Id, int Index, string Name,
double XM, double YM, double ZT, double ZD, double TiltD, double Dist
double XM, double YM, double ZT, double ZD, double TiltD, double Dist,
bool IsRayOn, double Voltage, double Current
) : CncNode(Id, Index, CncNodeType.ReferencePoint, Name);
/// <summary>保存节点(含图像)| Save node with image</summary>
@@ -113,4 +114,4 @@ namespace XplorePlane.Models
DateTime UpdatedAt,
IReadOnlyList<CncNode> Nodes
);
}
}
+30 -2
View File
@@ -6,6 +6,7 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using XP.Common.Logging.Interfaces;
using XP.Hardware.RaySource.Services;
using XplorePlane.Models;
using XplorePlane.Services.AppState;
@@ -20,6 +21,7 @@ namespace XplorePlane.Services.Cnc
public class CncProgramService : ICncProgramService
{
private readonly IAppStateService _appStateService;
private readonly IRaySourceService _raySourceService;
private readonly ILoggerService _logger;
// ── 序列化配置 | Serialization options ──
@@ -32,12 +34,15 @@ namespace XplorePlane.Services.Cnc
public CncProgramService(
IAppStateService appStateService,
IRaySourceService raySourceService,
ILoggerService logger)
{
ArgumentNullException.ThrowIfNull(appStateService);
ArgumentNullException.ThrowIfNull(raySourceService);
ArgumentNullException.ThrowIfNull(logger);
_appStateService = appStateService;
_raySourceService = raySourceService;
_logger = logger.ForModule<CncProgramService>();
_logger.Info("CncProgramService 已初始化 | CncProgramService initialized");
@@ -344,6 +349,7 @@ namespace XplorePlane.Services.Cnc
private ReferencePointNode CreateReferencePointNode(Guid id, int index)
{
var motion = _appStateService.MotionState;
var raySource = _appStateService.RaySourceState;
return new ReferencePointNode(
id, index, $"参考点_{index}",
XM: motion.XM,
@@ -351,7 +357,10 @@ namespace XplorePlane.Services.Cnc
ZT: motion.ZT,
ZD: motion.ZD,
TiltD: motion.TiltD,
Dist: motion.Dist);
Dist: motion.Dist,
IsRayOn: raySource.IsOn,
Voltage: raySource.Voltage,
Current: TryReadCurrent());
}
/// <summary>创建保存节点(含图像)| Create save node with image</summary>
@@ -382,5 +391,24 @@ namespace XplorePlane.Services.Cnc
id, index, $"保存位置_{index}",
MotionState: _appStateService.MotionState);
}
private double TryReadCurrent()
{
try
{
var result = _raySourceService.ReadCurrent();
if (result?.Success == true)
{
return result.GetFloat();
}
_logger.Warn("Failed to read ray source current, ReferencePoint node will use 0");
}
catch (Exception ex)
{
_logger.Warn("Failed to read ray source current: {Message}", ex.Message);
}
return 0d;
}
}
}
}
@@ -173,7 +173,7 @@ namespace XplorePlane.ViewModels.Cnc
if (_currentProgram == null)
{
_logger.Warn("无法插入节点:当前无程序 | Cannot insert node: no current program");
return;
ExecuteNewProgram();
}
try
@@ -365,7 +365,7 @@ namespace XplorePlane.ViewModels.Cnc
var sb = new StringBuilder();
// CSV 表头 | CSV header
sb.AppendLine("Index,NodeType,Name,XM,YM,ZT,ZD,TiltD,Dist,Voltage_kV,Power_W,RayOn,DetectorConnected,FrameRate,Resolution,ImageFile,MarkerType,MarkerX,MarkerY,DialogTitle,DialogMessage,DelayMs,Pipeline");
sb.AppendLine("Index,NodeType,Name,XM,YM,ZT,ZD,TiltD,Dist,Voltage_kV,Current_uA,Power_W,RayOn,DetectorConnected,FrameRate,Resolution,ImageFile,MarkerType,MarkerX,MarkerY,DialogTitle,DialogMessage,DelayMs,Pipeline");
var inv = CultureInfo.InvariantCulture;
@@ -373,11 +373,11 @@ namespace XplorePlane.ViewModels.Cnc
{
var row = node switch
{
ReferencePointNode rp => $"{rp.Index},{rp.NodeType},{Esc(rp.Name)},{rp.XM.ToString(inv)},{rp.YM.ToString(inv)},{rp.ZT.ToString(inv)},{rp.ZD.ToString(inv)},{rp.TiltD.ToString(inv)},{rp.Dist.ToString(inv)},,,,,,,,,,,,,,",
ReferencePointNode rp => $"{rp.Index},{rp.NodeType},{Esc(rp.Name)},{rp.XM.ToString(inv)},{rp.YM.ToString(inv)},{rp.ZT.ToString(inv)},{rp.ZD.ToString(inv)},{rp.TiltD.ToString(inv)},{rp.Dist.ToString(inv)},{rp.Voltage.ToString(inv)},{rp.Current.ToString(inv)},,{rp.IsRayOn},,,,,,,,,,",
SaveNodeWithImageNode sni => $"{sni.Index},{sni.NodeType},{Esc(sni.Name)},{sni.MotionState.XM.ToString(inv)},{sni.MotionState.YM.ToString(inv)},{sni.MotionState.ZT.ToString(inv)},{sni.MotionState.ZD.ToString(inv)},{sni.MotionState.TiltD.ToString(inv)},{sni.MotionState.Dist.ToString(inv)},{sni.RaySourceState.Voltage.ToString(inv)},{sni.RaySourceState.Power.ToString(inv)},{sni.RaySourceState.IsOn},{sni.DetectorState.IsConnected},{sni.DetectorState.FrameRate.ToString(inv)},{Esc(sni.DetectorState.Resolution)},{Esc(sni.ImageFileName)},,,,,,",
SaveNodeWithImageNode sni => $"{sni.Index},{sni.NodeType},{Esc(sni.Name)},{sni.MotionState.XM.ToString(inv)},{sni.MotionState.YM.ToString(inv)},{sni.MotionState.ZT.ToString(inv)},{sni.MotionState.ZD.ToString(inv)},{sni.MotionState.TiltD.ToString(inv)},{sni.MotionState.Dist.ToString(inv)},{sni.RaySourceState.Voltage.ToString(inv)},,{sni.RaySourceState.Power.ToString(inv)},{sni.RaySourceState.IsOn},{sni.DetectorState.IsConnected},{sni.DetectorState.FrameRate.ToString(inv)},{Esc(sni.DetectorState.Resolution)},{Esc(sni.ImageFileName)},,,,,,",
SaveNodeNode sn => $"{sn.Index},{sn.NodeType},{Esc(sn.Name)},{sn.MotionState.XM.ToString(inv)},{sn.MotionState.YM.ToString(inv)},{sn.MotionState.ZT.ToString(inv)},{sn.MotionState.ZD.ToString(inv)},{sn.MotionState.TiltD.ToString(inv)},{sn.MotionState.Dist.ToString(inv)},{sn.RaySourceState.Voltage.ToString(inv)},{sn.RaySourceState.Power.ToString(inv)},{sn.RaySourceState.IsOn},{sn.DetectorState.IsConnected},{sn.DetectorState.FrameRate.ToString(inv)},{Esc(sn.DetectorState.Resolution)},,,,,,,",
SaveNodeNode sn => $"{sn.Index},{sn.NodeType},{Esc(sn.Name)},{sn.MotionState.XM.ToString(inv)},{sn.MotionState.YM.ToString(inv)},{sn.MotionState.ZT.ToString(inv)},{sn.MotionState.ZD.ToString(inv)},{sn.MotionState.TiltD.ToString(inv)},{sn.MotionState.Dist.ToString(inv)},{sn.RaySourceState.Voltage.ToString(inv)},,{sn.RaySourceState.Power.ToString(inv)},{sn.RaySourceState.IsOn},{sn.DetectorState.IsConnected},{sn.DetectorState.FrameRate.ToString(inv)},{Esc(sn.DetectorState.Resolution)},,,,,,,",
SavePositionNode sp => $"{sp.Index},{sp.NodeType},{Esc(sp.Name)},{sp.MotionState.XM.ToString(inv)},{sp.MotionState.YM.ToString(inv)},{sp.MotionState.ZT.ToString(inv)},{sp.MotionState.ZD.ToString(inv)},{sp.MotionState.TiltD.ToString(inv)},{sp.MotionState.Dist.ToString(inv)},,,,,,,,,,,,,,",
@@ -466,4 +466,4 @@ namespace XplorePlane.ViewModels.Cnc
.Publish(new CncProgramChangedPayload(ProgramName ?? string.Empty, IsModified));
}
}
}
}
+41 -1
View File
@@ -10,6 +10,7 @@ using System.IO;
using System.Windows;
using System.Windows.Media.Imaging;
using XplorePlane.Events;
using XplorePlane.ViewModels.Cnc;
using XplorePlane.Views;
using XplorePlane.Views.Cnc;
using XP.Common.Logging.Interfaces;
@@ -23,6 +24,8 @@ namespace XplorePlane.ViewModels
private readonly ILoggerService _logger;
private readonly IContainerProvider _containerProvider;
private readonly IEventAggregator _eventAggregator;
private readonly CncEditorViewModel _cncEditorViewModel;
private readonly CncPageView _cncPageView;
private string _licenseInfo = "当前时间";
public string LicenseInfo
@@ -51,6 +54,17 @@ namespace XplorePlane.ViewModels
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 AxisResetCommand { get; }
@@ -94,6 +108,8 @@ namespace XplorePlane.ViewModels
_logger = logger?.ForModule<MainViewModel>() ?? throw new ArgumentNullException(nameof(logger));
_containerProvider = containerProvider ?? throw new ArgumentNullException(nameof(containerProvider));
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_cncEditorViewModel = _containerProvider.Resolve<CncEditorViewModel>();
_cncPageView = new CncPageView { DataContext = _cncEditorViewModel };
NavigationTree = new ObservableCollection<object>();
@@ -115,6 +131,17 @@ namespace XplorePlane.ViewModels
OpenLibraryVersionsCommand = new DelegateCommand(() => ShowWindow(new Views.LibraryVersionsWindow(), "关于"));
OpenUserManualCommand = new DelegateCommand(ExecuteOpenUserManual);
OpenCameraSettingsCommand = new DelegateCommand(ExecuteOpenCameraSettings);
NewCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.NewProgramCommand.Execute()));
SaveCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.SaveProgramCommand.Execute()));
LoadCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.LoadProgramCommand.Execute()));
InsertReferencePointCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertReferencePointCommand.Execute()));
InsertSavePositionCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertSavePositionCommand.Execute()));
InsertCompleteProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertCompleteProgramCommand.Execute()));
InsertInspectionMarkerCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertInspectionMarkerCommand.Execute()));
InsertInspectionModuleCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertInspectionModuleCommand.Execute()));
InsertSaveNodeCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertSaveNodeCommand.Execute()));
InsertPauseDialogCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertPauseDialogCommand.Execute()));
InsertWaitDelayCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertWaitDelayCommand.Execute()));
// 硬件命令
AxisResetCommand = new DelegateCommand(ExecuteAxisReset);
@@ -186,7 +213,20 @@ namespace XplorePlane.ViewModels
return;
}
ImagePanelContent = new CncPageView();
ShowCncEditor();
}
private void ExecuteCncEditorAction(Action<CncEditorViewModel> action)
{
ArgumentNullException.ThrowIfNull(action);
ShowCncEditor();
action(_cncEditorViewModel);
}
private void ShowCncEditor()
{
ImagePanelContent = _cncPageView;
ImagePanelWidth = new GridLength(430);
_isCncEditorMode = true;
_logger.Info("CNC 编辑器已切换到主界面图像区域");
+2 -1
View File
@@ -53,7 +53,8 @@
<ColumnDefinition Width="*" MinWidth="260" />
</Grid.ColumnDefinitions>
<ListBox
<!--树行节点显示-->
<ListBox
x:Name="CncNodeListBox"
Grid.Column="0"
Background="Transparent"
+12 -3
View File
@@ -74,7 +74,7 @@
<StackPanel>
<telerik:RadRibbonButton
telerik:ScreenTip.Title="新建CNC"
Command="{Binding Path=SetStyle.Command}"
Command="{Binding NewCncProgramCommand}"
Size="Medium"
SmallImage="/Assets/Icons/new-doc.png"
Text="新建CNC" />
@@ -82,11 +82,12 @@
telerik:ScreenTip.Description="保存当前X射线实时图像"
telerik:ScreenTip.Title="保存图像"
Size="Medium"
Command="{Binding SaveCncProgramCommand}"
SmallImage="/Assets/Icons/save.png"
Text="保存" />
<telerik:RadRibbonButton
telerik:ScreenTip.Title="另存为"
Command="{Binding OpenFileCommand}"
Command="{Binding SaveCncProgramCommand}"
Size="Medium"
SmallImage="/Assets/Icons/saveas.png"
Text="另存为" />
@@ -94,7 +95,7 @@
<StackPanel>
<telerik:RadRibbonButton
telerik:ScreenTip.Title="加载CNC"
Command="{Binding OpenFileCommand}"
Command="{Binding LoadCncProgramCommand}"
Size="Large"
SmallImage="/Assets/Icons/open.png"
Text="加载CNC" />
@@ -293,16 +294,19 @@
<telerik:RadRibbonButton
telerik:ScreenTip.Title="参考点"
Size="Medium"
Command="{Binding InsertReferencePointCommand}"
SmallImage="/Assets/Icons/reference.png"
Text="参考点" />
<telerik:RadRibbonButton
telerik:ScreenTip.Title="添加位置"
Size="Medium"
Command="{Binding InsertSavePositionCommand}"
SmallImage="/Assets/Icons/add-pos.png"
Text="添加位置" />
<telerik:RadRibbonButton
telerik:ScreenTip.Title="完成"
Size="Medium"
Command="{Binding InsertCompleteProgramCommand}"
SmallImage="/Assets/Icons/finish.png"
Text="完成" />
</StackPanel>
@@ -310,16 +314,19 @@
<telerik:RadRibbonButton
telerik:ScreenTip.Title="标记"
Size="Medium"
Command="{Binding InsertInspectionMarkerCommand}"
SmallImage="/Assets/Icons/mark.png"
Text="标记" />
<telerik:RadRibbonButton
telerik:ScreenTip.Title="模块"
Size="Medium"
Command="{Binding InsertInspectionModuleCommand}"
SmallImage="/Assets/Icons/Module.png"
Text="检测模块" />
<telerik:RadRibbonButton
telerik:ScreenTip.Title="全部保存"
Size="Medium"
Command="{Binding SaveCncProgramCommand}"
SmallImage="/Assets/Icons/saveall.png"
Text="全部保存" />
</StackPanel>
@@ -327,11 +334,13 @@
<telerik:RadRibbonButton
telerik:ScreenTip.Title="消息"
Size="Medium"
Command="{Binding InsertPauseDialogCommand}"
SmallImage="/Assets/Icons/message.png"
Text="消息弹窗" />
<telerik:RadRibbonButton
telerik:ScreenTip.Title="等待"
Size="Medium"
Command="{Binding InsertWaitDelayCommand}"
SmallImage="/Assets/Icons/wait.png"
Text="插入等待" />
</StackPanel>