Plan 用于 CNC 默认保存和加载,Tools 用于流程图配方 xpm,Data 用于执行结果和中间图像,Report 为报告预留目录

This commit is contained in:
zhengxuan.zhang
2026-05-06 14:56:07 +08:00
parent 9a8831c945
commit 3bee2898c5
15 changed files with 406 additions and 18 deletions
@@ -6,6 +6,7 @@ using System.Windows.Media.Imaging;
using XP.Common.Logging.Interfaces;
using XplorePlane.Models;
using XplorePlane.Services;
using XplorePlane.Services.Storage;
using XplorePlane.Tests.Helpers;
using XplorePlane.ViewModels;
using Xunit;
@@ -22,6 +23,7 @@ namespace XplorePlane.Tests.Pipeline
private readonly Mock<IPipelineExecutionService> _mockExecSvc;
private readonly Mock<IPipelinePersistenceService> _mockPersistSvc;
private readonly Mock<ILoggerService> _mockLogger;
private readonly Mock<IXpDataPathService> _mockDataPathService;
public PipelineEditorViewModelTests()
{
@@ -29,11 +31,19 @@ namespace XplorePlane.Tests.Pipeline
_mockExecSvc = new Mock<IPipelineExecutionService>();
_mockPersistSvc = new Mock<IPipelinePersistenceService>();
_mockLogger = new Mock<ILoggerService>();
_mockDataPathService = new Mock<IXpDataPathService>();
_mockLogger.Setup(l => l.ForModule<PipelineEditorViewModel>()).Returns(_mockLogger.Object);
_mockDataPathService.SetupGet(s => s.ToolsPath).Returns(Path.GetTempPath());
}
private PipelineEditorViewModel CreateVm() =>
new PipelineEditorViewModel(_mockImageSvc.Object, _mockExecSvc.Object, _mockPersistSvc.Object, new EventAggregator(), _mockLogger.Object);
new PipelineEditorViewModel(
_mockImageSvc.Object,
_mockExecSvc.Object,
_mockPersistSvc.Object,
new EventAggregator(),
_mockLogger.Object,
_mockDataPathService.Object);
// ── 6.1 AddOperatorCommand ────────────────────────────────────
@@ -1,8 +1,10 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Moq;
using XplorePlane.Models;
using XplorePlane.Services;
using XplorePlane.Services.Storage;
using XplorePlane.Tests.Helpers;
using Xunit;
@@ -199,5 +201,21 @@ namespace XplorePlane.Tests.Pipeline
Assert.Equal(2, result.Count);
}
[Fact]
public async Task LoadAllAsync_UsesToolsPath_WhenConstructedWithDataPathService()
{
var mockImageSvc = TestHelpers.CreateMockImageService(new[] { "Blur" });
var mockDataPathSvc = new Mock<IXpDataPathService>();
mockDataPathSvc.SetupGet(s => s.ToolsPath).Returns(_tempDir);
var service = new PipelinePersistenceService(mockImageSvc.Object, mockDataPathSvc.Object);
await service.SaveAsync(BuildModel("P3", "Blur"), Path.Combine(_tempDir, "p3.xpm"));
var result = await service.LoadAllAsync(null);
Assert.Single(result);
Assert.Equal("P3", result[0].Name);
}
}
}
}
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
using XP.Common.Logging.Interfaces;
using XplorePlane.Models;
using XplorePlane.Services;
using XplorePlane.Services.Storage;
using XplorePlane.Tests.Helpers;
using XplorePlane.ViewModels;
@@ -31,8 +32,16 @@ namespace XplorePlane.Tests.Pipeline
var mockExecSvc = new Mock<IPipelineExecutionService>();
var mockPersistSvc = new Mock<IPipelinePersistenceService>();
var mockLogger = new Mock<ILoggerService>();
var mockDataPathService = new Mock<IXpDataPathService>();
mockLogger.Setup(l => l.ForModule<PipelineEditorViewModel>()).Returns(mockLogger.Object);
return new PipelineEditorViewModel(mockImageSvc.Object, mockExecSvc.Object, mockPersistSvc.Object, new EventAggregator(), mockLogger.Object);
mockDataPathService.SetupGet(s => s.ToolsPath).Returns(Path.GetTempPath());
return new PipelineEditorViewModel(
mockImageSvc.Object,
mockExecSvc.Object,
mockPersistSvc.Object,
new EventAggregator(),
mockLogger.Object,
mockDataPathService.Object);
}
/// <summary>
@@ -11,6 +11,7 @@ using XP.Common.Database.Interfaces;
using XP.Common.Logging.Interfaces;
using XplorePlane.Models;
using XplorePlane.Services.InspectionResults;
using XplorePlane.Services.Storage;
using Xunit;
namespace XplorePlane.Tests.Services
@@ -294,6 +295,33 @@ namespace XplorePlane.Tests.Services
Assert.Equal(originalHash, snapshot.PipelineHash);
}
[Fact]
public async Task Constructor_WithDataPathService_WritesIntoDataInspectionResultsDirectory()
{
var mockDataPathService = new Mock<IXpDataPathService>();
var dataRoot = Path.Combine(_tempRoot, "xpdata", "Data");
mockDataPathService.SetupGet(s => s.DataPath).Returns(dataRoot);
var store = new InspectionResultStore(_dbContext, _mockLogger.Object, mockDataPathService.Object);
var run = new InspectionRunRecord
{
ProgramName = "Program-By-Service",
WorkpieceId = "Part-03",
SerialNumber = "SN-DATA"
};
await store.BeginRunAsync(run);
await store.CompleteRunAsync(run.RunId);
var manifestPath = Path.Combine(
dataRoot,
"InspectionResults",
run.ResultRootPath.Replace('/', Path.DirectorySeparatorChar),
"manifest.json");
Assert.True(File.Exists(manifestPath));
}
public void Dispose()
{
_dbContext.Dispose();
@@ -16,6 +16,7 @@ using XP.Common.Logging.Interfaces;
using XplorePlane.Models;
using XplorePlane.Services.AppState;
using XplorePlane.Services.Cnc;
using XplorePlane.Services.Storage;
using XplorePlane.ViewModels.Cnc;
using Xunit;
@@ -32,7 +33,9 @@ namespace XplorePlane.Tests.ViewModels
var mockCncProgramSvc = new Mock<ICncProgramService>();
var mockAppState = new Mock<IAppStateService>();
var mockLogger = new Mock<ILoggerService>();
var mockDataPathService = new Mock<IXpDataPathService>();
mockLogger.Setup(l => l.ForModule<CncEditorViewModel>()).Returns(mockLogger.Object);
mockDataPathService.SetupGet(s => s.PlanPath).Returns(System.IO.Path.GetTempPath());
mockExecSvc ??= new Mock<ICncExecutionService>();
@@ -52,7 +55,8 @@ namespace XplorePlane.Tests.ViewModels
mockAppState.Object,
new EventAggregator(),
mockLogger.Object,
mockExecSvc.Object);
mockExecSvc.Object,
mockDataPathService.Object);
if (initialProgram != null)
{
+1
View File
@@ -7,6 +7,7 @@
<appSettings>
<!-- 语言配置 可选值: ZhCN, ZhTW, EnUS| Language Configuration -->
<add key="Language" value="ZhCN" />
<add key="XpData:RootPath" value="D:\XPData" />
<add key="UserManual" value="D:\HMQProject\XplorePlane_CT\Code\XplorePlane\XP.App\bin\Debug\net8.0-windows7.0\UserManual.pdf" />
<!-- Serilog日志配置 -->
+2
View File
@@ -40,6 +40,7 @@ using XplorePlane.Services.MainViewport;
using XplorePlane.Services.Matrix;
using XplorePlane.Services.Measurement;
using XplorePlane.Services.Recipe;
using XplorePlane.Services.Storage;
using XplorePlane.ViewModels;
using XplorePlane.ViewModels.Cnc;
using XplorePlane.Views;
@@ -377,6 +378,7 @@ namespace XplorePlane
// 注册全局状态服务(单例)
containerRegistry.RegisterSingleton<IAppStateService, AppStateService>();
containerRegistry.RegisterSingleton<IXpDataPathService, XpDataPathService>();
// 注册检测配方服务(单例)
containerRegistry.RegisterSingleton<IRecipeService, RecipeService>();
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
using XP.Common.Database.Interfaces;
using XP.Common.Logging.Interfaces;
using XplorePlane.Models;
using XplorePlane.Services.Storage;
namespace XplorePlane.Services.InspectionResults
{
@@ -160,6 +161,14 @@ WHERE run_id = @run_id";
"InspectionResults");
}
public InspectionResultStore(IDbContext db, ILoggerService logger, IXpDataPathService dataPathService)
: this(
db,
logger,
Path.Combine(dataPathService?.DataPath ?? throw new ArgumentNullException(nameof(dataPathService)), "InspectionResults"))
{
}
public async Task BeginRunAsync(InspectionRunRecord runRecord, InspectionAssetWriteRequest runSourceAsset = null)
{
ArgumentNullException.ThrowIfNull(runRecord);
@@ -6,6 +6,7 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using XplorePlane.Models;
using XplorePlane.Services.Storage;
namespace XplorePlane.Services
{
@@ -29,6 +30,11 @@ namespace XplorePlane.Services
"XplorePlane", "Pipelines");
}
public PipelinePersistenceService(IImageProcessingService imageProcessingService, IXpDataPathService dataPathService)
: this(imageProcessingService, dataPathService?.ToolsPath)
{
}
public async Task SaveAsync(PipelineModel pipeline, string filePath)
{
if (pipeline == null) throw new ArgumentNullException(nameof(pipeline));
@@ -0,0 +1,23 @@
using System;
namespace XplorePlane.Services.Storage
{
public interface IXpDataPathService
{
string DefaultRootPath { get; }
string RootPath { get; }
string PlanPath { get; }
string ToolsPath { get; }
string DataPath { get; }
string ReportPath { get; }
string LegacyInspectionDataPath { get; }
void SaveRootPath(string rootPath);
}
}
@@ -0,0 +1,145 @@
using System;
using System.Configuration;
using System.IO;
using XP.Common.Logging.Interfaces;
namespace XplorePlane.Services.Storage
{
public class XpDataPathService : IXpDataPathService
{
internal const string RootPathSettingKey = "XpData:RootPath";
private const string DefaultRoot = @"D:\XPData";
private readonly ILoggerService _logger;
private readonly object _syncRoot = new();
private string _rootPath;
public XpDataPathService(ILoggerService logger)
{
_logger = (logger ?? throw new ArgumentNullException(nameof(logger))).ForModule<XpDataPathService>();
_rootPath = LoadConfiguredRootPath();
EnsureManagedDirectories(_rootPath);
}
public string DefaultRootPath => DefaultRoot;
public string RootPath
{
get
{
lock (_syncRoot)
{
return _rootPath;
}
}
}
public string PlanPath => EnsureSubdirectory("Plan");
public string ToolsPath => EnsureSubdirectory("Tools");
public string DataPath => EnsureSubdirectory("Data");
public string ReportPath => EnsureSubdirectory("Report");
public string LegacyInspectionDataPath => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"XplorePlane",
"InspectionResults");
public void SaveRootPath(string rootPath)
{
var normalizedRootPath = NormalizeRootPath(rootPath);
EnsureManagedDirectories(normalizedRootPath);
try
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var appSettings = config.AppSettings.Settings;
if (appSettings[RootPathSettingKey] == null)
{
appSettings.Add(RootPathSettingKey, normalizedRootPath);
}
else
{
appSettings[RootPathSettingKey].Value = normalizedRootPath;
}
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
}
catch (ConfigurationErrorsException ex)
{
_logger.Error(ex, "保存 XP 数据根目录失败:配置文件写入异常");
throw new InvalidOperationException($"保存数据根目录失败:{ex.Message}", ex);
}
catch (UnauthorizedAccessException ex)
{
_logger.Error(ex, "保存 XP 数据根目录失败:无权写入配置文件");
throw new InvalidOperationException("保存数据根目录失败:没有配置文件写入权限。", ex);
}
lock (_syncRoot)
{
_rootPath = normalizedRootPath;
}
}
private string EnsureSubdirectory(string directoryName)
{
string rootPath;
lock (_syncRoot)
{
rootPath = _rootPath;
}
EnsureManagedDirectories(rootPath);
return Path.Combine(rootPath, directoryName);
}
private string LoadConfiguredRootPath()
{
try
{
return NormalizeRootPath(ConfigurationManager.AppSettings[RootPathSettingKey]);
}
catch (Exception ex)
{
_logger.Warn("读取 XP 数据根目录失败,回退默认目录:{Message}", ex.Message);
return NormalizeRootPath(null);
}
}
private static string NormalizeRootPath(string rootPath)
{
var candidate = string.IsNullOrWhiteSpace(rootPath) ? DefaultRoot : rootPath.Trim();
try
{
var normalized = Path.GetFullPath(candidate)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
if (!Path.IsPathRooted(normalized))
{
return DefaultRoot;
}
return normalized;
}
catch
{
return DefaultRoot;
}
}
private static void EnsureManagedDirectories(string rootPath)
{
Directory.CreateDirectory(rootPath);
Directory.CreateDirectory(Path.Combine(rootPath, "Plan"));
Directory.CreateDirectory(Path.Combine(rootPath, "Tools"));
Directory.CreateDirectory(Path.Combine(rootPath, "Data"));
Directory.CreateDirectory(Path.Combine(rootPath, "Report"));
}
}
}
@@ -16,6 +16,7 @@ using XplorePlane.Events;
using XplorePlane.Models;
using XplorePlane.Services.AppState;
using XplorePlane.Services.Cnc;
using XplorePlane.Services.Storage;
namespace XplorePlane.ViewModels.Cnc
{
@@ -28,6 +29,7 @@ namespace XplorePlane.ViewModels.Cnc
private readonly IEventAggregator _eventAggregator;
private readonly ILoggerService _logger;
private readonly ICncExecutionService _cncExecutionService;
private readonly IXpDataPathService _dataPathService;
private CncProgram _currentProgram;
private ObservableCollection<CncNodeViewModel> _nodes;
@@ -51,13 +53,15 @@ namespace XplorePlane.ViewModels.Cnc
IAppStateService appStateService,
IEventAggregator eventAggregator,
ILoggerService logger,
ICncExecutionService cncExecutionService)
ICncExecutionService cncExecutionService,
IXpDataPathService dataPathService)
{
_cncProgramService = cncProgramService ?? throw new ArgumentNullException(nameof(cncProgramService));
ArgumentNullException.ThrowIfNull(appStateService);
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_logger = (logger ?? throw new ArgumentNullException(nameof(logger))).ForModule<CncEditorViewModel>();
_cncExecutionService = cncExecutionService ?? throw new ArgumentNullException(nameof(cncExecutionService));
_dataPathService = dataPathService ?? throw new ArgumentNullException(nameof(dataPathService));
_nodes = new ObservableCollection<CncNodeViewModel>();
_treeNodes = new ObservableCollection<CncNodeViewModel>();
@@ -331,7 +335,8 @@ namespace XplorePlane.ViewModels.Cnc
Title = "保存 CNC 程序",
Filter = "CNC 程序文件 (*.xp)|*.xp|所有文件 (*.*)|*.*",
DefaultExt = ".xp",
FileName = _currentProgram.Name
FileName = _currentProgram.Name,
InitialDirectory = GetPlanDirectory()
};
if (dlg.ShowDialog() != true)
@@ -355,7 +360,8 @@ namespace XplorePlane.ViewModels.Cnc
{
Title = "加载 CNC 程序",
Filter = "CNC 程序文件 (*.xp)|*.xp|所有文件 (*.*)|*.*",
DefaultExt = ".xp"
DefaultExt = ".xp",
InitialDirectory = GetPlanDirectory()
};
if (dlg.ShowDialog() != true)
@@ -438,6 +444,13 @@ namespace XplorePlane.ViewModels.Cnc
}
}
private string GetPlanDirectory()
{
var directory = _dataPathService.PlanPath;
Directory.CreateDirectory(directory);
return directory;
}
private static string Esc(string value)
{
if (string.IsNullOrEmpty(value)) return string.Empty;
@@ -14,6 +14,7 @@ using XP.Common.Logging.Interfaces;
using XplorePlane.Events;
using XplorePlane.Models;
using XplorePlane.Services;
using XplorePlane.Services.Storage;
namespace XplorePlane.ViewModels
{
@@ -28,6 +29,7 @@ namespace XplorePlane.ViewModels
private readonly IPipelinePersistenceService _persistenceService;
private readonly IEventAggregator _eventAggregator;
private readonly ILoggerService _logger;
private readonly IXpDataPathService _dataPathService;
private PipelineNodeViewModel _selectedNode;
private BitmapSource _sourceImage;
@@ -49,13 +51,15 @@ namespace XplorePlane.ViewModels
IPipelineExecutionService executionService,
IPipelinePersistenceService persistenceService,
IEventAggregator eventAggregator,
ILoggerService logger)
ILoggerService logger,
IXpDataPathService dataPathService)
{
_imageProcessingService = imageProcessingService ?? throw new ArgumentNullException(nameof(imageProcessingService));
_executionService = executionService ?? throw new ArgumentNullException(nameof(executionService));
_persistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_logger = logger?.ForModule<PipelineEditorViewModel>() ?? throw new ArgumentNullException(nameof(logger));
_dataPathService = dataPathService ?? throw new ArgumentNullException(nameof(dataPathService));
PipelineNodes = new ObservableCollection<PipelineNodeViewModel>();
AvailableDevices = new ObservableCollection<string>();
@@ -808,11 +812,9 @@ namespace XplorePlane.ViewModels
return $"已执行到“{ExecutionEndNode.DisplayName}” ({executionCount} 个有效节点)";
}
private static string GetPipelineDirectory()
private string GetPipelineDirectory()
{
var dir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"XplorePlane", "Pipelines");
var dir = _dataPathService.ToolsPath;
Directory.CreateDirectory(dir);
return dir;
}
+78 -1
View File
@@ -11,6 +11,7 @@ using System.Windows;
using System.Windows.Media.Imaging;
using XplorePlane.Events;
using XplorePlane.Services.MainViewport;
using XplorePlane.Services.Storage;
using XplorePlane.ViewModels.Cnc;
using XplorePlane.Views;
using XplorePlane.Views.Cnc;
@@ -29,6 +30,7 @@ namespace XplorePlane.ViewModels
private readonly IContainerProvider _containerProvider;
private readonly IEventAggregator _eventAggregator;
private readonly IMainViewportService _mainViewportService;
private readonly IXpDataPathService _xpDataPathService;
private readonly CncEditorViewModel _cncEditorViewModel;
private readonly CncPageView _cncPageView;
@@ -66,6 +68,9 @@ namespace XplorePlane.ViewModels
public DelegateCommand OpenUserManualCommand { get; }
public DelegateCommand OpenCameraSettingsCommand { get; }
public DelegateCommand OpenSettingsCommand { get; }
public DelegateCommand BrowseDataRootPathCommand { get; }
public DelegateCommand ResetDataRootPathCommand { get; }
public DelegateCommand SaveDataRootPathCommand { get; }
public DelegateCommand NewCncProgramCommand { get; }
public DelegateCommand SaveCncProgramCommand { get; }
public DelegateCommand LoadCncProgramCommand { get; }
@@ -119,6 +124,20 @@ namespace XplorePlane.ViewModels
public bool IsUsingLiveDetectorSource => _mainViewportService.CurrentSourceMode == MainViewportSourceMode.LiveDetector;
public string DataRootPath
{
get => _dataRootPath;
set => SetProperty(ref _dataRootPath, value);
}
public string PlanRootPath => _xpDataPathService.PlanPath;
public string ToolsRootPath => _xpDataPathService.ToolsPath;
public string ResultsRootPath => _xpDataPathService.DataPath;
public string ReportRootPath => _xpDataPathService.ReportPath;
/// <summary>右侧图像区域内容 | Right-side image panel content</summary>
public object ImagePanelContent
{
@@ -155,16 +174,20 @@ namespace XplorePlane.ViewModels
private bool _isCncEditorMode;
private string _licenseInfo = "当前时间";
private string _dataRootPath = string.Empty;
public MainViewModel(
ILoggerService logger,
IContainerProvider containerProvider,
IEventAggregator eventAggregator,
IMainViewportService mainViewportService)
IMainViewportService mainViewportService,
IXpDataPathService xpDataPathService)
{
_logger = logger?.ForModule<MainViewModel>() ?? throw new ArgumentNullException(nameof(logger));
_containerProvider = containerProvider ?? throw new ArgumentNullException(nameof(containerProvider));
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_mainViewportService = mainViewportService ?? throw new ArgumentNullException(nameof(mainViewportService));
_xpDataPathService = xpDataPathService ?? throw new ArgumentNullException(nameof(xpDataPathService));
_cncEditorViewModel = _containerProvider.Resolve<CncEditorViewModel>();
_cncPageView = new CncPageView { DataContext = _cncEditorViewModel };
@@ -215,6 +238,9 @@ namespace XplorePlane.ViewModels
OpenUserManualCommand = new DelegateCommand(ExecuteOpenUserManual);
OpenCameraSettingsCommand = new DelegateCommand(ExecuteOpenCameraSettings);
OpenSettingsCommand = new DelegateCommand(ExecuteOpenSettings);
BrowseDataRootPathCommand = new DelegateCommand(ExecuteBrowseDataRootPath);
ResetDataRootPathCommand = new DelegateCommand(ExecuteResetDataRootPath);
SaveDataRootPathCommand = new DelegateCommand(ExecuteSaveDataRootPath);
NewCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.NewProgramCommand.Execute()));
SaveCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.SaveProgramCommand.Execute()));
LoadCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.LoadProgramCommand.Execute()));
@@ -259,6 +285,7 @@ namespace XplorePlane.ViewModels
ImagePanelContent = new PipelineEditorView();
ViewportPanelWidth = new GridLength(1, GridUnitType.Star);
ImagePanelWidth = new GridLength(320);
DataRootPath = _xpDataPathService.RootPath;
_logger.Info("MainViewModel 已初始化");
}
@@ -395,6 +422,56 @@ namespace XplorePlane.ViewModels
}
}
private void ExecuteBrowseDataRootPath()
{
try
{
var dialog = new OpenFolderDialog
{
Title = "选择 XP 数据根目录",
InitialDirectory = Directory.Exists(DataRootPath) ? DataRootPath : _xpDataPathService.RootPath
};
if (dialog.ShowDialog() == true)
{
DataRootPath = dialog.FolderName;
}
}
catch (Exception ex)
{
_logger.Error(ex, "浏览 XP 数据根目录失败");
MessageBox.Show($"浏览数据根目录失败:{ex.Message}",
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ExecuteResetDataRootPath()
{
DataRootPath = _xpDataPathService.DefaultRootPath;
}
private void ExecuteSaveDataRootPath()
{
try
{
_xpDataPathService.SaveRootPath(DataRootPath);
DataRootPath = _xpDataPathService.RootPath;
RaisePropertyChanged(nameof(PlanRootPath));
RaisePropertyChanged(nameof(ToolsRootPath));
RaisePropertyChanged(nameof(ResultsRootPath));
RaisePropertyChanged(nameof(ReportRootPath));
MessageBox.Show("XP 数据根目录已保存。新的保存/加载对话框会立即使用新路径。",
"提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
_logger.Error(ex, "保存 XP 数据根目录失败");
MessageBox.Show($"保存数据根目录失败:{ex.Message}",
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ExecuteAxisReset()
{
var result = MessageBox.Show("确认执行轴复位操作?", "轴复位",
+46 -5
View File
@@ -28,20 +28,61 @@
<Style x:Key="ActionButtonStyle" TargetType="Button">
<Setter Property="Height" Value="32" />
<Setter Property="MinWidth" Value="110" />
<Setter Property="MinWidth" Value="96" />
<Setter Property="Margin" Value="0,0,10,10" />
<Setter Property="Padding" Value="12,0" />
</Style>
</Window.Resources>
<DockPanel Margin="12">
<TabControl Background="White">
<TabItem Header="通用">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="16">
<TextBlock Style="{StaticResource SectionTitleStyle}" Text="通用设置" />
<Border Style="{StaticResource CardStyle}">
<StackPanel>
<TextBlock FontSize="13"
FontWeight="SemiBold"
Text="XP 数据目录" />
<TextBlock Margin="0,8,0,8"
Foreground="#666666"
Text="Plan 用于 CNC 默认保存和加载,Tools 用于流程图配方 xpm,Data 用于执行结果和中间图像,Report 为报告预留目录。" />
<Grid Margin="0,4,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="数据根目录" />
<TextBox Grid.Column="1"
Height="30"
Margin="0,0,10,0"
Padding="8,0"
Text="{Binding DataRootPath, UpdateSourceTrigger=PropertyChanged}" />
<Button Grid.Column="2"
Command="{Binding BrowseDataRootPathCommand}"
Content="浏览"
Style="{StaticResource ActionButtonStyle}" />
<Button Grid.Column="3"
Command="{Binding ResetDataRootPathCommand}"
Content="恢复默认"
Style="{StaticResource ActionButtonStyle}" />
<Button Grid.Column="4"
Command="{Binding SaveDataRootPathCommand}"
Content="保存"
Style="{StaticResource ActionButtonStyle}" />
</Grid>
</StackPanel>
</Border>
<Border Style="{StaticResource CardStyle}">
<StackPanel>
<TextBlock FontSize="13"
@@ -93,7 +134,7 @@
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="报告标题" />
Text="报告标题" />
<TextBox Grid.Column="1"
Height="30"
Padding="8,0"
@@ -107,11 +148,11 @@
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Text="模板路径" />
Text="模板路径" />
<TextBox Grid.Column="1"
Height="30"
Padding="8,0"
Text="C:\Reports\Templates\Default" />
Text="D:\XPData\Report\Templates\Default" />
</Grid>
<CheckBox Margin="0,12,0,0"