diff --git a/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs b/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs index a371375..07c53a1 100644 --- a/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs +++ b/XplorePlane/ViewModels/Cnc/CncEditorViewModel.cs @@ -42,6 +42,9 @@ namespace XplorePlane.ViewModels.Cnc private bool _pendingInsertAfterAnchor; private CancellationTokenSource _cts; private bool _isRunning; + private string _statusMessage = "就绪"; + private string _executionError; + private bool _hasExecutionError; public CncEditorViewModel( ICncProgramService cncProgramService, @@ -159,6 +162,24 @@ namespace XplorePlane.ViewModels.Cnc } } + public string StatusMessage + { + get => _statusMessage; + private set => SetProperty(ref _statusMessage, value); + } + + public string ExecutionError + { + get => _executionError; + private set => SetProperty(ref _executionError, value); + } + + public bool HasExecutionError + { + get => _hasExecutionError; + private set => SetProperty(ref _hasExecutionError, value); + } + public DelegateCommand InsertReferencePointCommand { get; } public DelegateCommand InsertSaveNodeWithImageCommand { get; } public DelegateCommand InsertSaveNodeCommand { get; } @@ -435,14 +456,28 @@ namespace XplorePlane.ViewModels.Cnc { _cts = new CancellationTokenSource(); IsRunning = true; + HasExecutionError = false; + ExecutionError = null; + StatusMessage = $"正在执行:{_currentProgram?.Name ?? "程序"}(共 {_currentProgram?.Nodes?.Count ?? 0} 个节点)"; try { var progress = new Progress(OnExecutionProgress); await _cncExecutionService.ExecuteAsync(_currentProgram, progress, _cts.Token); + if (_cts.IsCancellationRequested) + StatusMessage = "执行已停止"; + else + StatusMessage = $"执行完成:{_currentProgram?.Name}"; + } + catch (OperationCanceledException) + { + StatusMessage = "执行已取消"; } catch (Exception ex) { _logger.Error(ex, "CNC execution failed"); + ExecutionError = ex.Message; + HasExecutionError = true; + StatusMessage = $"执行失败:{ex.Message}"; } finally { @@ -462,7 +497,17 @@ namespace XplorePlane.ViewModels.Cnc { var nodeVm = Nodes.FirstOrDefault(n => n.Id == progress.NodeId); if (nodeVm != null) + { nodeVm.ExecutionState = progress.State; + if (progress.State == NodeExecutionState.Running) + StatusMessage = $"正在执行节点:{nodeVm.Name}({nodeVm.Index + 1}/{_currentProgram?.Nodes?.Count ?? 0})"; + else if (progress.State == NodeExecutionState.Failed) + { + HasExecutionError = true; + ExecutionError = $"节点 [{nodeVm.Name}] 执行失败"; + StatusMessage = $"错误:节点 [{nodeVm.Name}] 执行失败"; + } + } } private void ResetAllNodeStates() diff --git a/XplorePlane/ViewModels/Main/MainViewModel.cs b/XplorePlane/ViewModels/Main/MainViewModel.cs index 3b52f4f..43cdc1a 100644 --- a/XplorePlane/ViewModels/Main/MainViewModel.cs +++ b/XplorePlane/ViewModels/Main/MainViewModel.cs @@ -57,6 +57,20 @@ namespace XplorePlane.ViewModels _cncPageView = new CncPageView { DataContext = _cncEditorViewModel }; _mainViewportService.StateChanged += OnMainViewportStateChanged; + _cncEditorViewModel.PropertyChanged += (s, e) => + { + if (e.PropertyName == nameof(CncEditorViewModel.StatusMessage)) + RaisePropertyChanged(nameof(CncStatusMessage)); + else if (e.PropertyName == nameof(CncEditorViewModel.HasExecutionError)) + RaisePropertyChanged(nameof(CncHasExecutionError)); + else if (e.PropertyName == nameof(CncEditorViewModel.IsRunning)) + { + RunCncCommand.RaiseCanExecuteChanged(); + StopCncCommand.RaiseCanExecuteChanged(); + } + }; + _cncEditorViewModel.RunCncCommand.CanExecuteChanged += (s, e) => RunCncCommand.RaiseCanExecuteChanged(); + _cncEditorViewModel.StopCncCommand.CanExecuteChanged += (s, e) => StopCncCommand.RaiseCanExecuteChanged(); NavigationTree = new ObservableCollection(); @@ -87,8 +101,12 @@ namespace XplorePlane.ViewModels InsertSaveNodeCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertSaveNodeCommand.Execute())); InsertPauseDialogCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertPauseDialogCommand.Execute())); InsertWaitDelayCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertWaitDelayCommand.Execute())); - RunCncCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.RunCncCommand.Execute())); - StopCncCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.StopCncCommand.Execute())); + RunCncCommand = new DelegateCommand( + () => ExecuteCncEditorAction(vm => vm.RunCncCommand.Execute()), + () => _cncEditorViewModel.RunCncCommand.CanExecute()); + StopCncCommand = new DelegateCommand( + () => ExecuteCncEditorAction(vm => vm.StopCncCommand.Execute()), + () => _cncEditorViewModel.StopCncCommand.CanExecute()); PointDistanceMeasureCommand = new DelegateCommand(ExecutePointDistanceMeasure); PointLineDistanceMeasureCommand = new DelegateCommand(ExecutePointLineDistanceMeasure); @@ -121,6 +139,9 @@ namespace XplorePlane.ViewModels set => SetProperty(ref _licenseInfo, value); } + public string CncStatusMessage => _cncEditorViewModel.StatusMessage; + public bool CncHasExecutionError => _cncEditorViewModel.HasExecutionError; + public ObservableCollection NavigationTree { get; set; } public DelegateCommand NavigateHomeCommand { get; set; } diff --git a/XplorePlane/Views/Main/MainWindow.xaml b/XplorePlane/Views/Main/MainWindow.xaml index d1e32a9..1e51ebf 100644 --- a/XplorePlane/Views/Main/MainWindow.xaml +++ b/XplorePlane/Views/Main/MainWindow.xaml @@ -106,6 +106,7 @@ @@ -114,6 +115,7 @@ @@ -554,7 +556,19 @@ FontFamily="Microsoft YaHei UI" FontSize="11" Foreground="White" - Text="就绪" /> + Text="{Binding CncStatusMessage}"> + + + + + Text="x: 0 y: 0" />