diff --git a/.gitignore b/.gitignore index cdfade7..2265021 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,4 @@ ExternalLibraries/Models/ XplorePlane/Tests/ ExternalLibraries/Telerik/ build_out.txt +XplorePlane/data/ diff --git a/XP.Hardware.Detector/bin/Debug/net8.0-windows7.0/XP.Hardware.Detector.deps.json b/XP.Hardware.Detector/bin/Debug/net8.0-windows7.0/XP.Hardware.Detector.deps.json index 6861e9d..a3e12f2 100644 --- a/XP.Hardware.Detector/bin/Debug/net8.0-windows7.0/XP.Hardware.Detector.deps.json +++ b/XP.Hardware.Detector/bin/Debug/net8.0-windows7.0/XP.Hardware.Detector.deps.json @@ -1705,10 +1705,7 @@ "Telerik.UI.for.Wpf.NetCore.Xaml": "2024.1.408" }, "runtime": { - "XP.Common.dll": { - "assemblyVersion": "1.4.16.1", - "fileVersion": "1.4.16.1" - } + "XP.Common.dll": {} }, "resources": { "en-US/XP.Common.resources.dll": { diff --git a/XplorePlane.Tests/Services/CncExecutionServiceTests.cs b/XplorePlane.Tests/Services/CncExecutionServiceTests.cs index 1929964..b1ccd00 100644 --- a/XplorePlane.Tests/Services/CncExecutionServiceTests.cs +++ b/XplorePlane.Tests/Services/CncExecutionServiceTests.cs @@ -10,7 +10,9 @@ using Moq; using XP.Common.Logging.Interfaces; using XplorePlane.Models; using XplorePlane.Services.Cnc; +using XplorePlane.Services; using XplorePlane.Services.InspectionResults; +using XplorePlane.Services.MainViewport; using Xunit; namespace XplorePlane.Tests.Services @@ -175,6 +177,9 @@ internal sealed class SynchronousProgress : IProgress { var mockStore = new Mock(); var mockLogger = new Mock(); + var mockMainViewportService = new Mock(); + var mockPipelineExecutionService = new Mock(); + var mockImageProcessingService = new Mock(); mockLogger.Setup(l => l.ForModule()).Returns(mockLogger.Object); mockStore.Setup(s => s.BeginRunAsync( @@ -195,7 +200,12 @@ internal sealed class SynchronousProgress : IProgress It.IsAny())) .Returns(Task.CompletedTask); - var service = new CncExecutionService(mockStore.Object, mockLogger.Object); + var service = new CncExecutionService( + mockStore.Object, + mockLogger.Object, + mockMainViewportService.Object, + mockPipelineExecutionService.Object, + mockImageProcessingService.Object); return (service, mockStore, mockLogger); } diff --git a/XplorePlane/App.xaml.cs b/XplorePlane/App.xaml.cs index 9b03f95..66d730f 100644 --- a/XplorePlane/App.xaml.cs +++ b/XplorePlane/App.xaml.cs @@ -369,7 +369,7 @@ namespace XplorePlane // 注册图像处理服务与视图 containerRegistry.RegisterSingleton(); containerRegistry.Register(); - containerRegistry.RegisterForNavigation(); + // 注册流水线服务(单例,共享 IImageProcessingService) containerRegistry.RegisterSingleton(); diff --git a/XplorePlane/Services/Cnc/CncExecutionService.cs b/XplorePlane/Services/Cnc/CncExecutionService.cs index 1284ac4..3fdae6c 100644 --- a/XplorePlane/Services/Cnc/CncExecutionService.cs +++ b/XplorePlane/Services/Cnc/CncExecutionService.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Media.Imaging; -using XP.Common.GeneralForm.Views; using XP.Common.Logging.Interfaces; using XplorePlane.Models; using XplorePlane.Services.InspectionResults; @@ -42,7 +41,7 @@ namespace XplorePlane.Services.Cnc public async Task ExecuteAsync(CncProgram program, IProgress progress, CancellationToken cancellationToken) { - // Pre-cancellation check — do NOT call BeginRunAsync if already cancelled + // Pre-cancellation check - do NOT call BeginRunAsync if already cancelled if (cancellationToken.IsCancellationRequested) return; @@ -150,7 +149,10 @@ namespace XplorePlane.Services.Cnc { _logger.ForModule().Error(ex, "Unexpected error executing node '{0}' (Id={1})", node.Name, node.Id); - nodeSucceeded = false; + if (cancellationToken.IsCancellationRequested) + cancelled = true; + else + nodeSucceeded = false; } if (cancelled) @@ -160,7 +162,7 @@ namespace XplorePlane.Services.Cnc } // InspectionModuleNode 完成时携带结果图像,供 ViewModel 缓存到节点上 - var nodeResultImage = (node is InspectionModuleNode) ? lastResultImage : null; + var nodeResultImage = node is InspectionModuleNode ? lastResultImage : null; var finalState = nodeSucceeded ? NodeExecutionState.Succeeded : NodeExecutionState.Failed; progress?.Report(new CncNodeExecutionProgress(node.Id, finalState, nodeResultImage)); @@ -168,7 +170,7 @@ namespace XplorePlane.Services.Cnc allSucceeded = false; } - endLoop: + endLoop: bool? overallPass = cancelled ? null : (bool?)allSucceeded; @@ -211,10 +213,8 @@ namespace XplorePlane.Services.Cnc }; } - // 构建资产列表 var assets = new System.Collections.Generic.List(); - // input.bmp — 当前源图像 if (sourceImage != null) { assets.Add(new InspectionAssetWriteRequest @@ -227,7 +227,6 @@ namespace XplorePlane.Services.Cnc }); } - // result_overlay.bmp — 执行流水线后的结果图像 BitmapSource resultImage = null; if (_pipelineExecutionService != null && inspectionNode.Pipeline?.Nodes?.Count > 0 && sourceImage != null) { @@ -248,8 +247,6 @@ namespace XplorePlane.Services.Cnc Height = resultImage.PixelHeight }); nodeResult.Status = InspectionNodeStatus.Succeeded; - - // 执行完立即更新主视口 _mainViewportService?.SetManualImage(resultImage, $"CNC节点:{inspectionNode.Name}"); } } @@ -265,21 +262,20 @@ namespace XplorePlane.Services.Cnc return resultImage; } - private System.Collections.Generic.IEnumerable BuildPipelineNodeViewModels(PipelineModel pipeline) + private System.Collections.Generic.IEnumerable BuildPipelineNodeViewModels(PipelineModel pipeline) { - var nodes = new System.Collections.Generic.List(); + var nodes = new System.Collections.Generic.List(); if (pipeline?.Nodes == null) return nodes; foreach (var nodeModel in pipeline.Nodes.OrderBy(n => n.Order)) { var displayName = _imageProcessingService?.GetProcessorDisplayName(nodeModel.OperatorKey) ?? nodeModel.OperatorKey; - var vm = new ViewModels.PipelineNodeViewModel(nodeModel.OperatorKey, displayName, string.Empty) + var vm = new PipelineNodeViewModel(nodeModel.OperatorKey, displayName, string.Empty) { Order = nodeModel.Order, IsEnabled = nodeModel.IsEnabled }; - // 加载参数定义并恢复保存的值 if (_imageProcessingService != null) { var paramDefs = _imageProcessingService.GetProcessorParameters(nodeModel.OperatorKey); @@ -287,7 +283,7 @@ namespace XplorePlane.Services.Cnc { foreach (var def in paramDefs) { - var paramVm = new ViewModels.ProcessorParameterVM(def); + var paramVm = new ProcessorParameterVM(def); if (nodeModel.Parameters != null && nodeModel.Parameters.TryGetValue(def.Name, out var saved)) paramVm.Value = ConvertSavedValue(saved, def.ValueType); vm.Parameters.Add(paramVm); @@ -300,19 +296,16 @@ namespace XplorePlane.Services.Cnc return nodes; } - /// - /// 将 JSON 反序列化后的 JsonElement 转换为参数所需的实际类型。 - /// private static object ConvertSavedValue(object savedValue, Type targetType) { - if (savedValue is not System.Text.Json.JsonElement jsonElement) + if (savedValue is not JsonElement jsonElement) return savedValue; try { - if (targetType == typeof(int)) return jsonElement.GetInt32(); + if (targetType == typeof(int)) return jsonElement.GetInt32(); if (targetType == typeof(double)) return jsonElement.GetDouble(); - if (targetType == typeof(bool)) return jsonElement.GetBoolean(); + if (targetType == typeof(bool)) return jsonElement.GetBoolean(); if (targetType == typeof(string)) return jsonElement.GetString() ?? string.Empty; return jsonElement.ToString(); } @@ -338,40 +331,16 @@ namespace XplorePlane.Services.Cnc return; const int tickMs = 50; - ProgressWindow progressWindow = null; - await Application.Current.Dispatcher.InvokeAsync(() => + int elapsed = 0; + while (elapsed < totalMs) { - progressWindow = new ProgressWindow( - title: "延时等待", - message: $"节点:{waitNode.Name} 等待 {totalMs / 1000.0:F1} 秒", - isCancelable: false); - progressWindow.Owner = Application.Current.MainWindow; - progressWindow.Show(); - }); + cancellationToken.ThrowIfCancellationRequested(); - try - { - int elapsed = 0; - while (elapsed < totalMs) - { - cancellationToken.ThrowIfCancellationRequested(); - - int remaining = totalMs - elapsed; - int delay = Math.Min(tickMs, remaining); - await Task.Delay(delay, cancellationToken); - elapsed += delay; - - double pct = Math.Min(100.0 * elapsed / totalMs, 100.0); - double remainSec = Math.Max(0, (totalMs - elapsed) / 1000.0); - string msg = $"节点:{waitNode.Name} 剩余 {remainSec:F1} 秒"; - - progressWindow?.UpdateProgress(msg, pct); - } - } - finally - { - progressWindow?.Close(); + int remaining = totalMs - elapsed; + int delay = Math.Min(tickMs, remaining); + await Task.Delay(delay, cancellationToken); + elapsed += delay; } } } diff --git a/XplorePlane/ViewModels/Main/MainViewModel.cs b/XplorePlane/ViewModels/Main/MainViewModel.cs index 22d3cae..f4770ae 100644 --- a/XplorePlane/ViewModels/Main/MainViewModel.cs +++ b/XplorePlane/ViewModels/Main/MainViewModel.cs @@ -23,7 +23,7 @@ namespace XplorePlane.ViewModels { public class MainViewModel : BindableBase { - private const double CncEditorHostWidth = 502d; + private const double CncEditorHostWidth = 402d; private readonly ILoggerService _logger; private readonly IContainerProvider _containerProvider; @@ -203,7 +203,7 @@ namespace XplorePlane.ViewModels ClearCommand = new DelegateCommand(OnClear); EditPropertiesCommand = new DelegateCommand(OnEditProperties); - OpenImageProcessingCommand = new DelegateCommand(() => ShowWindow(new Views.ImageProcessingWindow(), "图像处理")); + LoadImageCommand = new DelegateCommand(ExecuteLoadImage); OpenPipelineEditorCommand = new DelegateCommand(() => ShowWindow(new Views.PipelineEditorWindow(), "流水线编辑器")); OpenCncEditorCommand = new DelegateCommand(ExecuteOpenCncEditor); diff --git a/XplorePlane/Views/Cnc/CncPageView.xaml b/XplorePlane/Views/Cnc/CncPageView.xaml index 6755537..f25eda1 100644 --- a/XplorePlane/Views/Cnc/CncPageView.xaml +++ b/XplorePlane/Views/Cnc/CncPageView.xaml @@ -10,7 +10,7 @@ xmlns:views="clr-namespace:XplorePlane.Views" xmlns:vm="clr-namespace:XplorePlane.ViewModels.Cnc" d:DesignHeight="760" - d:DesignWidth="502" + d:DesignWidth="402" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d"> @@ -99,8 +99,8 @@ + d:DesignHeight="600" + d:DesignWidth="500"> - Microsoft YaHei UI + + + BorderThickness="1" + CornerRadius="4"> @@ -27,14 +34,24 @@ - - + - - + + + + + + + + + + + + + + + + + + diff --git a/XplorePlane/Views/ImageProcessing/PipelineEditorWindow.xaml b/XplorePlane/Views/ImageProcessing/PipelineEditorWindow.xaml index 31155bb..2358f3c 100644 --- a/XplorePlane/Views/ImageProcessing/PipelineEditorWindow.xaml +++ b/XplorePlane/Views/ImageProcessing/PipelineEditorWindow.xaml @@ -10,21 +10,11 @@ ShowInTaskbar="False"> - - + - - - - - + + + + - - - - + + telerik:ScreenTip.Description="探测器控制" + telerik:ScreenTip.Title="探测器" + Command="{Binding OpenDetectorConfigCommand}" + Size="Medium" + SmallImage="/Assets/Icons/detector2.png" + Text="探测器" /> + telerik:ScreenTip.Description="运动控制" + telerik:ScreenTip.Title="运动控制" + Command="{Binding OpenMotionDebugCommand}" + Size="Medium" + SmallImage="/Assets/Icons/xyz.png" + Text="运动控制" /> + telerik:ScreenTip.Description="打开相机参数设置窗口" + telerik:ScreenTip.Title="相机设置" + Command="{Binding OpenCameraSettingsCommand}" + Size="Medium" + SmallImage="/Assets/Icons/detector2.png" + Text="相机设置" /> + telerik:ScreenTip.Description="打开 PLC 地址配置窗口" + telerik:ScreenTip.Title="PLC 地址配置" + Command="{Binding OpenPlcAddrConfigCommand}" + Size="Medium" + SmallImage="/Assets/Icons/tools.png" + Text="PLC 地址" /> + + + + telerik:ScreenTip.Description="切换应用程序显示语言" + telerik:ScreenTip.Title="多语言设置" + Size="Large" + SmallImage="/Assets/Icons/tools.png" + Command="{Binding OpenLanguageSwitcherCommand}" + Text="语言设置" /> + + + + + + + + + telerik:ScreenTip.Description="打开实时日志查看器" + telerik:ScreenTip.Title="查看日志" + Size="Large" + SmallImage="/Assets/Icons/message.png" + Command="{Binding OpenRealTimeLogViewerCommand}" + Text="查看日志" /> @@ -493,26 +493,18 @@ + Size="Large" + SmallImage="/Assets/Icons/message.png" + Command="{Binding OpenUserManualCommand}" + Text="帮助文档" /> + Size="Large" + SmallImage="/Assets/Icons/tools.png" + Command="{Binding OpenLibraryVersionsCommand}" + Text="关于" /> - - - - - + Text="导航" /> diff --git a/XplorePlane/XplorePlane.csproj b/XplorePlane/XplorePlane.csproj index df3b7ca..e1a2d7c 100644 --- a/XplorePlane/XplorePlane.csproj +++ b/XplorePlane/XplorePlane.csproj @@ -15,7 +15,10 @@ + + + @@ -146,7 +149,10 @@ PreserveNewest Libs\Hardware\zh-TW\%(Filename)%(Extension) + + + PreserveNewest