Compare commits
3 Commits
2969ada965
...
514eace979
| Author | SHA1 | Date | |
|---|---|---|---|
| 514eace979 | |||
| 89759cf511 | |||
| 0d6a4bd22f |
@@ -180,7 +180,7 @@ namespace XplorePlane.Services.MainViewport
|
|||||||
_currentDisplayInfo = _latestManualInfo;
|
_currentDisplayInfo = _latestManualInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Info("主界面已加载手动图像 {FileName}", fileName);
|
_logger.Info("[图像链路] MainViewportService.SetManualImage:已更新图像 {FileName},触发 StateChanged", fileName);
|
||||||
RaiseStateChanged();
|
RaiseStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,17 @@ using System.ComponentModel;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using XplorePlane.Events;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
using XplorePlane.Services;
|
using XplorePlane.Services;
|
||||||
|
using XplorePlane.Services.MainViewport;
|
||||||
using XP.Common.Logging.Interfaces;
|
using XP.Common.Logging.Interfaces;
|
||||||
|
using Prism.Events;
|
||||||
|
|
||||||
namespace XplorePlane.ViewModels.Cnc
|
namespace XplorePlane.ViewModels.Cnc
|
||||||
{
|
{
|
||||||
@@ -21,6 +27,9 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
private readonly CncEditorViewModel _editorViewModel;
|
private readonly CncEditorViewModel _editorViewModel;
|
||||||
private readonly IImageProcessingService _imageProcessingService;
|
private readonly IImageProcessingService _imageProcessingService;
|
||||||
private readonly IPipelinePersistenceService _persistenceService;
|
private readonly IPipelinePersistenceService _persistenceService;
|
||||||
|
private readonly IPipelineExecutionService _executionService;
|
||||||
|
private readonly IMainViewportService _mainViewportService;
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly ILoggerService _logger;
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
private CncNodeViewModel _activeModuleNode;
|
private CncNodeViewModel _activeModuleNode;
|
||||||
@@ -29,17 +38,26 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
private string _pipelineFileDisplayName = "未命名模块.xpm";
|
private string _pipelineFileDisplayName = "未命名模块.xpm";
|
||||||
private string _currentFilePath;
|
private string _currentFilePath;
|
||||||
private bool _isSynchronizing;
|
private bool _isSynchronizing;
|
||||||
|
private CancellationTokenSource _debounceCts;
|
||||||
|
|
||||||
|
private const int DebounceDelayMs = 300;
|
||||||
|
|
||||||
public CncInspectionModulePipelineViewModel(
|
public CncInspectionModulePipelineViewModel(
|
||||||
CncEditorViewModel editorViewModel,
|
CncEditorViewModel editorViewModel,
|
||||||
IImageProcessingService imageProcessingService,
|
IImageProcessingService imageProcessingService,
|
||||||
IPipelinePersistenceService persistenceService,
|
IPipelinePersistenceService persistenceService,
|
||||||
ILoggerService logger)
|
ILoggerService logger,
|
||||||
|
IPipelineExecutionService executionService = null,
|
||||||
|
IMainViewportService mainViewportService = null,
|
||||||
|
IEventAggregator eventAggregator = null)
|
||||||
{
|
{
|
||||||
_editorViewModel = editorViewModel ?? throw new ArgumentNullException(nameof(editorViewModel));
|
_editorViewModel = editorViewModel ?? throw new ArgumentNullException(nameof(editorViewModel));
|
||||||
_imageProcessingService = imageProcessingService ?? throw new ArgumentNullException(nameof(imageProcessingService));
|
_imageProcessingService = imageProcessingService ?? throw new ArgumentNullException(nameof(imageProcessingService));
|
||||||
_persistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
|
_persistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
|
||||||
_logger = (logger ?? throw new ArgumentNullException(nameof(logger))).ForModule<CncInspectionModulePipelineViewModel>();
|
_logger = (logger ?? throw new ArgumentNullException(nameof(logger))).ForModule<CncInspectionModulePipelineViewModel>();
|
||||||
|
_executionService = executionService;
|
||||||
|
_mainViewportService = mainViewportService;
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
|
||||||
PipelineNodes = new ObservableCollection<PipelineNodeViewModel>();
|
PipelineNodes = new ObservableCollection<PipelineNodeViewModel>();
|
||||||
|
|
||||||
@@ -373,6 +391,52 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
|
|
||||||
_activeModuleNode.Pipeline = BuildPipelineModel();
|
_activeModuleNode.Pipeline = BuildPipelineModel();
|
||||||
StatusMessage = statusMessage;
|
StatusMessage = statusMessage;
|
||||||
|
TriggerDebouncedPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerDebouncedPreview()
|
||||||
|
{
|
||||||
|
if (_executionService == null || _mainViewportService == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sourceImage = _mainViewportService.CurrentDisplayImage as BitmapSource
|
||||||
|
?? _mainViewportService.LatestManualImage as BitmapSource;
|
||||||
|
if (sourceImage == null)
|
||||||
|
{
|
||||||
|
_logger.Debug("[图像链路][CNC] TriggerDebouncedPreview:无可用源图像,跳过");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_debounceCts?.Cancel();
|
||||||
|
_debounceCts = new CancellationTokenSource();
|
||||||
|
var token = _debounceCts.Token;
|
||||||
|
|
||||||
|
Task.Delay(DebounceDelayMs, token).ContinueWith(t =>
|
||||||
|
{
|
||||||
|
if (!t.IsCanceled)
|
||||||
|
_ = ExecutePreviewAsync(sourceImage, token);
|
||||||
|
}, TaskScheduler.FromCurrentSynchronizationContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecutePreviewAsync(BitmapSource sourceImage, CancellationToken token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:开始执行,节点数={Count}", PipelineNodes.Count);
|
||||||
|
var result = await _executionService.ExecutePipelineAsync(PipelineNodes, sourceImage, null, token);
|
||||||
|
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:执行完成,推送结果图像");
|
||||||
|
_mainViewportService.SetManualImage(result, string.Empty);
|
||||||
|
_eventAggregator?.GetEvent<PipelinePreviewUpdatedEvent>()
|
||||||
|
.Publish(new PipelinePreviewUpdatedPayload(result, StatusMessage));
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_logger.Debug("[图像链路][CNC] ExecutePreviewAsync:已取消");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "[图像链路][CNC] ExecutePreviewAsync:执行失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PipelineModel BuildPipelineModel()
|
private PipelineModel BuildPipelineModel()
|
||||||
|
|||||||
@@ -357,7 +357,12 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
private async Task ExecutePipelineAsync()
|
private async Task ExecutePipelineAsync()
|
||||||
{
|
{
|
||||||
if (SourceImage == null || IsExecuting) return;
|
if (SourceImage == null || IsExecuting)
|
||||||
|
{
|
||||||
|
_logger.Debug("[图像链路] ExecutePipelineAsync:跳过,SourceImage={HasImage},IsExecuting={IsExec}",
|
||||||
|
SourceImage != null, IsExecuting);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (TryReportInvalidParameters())
|
if (TryReportInvalidParameters())
|
||||||
return;
|
return;
|
||||||
@@ -368,6 +373,7 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
IsExecuting = true;
|
IsExecuting = true;
|
||||||
SetInfoStatus("正在执行流水线...");
|
SetInfoStatus("正在执行流水线...");
|
||||||
|
_logger.Info("[图像链路] ExecutePipelineAsync:开始执行,节点数={Count}", PipelineNodes.Count);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -379,19 +385,23 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
PreviewImage = result;
|
PreviewImage = result;
|
||||||
SetInfoStatus("流水线执行完成");
|
SetInfoStatus("流水线执行完成");
|
||||||
|
_logger.Info("[图像链路] ExecutePipelineAsync:执行完成,准备发布 PipelinePreviewUpdatedEvent");
|
||||||
PublishPipelinePreviewUpdated(result, StatusMessage);
|
PublishPipelinePreviewUpdated(result, StatusMessage);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
SetInfoStatus("流水线执行已取消");
|
SetInfoStatus("流水线执行已取消");
|
||||||
|
_logger.Info("[图像链路] ExecutePipelineAsync:执行已取消");
|
||||||
}
|
}
|
||||||
catch (PipelineExecutionException ex)
|
catch (PipelineExecutionException ex)
|
||||||
{
|
{
|
||||||
SetErrorStatus($"执行失败:{ex.Message}");
|
SetErrorStatus($"执行失败:{ex.Message}");
|
||||||
|
_logger.Warn("[图像链路] ExecutePipelineAsync:执行失败 {Msg}", ex.Message);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
SetErrorStatus($"执行错误:{ex.Message}");
|
SetErrorStatus($"执行错误:{ex.Message}");
|
||||||
|
_logger.Error(ex, "[图像链路] ExecutePipelineAsync:未预期异常");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -491,8 +501,13 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
private void PublishPipelinePreviewUpdated(BitmapSource bitmap, string statusMessage)
|
private void PublishPipelinePreviewUpdated(BitmapSource bitmap, string statusMessage)
|
||||||
{
|
{
|
||||||
if (bitmap == null) return;
|
if (bitmap == null)
|
||||||
|
{
|
||||||
|
_logger.Warn("[图像链路] PublishPipelinePreviewUpdated:bitmap 为 null,跳过发布");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Info("[图像链路] PublishPipelinePreviewUpdated:发布事件,statusMessage={Msg}", statusMessage);
|
||||||
_eventAggregator.GetEvent<PipelinePreviewUpdatedEvent>()
|
_eventAggregator.GetEvent<PipelinePreviewUpdatedEvent>()
|
||||||
.Publish(new PipelinePreviewUpdatedPayload(bitmap, statusMessage));
|
.Publish(new PipelinePreviewUpdatedPayload(bitmap, statusMessage));
|
||||||
}
|
}
|
||||||
@@ -502,6 +517,7 @@ namespace XplorePlane.ViewModels
|
|||||||
if (payload?.Image == null) return;
|
if (payload?.Image == null) return;
|
||||||
if (ReferenceEquals(SourceImage, payload.Image)) return;
|
if (ReferenceEquals(SourceImage, payload.Image)) return;
|
||||||
|
|
||||||
|
_logger.Info("[图像链路] OnManualImageLoaded:收到图像 {File},设置 SourceImage", payload.FileName);
|
||||||
SourceImage = payload.Image;
|
SourceImage = payload.Image;
|
||||||
PreviewImage = payload.Image;
|
PreviewImage = payload.Image;
|
||||||
SetInfoStatus($"已加载图像:{payload.FileName}");
|
SetInfoStatus($"已加载图像:{payload.FileName}");
|
||||||
@@ -514,8 +530,13 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
private void TriggerDebouncedExecution()
|
private void TriggerDebouncedExecution()
|
||||||
{
|
{
|
||||||
if (SourceImage == null) return;
|
if (SourceImage == null)
|
||||||
|
{
|
||||||
|
_logger.Debug("[图像链路] TriggerDebouncedExecution:SourceImage 为 null,跳过执行");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("[图像链路] TriggerDebouncedExecution:触发防抖执行,节点数={Count}", PipelineNodes.Count);
|
||||||
_debounceCts?.Cancel();
|
_debounceCts?.Cancel();
|
||||||
_debounceCts = new CancellationTokenSource();
|
_debounceCts = new CancellationTokenSource();
|
||||||
var token = _debounceCts.Token;
|
var token = _debounceCts.Token;
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ namespace XplorePlane.ViewModels
|
|||||||
_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();
|
||||||
|
|
||||||
|
_eventAggregator.GetEvent<PipelinePreviewUpdatedEvent>()
|
||||||
|
.Subscribe(OnPipelinePreviewUpdated, ThreadOption.UIThread);
|
||||||
|
|
||||||
NavigationTree = new ObservableCollection<object>();
|
NavigationTree = new ObservableCollection<object>();
|
||||||
|
|
||||||
NavigateHomeCommand = new DelegateCommand(OnNavigateHome);
|
NavigateHomeCommand = new DelegateCommand(OnNavigateHome);
|
||||||
@@ -416,7 +419,14 @@ namespace XplorePlane.ViewModels
|
|||||||
bitmap.EndInit();
|
bitmap.EndInit();
|
||||||
bitmap.Freeze();
|
bitmap.Freeze();
|
||||||
|
|
||||||
|
_logger.Info("[图像链路] ExecuteLoadImage:加载图像 {Path},准备推送到 MainViewportService 和 ManualImageLoadedEvent", dialog.FileName);
|
||||||
_mainViewportService.SetManualImage(bitmap, dialog.FileName);
|
_mainViewportService.SetManualImage(bitmap, dialog.FileName);
|
||||||
|
|
||||||
|
// 同时发布事件,让 PipelineEditorViewModel 收到图像并触发流水线执行
|
||||||
|
_eventAggregator.GetEvent<ManualImageLoadedEvent>()
|
||||||
|
.Publish(new ManualImageLoadedPayload(bitmap, dialog.FileName));
|
||||||
|
_logger.Info("[图像链路] ManualImageLoadedEvent 已发布");
|
||||||
|
|
||||||
RaisePropertyChanged(nameof(IsUsingLiveDetectorSource));
|
RaisePropertyChanged(nameof(IsUsingLiveDetectorSource));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -553,5 +563,16 @@ namespace XplorePlane.ViewModels
|
|||||||
RaisePropertyChanged(nameof(IsUsingLiveDetectorSource));
|
RaisePropertyChanged(nameof(IsUsingLiveDetectorSource));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnPipelinePreviewUpdated(PipelinePreviewUpdatedPayload payload)
|
||||||
|
{
|
||||||
|
if (payload?.Image == null)
|
||||||
|
{
|
||||||
|
_logger.Warn("[图像链路] OnPipelinePreviewUpdated:payload 或 Image 为 null,跳过");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_logger.Info("[图像链路] OnPipelinePreviewUpdated:收到流水线结果图像,推送到 MainViewportService");
|
||||||
|
_mainViewportService.SetManualImage(payload.Image, string.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,12 +61,18 @@ namespace XplorePlane.Views.Cnc
|
|||||||
var imageProcessingService = ContainerLocator.Current.Resolve<IImageProcessingService>();
|
var imageProcessingService = ContainerLocator.Current.Resolve<IImageProcessingService>();
|
||||||
var persistenceService = ContainerLocator.Current.Resolve<IPipelinePersistenceService>();
|
var persistenceService = ContainerLocator.Current.Resolve<IPipelinePersistenceService>();
|
||||||
var logger = ContainerLocator.Current.Resolve<ILoggerService>();
|
var logger = ContainerLocator.Current.Resolve<ILoggerService>();
|
||||||
|
var executionService = ContainerLocator.Current.Resolve<IPipelineExecutionService>();
|
||||||
|
var mainViewportService = ContainerLocator.Current.Resolve<XplorePlane.Services.MainViewport.IMainViewportService>();
|
||||||
|
var eventAggregator = ContainerLocator.Current.Resolve<Prism.Events.IEventAggregator>();
|
||||||
|
|
||||||
_inspectionModulePipelineViewModel = new CncInspectionModulePipelineViewModel(
|
_inspectionModulePipelineViewModel = new CncInspectionModulePipelineViewModel(
|
||||||
editorViewModel,
|
editorViewModel,
|
||||||
imageProcessingService,
|
imageProcessingService,
|
||||||
persistenceService,
|
persistenceService,
|
||||||
logger);
|
logger,
|
||||||
|
executionService,
|
||||||
|
mainViewportService,
|
||||||
|
eventAggregator);
|
||||||
|
|
||||||
InspectionModulePipelineEditor.DataContext = _inspectionModulePipelineViewModel;
|
InspectionModulePipelineEditor.DataContext = _inspectionModulePipelineViewModel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ namespace XplorePlane.Views
|
|||||||
PipelineListBox.PreviewMouseMove += OnPreviewMouseMove;
|
PipelineListBox.PreviewMouseMove += OnPreviewMouseMove;
|
||||||
PipelineListBox.PreviewMouseLeftButtonUp -= OnPreviewMouseLeftButtonUp;
|
PipelineListBox.PreviewMouseLeftButtonUp -= OnPreviewMouseLeftButtonUp;
|
||||||
PipelineListBox.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp;
|
PipelineListBox.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp;
|
||||||
|
PipelineListBox.MouseDoubleClick -= OnMouseDoubleClick;
|
||||||
|
PipelineListBox.MouseDoubleClick += OnMouseDoubleClick;
|
||||||
PipelineListBox.PreviewKeyDown -= OnPreviewKeyDown;
|
PipelineListBox.PreviewKeyDown -= OnPreviewKeyDown;
|
||||||
PipelineListBox.PreviewKeyDown += OnPreviewKeyDown;
|
PipelineListBox.PreviewKeyDown += OnPreviewKeyDown;
|
||||||
}
|
}
|
||||||
@@ -103,7 +105,6 @@ namespace XplorePlane.Views
|
|||||||
|
|
||||||
private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
private void OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
var vm = DataContext as IPipelineEditorHostViewModel;
|
|
||||||
var clickedNode = FindNodeFromOriginalSource(e.OriginalSource);
|
var clickedNode = FindNodeFromOriginalSource(e.OriginalSource);
|
||||||
|
|
||||||
if (_isInternalDragging)
|
if (_isInternalDragging)
|
||||||
@@ -119,19 +120,32 @@ namespace XplorePlane.Views
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm == null || clickedNode == null || IsInteractiveChild(e.OriginalSource))
|
if (clickedNode == null || IsInteractiveChild(e.OriginalSource))
|
||||||
{
|
{
|
||||||
ResetDragState();
|
ResetDragState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 单击只选中,不切换启用状态
|
||||||
PipelineListBox.SelectedItem = clickedNode;
|
PipelineListBox.SelectedItem = clickedNode;
|
||||||
PipelineListBox.Focus();
|
PipelineListBox.Focus();
|
||||||
vm.ToggleOperatorEnabledCommand.Execute(clickedNode);
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
ResetDragState();
|
ResetDragState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var vm = DataContext as IPipelineEditorHostViewModel;
|
||||||
|
var clickedNode = FindNodeFromOriginalSource(e.OriginalSource);
|
||||||
|
|
||||||
|
if (vm == null || clickedNode == null || IsInteractiveChild(e.OriginalSource))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 双击切换启用/禁用
|
||||||
|
vm.ToggleOperatorEnabledCommand.Execute(clickedNode);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
|
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Key != Key.Delete || DataContext is not IPipelineEditorHostViewModel vm || vm.SelectedNode == null)
|
if (e.Key != Key.Delete || DataContext is not IPipelineEditorHostViewModel vm || vm.SelectedNode == null)
|
||||||
|
|||||||
Reference in New Issue
Block a user