1、当前根节点选择中是 高亮背景和文字不协调,文字改为黑色

2、延时节点执行时,进度条显示功能失效,修复
   3、自动执行,当加载CNC后,点击rabbion中运行,此时没有看到执行中,已执行,等高亮规则
This commit is contained in:
zhengxuan.zhang
2026-05-06 11:46:49 +08:00
parent 48c419d777
commit 2eb3fed4d0
8 changed files with 121 additions and 65 deletions
@@ -95,7 +95,7 @@ namespace XplorePlane.Services.Cnc
break;
}
progress?.Report(new CncNodeExecutionProgress(node.Id, NodeExecutionState.Running));
progress?.Report(new CncNodeExecutionProgress(node.Id, NodeExecutionState.Running, ProgressPercent: 0));
bool nodeSucceeded = true;
@@ -106,7 +106,7 @@ namespace XplorePlane.Services.Cnc
case WaitDelayNode waitNode:
try
{
await ExecuteWaitDelayWithProgressAsync(waitNode, cancellationToken);
await ExecuteWaitDelayWithProgressAsync(waitNode, progress, cancellationToken);
}
catch (OperationCanceledException)
{
@@ -324,11 +324,17 @@ namespace XplorePlane.Services.Cnc
return ms.ToArray();
}
private static async Task ExecuteWaitDelayWithProgressAsync(WaitDelayNode waitNode, CancellationToken cancellationToken)
private static async Task ExecuteWaitDelayWithProgressAsync(
WaitDelayNode waitNode,
IProgress<CncNodeExecutionProgress> progress,
CancellationToken cancellationToken)
{
int totalMs = waitNode.DelayMilliseconds;
if (totalMs <= 0)
{
progress?.Report(new CncNodeExecutionProgress(waitNode.Id, NodeExecutionState.Running, ProgressPercent: 100));
return;
}
const int tickMs = 50;
@@ -341,6 +347,10 @@ namespace XplorePlane.Services.Cnc
int delay = Math.Min(tickMs, remaining);
await Task.Delay(delay, cancellationToken);
elapsed += delay;
progress?.Report(new CncNodeExecutionProgress(
waitNode.Id,
NodeExecutionState.Running,
ProgressPercent: elapsed * 100d / totalMs));
}
}
}
@@ -17,6 +17,11 @@ namespace XplorePlane.Services.Cnc
/// <summary>
/// Progress report for a single CNC node execution.
/// ResultImage is non-null when an InspectionModuleNode produces output.
/// ProgressPercent is used by long-running nodes such as WaitDelayNode.
/// </summary>
public record CncNodeExecutionProgress(Guid NodeId, NodeExecutionState State, BitmapSource ResultImage = null);
public record CncNodeExecutionProgress(
Guid NodeId,
NodeExecutionState State,
BitmapSource ResultImage = null,
double? ProgressPercent = null);
}
@@ -455,6 +455,7 @@ namespace XplorePlane.ViewModels.Cnc
private async Task ExecuteRunAsync()
{
_cts = new CancellationTokenSource();
ResetAllNodeStates();
IsRunning = true;
HasExecutionError = false;
ExecutionError = null;
@@ -482,7 +483,6 @@ namespace XplorePlane.ViewModels.Cnc
finally
{
IsRunning = false;
ResetAllNodeStates();
_cts?.Dispose();
_cts = null;
}
@@ -499,6 +499,7 @@ namespace XplorePlane.ViewModels.Cnc
if (nodeVm != null)
{
nodeVm.ExecutionState = progress.State;
nodeVm.ExecutionProgressPercent = progress.ProgressPercent ?? (progress.State == NodeExecutionState.Succeeded ? 100d : 0d);
if (progress.State == NodeExecutionState.Running)
StatusMessage = $"正在执行节点:{nodeVm.Name}{nodeVm.Index + 1}/{_currentProgram?.Nodes?.Count ?? 0}";
else if (progress.State == NodeExecutionState.Succeeded)
@@ -519,7 +520,10 @@ namespace XplorePlane.ViewModels.Cnc
private void ResetAllNodeStates()
{
foreach (var node in Nodes)
{
node.ExecutionState = NodeExecutionState.Idle;
node.ExecutionProgressPercent = 0;
}
}
private void RaiseEditCommandsCanExecuteChanged()
@@ -16,6 +16,7 @@ namespace XplorePlane.ViewModels.Cnc
private string _icon;
private bool _isExpanded = true;
private NodeExecutionState _executionState = NodeExecutionState.Idle;
private double _executionProgressPercent;
/// <summary>执行后缓存的流水线输出图像(仅 InspectionModuleNode</summary>
public BitmapSource ResultImage { get; set; }
@@ -72,6 +73,7 @@ namespace XplorePlane.ViewModels.Cnc
RaisePropertyChanged(nameof(IsRunningNode));
RaisePropertyChanged(nameof(IsSucceededNode));
RaisePropertyChanged(nameof(IsFailedNode));
RaisePropertyChanged(nameof(IsDelayProgressVisible));
}
}
}
@@ -79,6 +81,21 @@ namespace XplorePlane.ViewModels.Cnc
public bool IsRunningNode => ExecutionState == NodeExecutionState.Running;
public bool IsSucceededNode => ExecutionState == NodeExecutionState.Succeeded;
public bool IsFailedNode => ExecutionState == NodeExecutionState.Failed;
public bool IsDelayProgressVisible => IsWaitDelay && IsRunningNode;
public double ExecutionProgressPercent
{
get => _executionProgressPercent;
set
{
if (SetProperty(ref _executionProgressPercent, Math.Clamp(value, 0d, 100d)))
{
RaisePropertyChanged(nameof(ExecutionProgressText));
}
}
}
public string ExecutionProgressText => $"{ExecutionProgressPercent:0}%";
public bool IsReferencePoint => _model is ReferencePointNode;
public bool IsSaveNode => _model is SaveNodeNode;
@@ -567,6 +584,9 @@ namespace XplorePlane.ViewModels.Cnc
RaisePropertyChanged(nameof(IsRunningNode));
RaisePropertyChanged(nameof(IsSucceededNode));
RaisePropertyChanged(nameof(IsFailedNode));
RaisePropertyChanged(nameof(IsDelayProgressVisible));
RaisePropertyChanged(nameof(ExecutionProgressPercent));
RaisePropertyChanged(nameof(ExecutionProgressText));
}
private enum MotionAxis
@@ -32,7 +32,7 @@ namespace XplorePlane.ViewModels
private PipelineNodeViewModel _selectedNode;
private BitmapSource _sourceImage;
private BitmapSource _previewImage;
private string _pipelineName = "新建流水线";
private string _pipelineName = "新建模块";
private string _selectedDevice = string.Empty;
private bool _isExecuting;
private bool _isStatusError;
@@ -646,7 +646,7 @@ namespace XplorePlane.ViewModels
var dialog = new SaveFileDialog
{
Filter = "XP 模块流水线 (*.xpm)|*.xpm",
Filter = "XP 模块 (*.xpm)|*.xpm",
DefaultExt = ".xpm",
AddExtension = true,
FileName = PipelineName,
@@ -698,7 +698,7 @@ namespace XplorePlane.ViewModels
{
var dialog = new OpenFileDialog
{
Filter = "XP 模块流水线 (*.xpm)|*.xpm",
Filter = "XP 模块 (*.xpm)|*.xpm",
DefaultExt = ".xpm",
InitialDirectory = GetPipelineDirectory()
};
+62 -6
View File
@@ -280,6 +280,7 @@
FontFamily="{StaticResource UiFont}"
FontSize="12"
FontWeight="SemiBold"
x:Name="ProgramRootNameText"
Text="{Binding DisplayName}"
TextTrimming="CharacterEllipsis" />
</StackPanel>
@@ -288,6 +289,7 @@
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="True">
<Setter TargetName="ProgramRootCard" Property="Background" Value="#E7F1FB" />
<Setter TargetName="ProgramRootCard" Property="BorderBrush" Value="#9FC6E8" />
<Setter TargetName="ProgramRootNameText" Property="Foreground" Value="#111111" />
</DataTrigger>
</DataTemplate.Triggers>
</HierarchicalDataTemplate>
@@ -305,6 +307,10 @@
BorderThickness="1"
CornerRadius="4">
<Grid x:Name="NodeRoot" MinHeight="23">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15" />
<ColumnDefinition Width="20" />
@@ -312,7 +318,7 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid Grid.RowSpan="2" Grid.Column="0">
<Border
x:Name="ChildStem"
Width="1"
@@ -330,6 +336,7 @@
</Grid>
<Border
Grid.RowSpan="2"
Grid.Column="1"
Width="16"
Height="16"
@@ -346,6 +353,7 @@
<TextBlock
x:Name="NodeNameText"
Grid.Row="0"
Grid.Column="2"
Margin="3,0,0,0"
VerticalAlignment="Center"
@@ -357,6 +365,7 @@
<StackPanel
x:Name="NodeActions"
Grid.Row="0"
Grid.Column="3"
Margin="0,0,2,0"
VerticalAlignment="Center"
@@ -375,6 +384,31 @@
FontSize="10"
ToolTip="删除" />
</StackPanel>
<Grid
x:Name="WaitDelayProgressHost"
Grid.Row="1"
Grid.Column="2"
Grid.ColumnSpan="2"
Margin="3,2,6,1"
Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ProgressBar
Height="6"
Minimum="0"
Maximum="100"
Value="{Binding ExecutionProgressPercent}" />
<TextBlock
Grid.Column="1"
Margin="6,-4,0,0"
VerticalAlignment="Center"
FontFamily="{StaticResource UiFont}"
FontSize="10"
Foreground="#444444"
Text="{Binding ExecutionProgressText}" />
</Grid>
</Grid>
</Border>
<DataTemplate.Triggers>
@@ -387,22 +421,30 @@
<Setter TargetName="NodeActions" Property="Visibility" Value="Visible" />
<Setter TargetName="NodeCard" Property="Background" Value="#DCEEFF" />
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#71A9DB" />
<Setter TargetName="NodeNameText" Property="Foreground" Value="#1F2D3D" />
</DataTrigger>
<DataTrigger Binding="{Binding ExecutionState}" Value="Running">
<Setter TargetName="NodeCard" Property="Background" Value="#FF1E6FD9" />
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF1E6FD9" />
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
<Setter TargetName="NodeCard" Property="Background" Value="#FFD54F" />
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#C89B00" />
<Setter TargetName="NodeNameText" Property="Foreground" Value="#1F1F1F" />
</DataTrigger>
<DataTrigger Binding="{Binding ExecutionState}" Value="Succeeded">
<Setter TargetName="NodeCard" Property="Background" Value="#FF2E7D32" />
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF2E7D32" />
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF1B5E20" />
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding ExecutionState}" Value="Failed">
<Setter TargetName="NodeCard" Property="Background" Value="#FFC62828" />
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FFC62828" />
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF8E0000" />
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsWaitDelay}" Value="True" />
<Condition Binding="{Binding IsRunningNode}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="WaitDelayProgressHost" Property="Visibility" Value="Visible" />
</MultiDataTrigger>
</DataTemplate.Triggers>
</HierarchicalDataTemplate>
</TreeView.Resources>
@@ -569,6 +611,20 @@
<StackPanel Margin="10,8,10,6">
<TextBlock Style="{StaticResource LabelStyle}" Text="延时 (ms)" />
<TextBox Style="{StaticResource EditorBox}" Text="{Binding SelectedNode.DelayMilliseconds, UpdateSourceTrigger=LostFocus}" />
<ProgressBar
Height="8"
Margin="0,2,0,0"
Minimum="0"
Maximum="100"
Value="{Binding SelectedNode.ExecutionProgressPercent}"
Visibility="{Binding SelectedNode.IsDelayProgressVisible, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
Margin="0,4,0,0"
FontFamily="{StaticResource UiFont}"
FontSize="10"
Foreground="#666666"
Text="{Binding SelectedNode.ExecutionProgressText}"
Visibility="{Binding SelectedNode.IsDelayProgressVisible, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
</GroupBox>
</StackPanel>
+5 -31
View File
@@ -19,14 +19,6 @@ namespace XplorePlane.Views.Cnc
/// </summary>
public partial class CncPageView : UserControl
{
private static readonly Brush SelectedNodeBackground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#E7F0F7"));
private static readonly Brush SelectedNodeBorder = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9CB9D1"));
private static readonly Brush HoverNodeBackground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F6FAFC"));
private static readonly Brush HoverNodeBorder = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#D7E4EE"));
private static readonly Brush SelectedNodeForeground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#1F4E79"));
private static readonly Brush DefaultNodeForeground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#202020"));
private static readonly Brush TransparentBrush = Brushes.Transparent;
private CncInspectionModulePipelineViewModel _inspectionModulePipelineViewModel;
private readonly Dictionary<TextBox, Label> _textDisplayLabels = new();
private readonly Dictionary<CheckBox, Label> _checkDisplayLabels = new();
@@ -186,24 +178,9 @@ namespace XplorePlane.Views.Cnc
continue;
}
if (item.IsSelected)
{
card.Background = SelectedNodeBackground;
card.BorderBrush = SelectedNodeBorder;
ApplyNodeTextForeground(card, SelectedNodeForeground);
}
else if (card.IsMouseOver)
{
card.Background = HoverNodeBackground;
card.BorderBrush = HoverNodeBorder;
ApplyNodeTextForeground(card, DefaultNodeForeground);
}
else
{
card.Background = TransparentBrush;
card.BorderBrush = TransparentBrush;
ApplyNodeTextForeground(card, DefaultNodeForeground);
}
card.ClearValue(Border.BackgroundProperty);
card.ClearValue(Border.BorderBrushProperty);
ClearNodeTextForeground(card);
}
}
@@ -318,14 +295,11 @@ namespace XplorePlane.Views.Cnc
panel.Children.Insert(index + 1, companionControl);
}
private static void ApplyNodeTextForeground(Border card, Brush foreground)
private static void ClearNodeTextForeground(Border card)
{
foreach (var textBlock in FindVisualDescendants<TextBlock>(card))
{
if (textBlock.Visibility == Visibility.Visible)
{
textBlock.Foreground = foreground;
}
textBlock.ClearValue(TextBlock.ForegroundProperty);
}
}
@@ -80,38 +80,25 @@
Orientation="Horizontal">
<Button
Command="{Binding NewPipelineCommand}"
Content="新建"
Content="新建配方"
Style="{StaticResource ToolbarBtn}"
ToolTip="新建流水线" />
ToolTip="新建配方" />
<Button
Command="{Binding SavePipelineCommand}"
Content="保存"
Content="保存配方"
Style="{StaticResource ToolbarBtn}"
ToolTip="保存当前流水线" />
ToolTip="保存当前配方" />
<Button
Width="64"
Command="{Binding SaveAsPipelineCommand}"
Content="另存为"
Style="{StaticResource ToolbarBtn}"
ToolTip="另存当前流水线" />
ToolTip="另存当前配方" />
<Button
Command="{Binding LoadPipelineCommand}"
Content="加载"
Content="加载配方"
Style="{StaticResource ToolbarBtn}"
ToolTip="加载流水线" />
<Button
Width="84"
Command="{Binding ExecuteToNodeCommand}"
CommandParameter="{Binding SelectedNode}"
Content="执行到当前"
Style="{StaticResource ToolbarBtn}"
ToolTip="执行到当前选中的节点" />
<Button
Width="68"
Command="{Binding ClearExecutionRangeCommand}"
Content="执行全部"
Style="{StaticResource ToolbarBtn}"
ToolTip="清除截止位置并执行全部节点" />
ToolTip="加载配方" />
</StackPanel>
<TextBlock