Compare commits
6 Commits
fdf2419eb6
...
a483144d29
| Author | SHA1 | Date | |
|---|---|---|---|
| a483144d29 | |||
| 2eb3fed4d0 | |||
| 48c419d777 | |||
| d4050b4218 | |||
| f5dceeceb7 | |||
| fd4048a6c0 |
@@ -16,8 +16,8 @@
|
|||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
BorderBrush="#FFD5DFE5"
|
BorderBrush="#FFD5DFE5"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
Padding="10"
|
Padding="8"
|
||||||
Margin="0,0,0,10">
|
Margin="0,0,0,8">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock x:Name="txtProcessorName"
|
<TextBlock x:Name="txtProcessorName"
|
||||||
FontSize="14"
|
FontSize="14"
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
<!-- 参数列表 -->
|
<!-- 参数列表 -->
|
||||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel x:Name="pnlParameters" Margin="5" />
|
<StackPanel x:Name="pnlParameters" Margin="2" />
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -143,12 +143,15 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
var textBox = new TextBox
|
var textBox = new TextBox
|
||||||
{
|
{
|
||||||
Text = param.Value.ToString(),
|
Text = param.Value.ToString(),
|
||||||
Width = 100,
|
Width = 56,
|
||||||
HorizontalAlignment = HorizontalAlignment.Left
|
MinWidth = 56,
|
||||||
|
HorizontalContentAlignment = HorizontalAlignment.Center,
|
||||||
|
VerticalContentAlignment = VerticalAlignment.Center
|
||||||
};
|
};
|
||||||
|
|
||||||
if (param.MinValue != null && param.MaxValue != null)
|
if (param.MinValue != null && param.MaxValue != null)
|
||||||
{
|
{
|
||||||
|
var rangeGrid = CreateRangeEditorContainer();
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
Minimum = Convert.ToDouble(param.MinValue),
|
Minimum = Convert.ToDouble(param.MinValue),
|
||||||
@@ -156,7 +159,8 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
Value = Convert.ToDouble(param.Value),
|
Value = Convert.ToDouble(param.Value),
|
||||||
TickFrequency = 1,
|
TickFrequency = 1,
|
||||||
IsSnapToTickEnabled = true,
|
IsSnapToTickEnabled = true,
|
||||||
Margin = new Thickness(0, 0, 0, 5)
|
MinWidth = 120,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center
|
||||||
};
|
};
|
||||||
|
|
||||||
slider.ValueChanged += (s, e) =>
|
slider.ValueChanged += (s, e) =>
|
||||||
@@ -181,7 +185,11 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
panel.Children.Add(slider);
|
Grid.SetColumn(slider, 0);
|
||||||
|
Grid.SetColumn(textBox, 2);
|
||||||
|
rangeGrid.Children.Add(slider);
|
||||||
|
rangeGrid.Children.Add(textBox);
|
||||||
|
panel.Children.Add(rangeGrid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -193,9 +201,9 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
OnParameterChanged();
|
OnParameterChanged();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
panel.Children.Add(textBox);
|
panel.Children.Add(textBox);
|
||||||
|
}
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
@@ -211,19 +219,23 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
var textBox = new TextBox
|
var textBox = new TextBox
|
||||||
{
|
{
|
||||||
Text = Convert.ToDouble(param.Value).ToString("F2"),
|
Text = Convert.ToDouble(param.Value).ToString("F2"),
|
||||||
Width = 100,
|
Width = 56,
|
||||||
HorizontalAlignment = HorizontalAlignment.Left
|
MinWidth = 56,
|
||||||
|
HorizontalContentAlignment = HorizontalAlignment.Center,
|
||||||
|
VerticalContentAlignment = VerticalAlignment.Center
|
||||||
};
|
};
|
||||||
|
|
||||||
if (param.MinValue != null && param.MaxValue != null)
|
if (param.MinValue != null && param.MaxValue != null)
|
||||||
{
|
{
|
||||||
|
var rangeGrid = CreateRangeEditorContainer();
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
Minimum = Convert.ToDouble(param.MinValue),
|
Minimum = Convert.ToDouble(param.MinValue),
|
||||||
Maximum = Convert.ToDouble(param.MaxValue),
|
Maximum = Convert.ToDouble(param.MaxValue),
|
||||||
Value = Convert.ToDouble(param.Value),
|
Value = Convert.ToDouble(param.Value),
|
||||||
TickFrequency = 0.1,
|
TickFrequency = 0.1,
|
||||||
Margin = new Thickness(0, 0, 0, 5)
|
MinWidth = 120,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center
|
||||||
};
|
};
|
||||||
|
|
||||||
slider.ValueChanged += (s, e) =>
|
slider.ValueChanged += (s, e) =>
|
||||||
@@ -248,7 +260,11 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
panel.Children.Add(slider);
|
Grid.SetColumn(slider, 0);
|
||||||
|
Grid.SetColumn(textBox, 2);
|
||||||
|
rangeGrid.Children.Add(slider);
|
||||||
|
rangeGrid.Children.Add(textBox);
|
||||||
|
panel.Children.Add(rangeGrid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -260,9 +276,9 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
OnParameterChanged();
|
OnParameterChanged();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
panel.Children.Add(textBox);
|
panel.Children.Add(textBox);
|
||||||
|
}
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
@@ -302,8 +318,8 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
var comboBox = new ComboBox
|
var comboBox = new ComboBox
|
||||||
{
|
{
|
||||||
Margin = new Thickness(0, 5, 0, 0),
|
Margin = new Thickness(0, 5, 0, 0),
|
||||||
Width = 200,
|
MinWidth = 160,
|
||||||
HorizontalAlignment = HorizontalAlignment.Left
|
HorizontalAlignment = HorizontalAlignment.Stretch
|
||||||
};
|
};
|
||||||
|
|
||||||
if (param.Options != null)
|
if (param.Options != null)
|
||||||
@@ -344,8 +360,8 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
{
|
{
|
||||||
Text = param.Value?.ToString() ?? "",
|
Text = param.Value?.ToString() ?? "",
|
||||||
Margin = new Thickness(0, 5, 0, 0),
|
Margin = new Thickness(0, 5, 0, 0),
|
||||||
Width = 200,
|
MinWidth = 160,
|
||||||
HorizontalAlignment = HorizontalAlignment.Left
|
HorizontalAlignment = HorizontalAlignment.Stretch
|
||||||
};
|
};
|
||||||
|
|
||||||
textBox.TextChanged += (s, e) =>
|
textBox.TextChanged += (s, e) =>
|
||||||
@@ -374,4 +390,16 @@ public partial class ProcessorParameterControl : UserControl
|
|||||||
pnlParameters.Children.Clear();
|
pnlParameters.Children.Clear();
|
||||||
UpdateNoProcessorText();
|
UpdateNoProcessorText();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private static Grid CreateRangeEditorContainer()
|
||||||
|
{
|
||||||
|
var grid = new Grid
|
||||||
|
{
|
||||||
|
Margin = new Thickness(0, 0, 0, 4)
|
||||||
|
};
|
||||||
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||||
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(6) });
|
||||||
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ namespace XplorePlane.Services.Cnc
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
progress?.Report(new CncNodeExecutionProgress(node.Id, NodeExecutionState.Running));
|
progress?.Report(new CncNodeExecutionProgress(node.Id, NodeExecutionState.Running, ProgressPercent: 0));
|
||||||
|
|
||||||
bool nodeSucceeded = true;
|
bool nodeSucceeded = true;
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ namespace XplorePlane.Services.Cnc
|
|||||||
case WaitDelayNode waitNode:
|
case WaitDelayNode waitNode:
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ExecuteWaitDelayWithProgressAsync(waitNode, cancellationToken);
|
await ExecuteWaitDelayWithProgressAsync(waitNode, progress, cancellationToken);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -324,11 +324,17 @@ namespace XplorePlane.Services.Cnc
|
|||||||
return ms.ToArray();
|
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;
|
int totalMs = waitNode.DelayMilliseconds;
|
||||||
if (totalMs <= 0)
|
if (totalMs <= 0)
|
||||||
|
{
|
||||||
|
progress?.Report(new CncNodeExecutionProgress(waitNode.Id, NodeExecutionState.Running, ProgressPercent: 100));
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const int tickMs = 50;
|
const int tickMs = 50;
|
||||||
|
|
||||||
@@ -341,6 +347,10 @@ namespace XplorePlane.Services.Cnc
|
|||||||
int delay = Math.Min(tickMs, remaining);
|
int delay = Math.Min(tickMs, remaining);
|
||||||
await Task.Delay(delay, cancellationToken);
|
await Task.Delay(delay, cancellationToken);
|
||||||
elapsed += delay;
|
elapsed += delay;
|
||||||
|
progress?.Report(new CncNodeExecutionProgress(
|
||||||
|
waitNode.Id,
|
||||||
|
NodeExecutionState.Running,
|
||||||
|
ProgressPercent: elapsed * 100d / totalMs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ namespace XplorePlane.Services.Cnc
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Progress report for a single CNC node execution.
|
/// Progress report for a single CNC node execution.
|
||||||
/// ResultImage is non-null when an InspectionModuleNode produces output.
|
/// ResultImage is non-null when an InspectionModuleNode produces output.
|
||||||
|
/// ProgressPercent is used by long-running nodes such as WaitDelayNode.
|
||||||
/// </summary>
|
/// </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()
|
private async Task ExecuteRunAsync()
|
||||||
{
|
{
|
||||||
_cts = new CancellationTokenSource();
|
_cts = new CancellationTokenSource();
|
||||||
|
ResetAllNodeStates();
|
||||||
IsRunning = true;
|
IsRunning = true;
|
||||||
HasExecutionError = false;
|
HasExecutionError = false;
|
||||||
ExecutionError = null;
|
ExecutionError = null;
|
||||||
@@ -482,7 +483,6 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
ResetAllNodeStates();
|
|
||||||
_cts?.Dispose();
|
_cts?.Dispose();
|
||||||
_cts = null;
|
_cts = null;
|
||||||
}
|
}
|
||||||
@@ -499,6 +499,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
if (nodeVm != null)
|
if (nodeVm != null)
|
||||||
{
|
{
|
||||||
nodeVm.ExecutionState = progress.State;
|
nodeVm.ExecutionState = progress.State;
|
||||||
|
nodeVm.ExecutionProgressPercent = progress.ProgressPercent ?? (progress.State == NodeExecutionState.Succeeded ? 100d : 0d);
|
||||||
if (progress.State == NodeExecutionState.Running)
|
if (progress.State == NodeExecutionState.Running)
|
||||||
StatusMessage = $"正在执行节点:{nodeVm.Name}({nodeVm.Index + 1}/{_currentProgram?.Nodes?.Count ?? 0})";
|
StatusMessage = $"正在执行节点:{nodeVm.Name}({nodeVm.Index + 1}/{_currentProgram?.Nodes?.Count ?? 0})";
|
||||||
else if (progress.State == NodeExecutionState.Succeeded)
|
else if (progress.State == NodeExecutionState.Succeeded)
|
||||||
@@ -519,7 +520,10 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
private void ResetAllNodeStates()
|
private void ResetAllNodeStates()
|
||||||
{
|
{
|
||||||
foreach (var node in Nodes)
|
foreach (var node in Nodes)
|
||||||
|
{
|
||||||
node.ExecutionState = NodeExecutionState.Idle;
|
node.ExecutionState = NodeExecutionState.Idle;
|
||||||
|
node.ExecutionProgressPercent = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RaiseEditCommandsCanExecuteChanged()
|
private void RaiseEditCommandsCanExecuteChanged()
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
private string _statusMessage = "请选择检测模块以编辑其流水线。";
|
private string _statusMessage = "请选择检测模块以编辑其流水线。";
|
||||||
private string _pipelineFileDisplayName = "未命名模块.xpm";
|
private string _pipelineFileDisplayName = "未命名模块.xpm";
|
||||||
private string _currentFilePath;
|
private string _currentFilePath;
|
||||||
|
private PipelineNodeViewModel _executionEndNode;
|
||||||
private bool _isSynchronizing;
|
private bool _isSynchronizing;
|
||||||
private CancellationTokenSource _debounceCts;
|
private CancellationTokenSource _debounceCts;
|
||||||
|
|
||||||
@@ -65,6 +66,8 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
RemoveOperatorCommand = new DelegateCommand<PipelineNodeViewModel>(RemoveOperator);
|
RemoveOperatorCommand = new DelegateCommand<PipelineNodeViewModel>(RemoveOperator);
|
||||||
ReorderOperatorCommand = new DelegateCommand<PipelineReorderArgs>(ReorderOperator);
|
ReorderOperatorCommand = new DelegateCommand<PipelineReorderArgs>(ReorderOperator);
|
||||||
ToggleOperatorEnabledCommand = new DelegateCommand<PipelineNodeViewModel>(ToggleOperatorEnabled);
|
ToggleOperatorEnabledCommand = new DelegateCommand<PipelineNodeViewModel>(ToggleOperatorEnabled);
|
||||||
|
ExecuteToNodeCommand = new DelegateCommand<PipelineNodeViewModel>(ExecuteToNode);
|
||||||
|
ClearExecutionRangeCommand = new DelegateCommand(ClearExecutionRange);
|
||||||
MoveNodeUpCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeUp);
|
MoveNodeUpCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeUp);
|
||||||
MoveNodeDownCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeDown);
|
MoveNodeDownCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeDown);
|
||||||
NewPipelineCommand = new DelegateCommand(NewPipeline);
|
NewPipelineCommand = new DelegateCommand(NewPipeline);
|
||||||
@@ -98,6 +101,16 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
private set => SetProperty(ref _pipelineFileDisplayName, value);
|
private set => SetProperty(ref _pipelineFileDisplayName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PipelineNodeViewModel ExecutionEndNode
|
||||||
|
{
|
||||||
|
get => _executionEndNode;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _executionEndNode, value))
|
||||||
|
UpdateExecutionRangeState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool HasActiveModule => _activeModuleNode?.IsInspectionModule == true;
|
public bool HasActiveModule => _activeModuleNode?.IsInspectionModule == true;
|
||||||
|
|
||||||
public Visibility EditorVisibility => HasActiveModule ? Visibility.Visible : Visibility.Collapsed;
|
public Visibility EditorVisibility => HasActiveModule ? Visibility.Visible : Visibility.Collapsed;
|
||||||
@@ -112,6 +125,10 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
|
|
||||||
public ICommand ToggleOperatorEnabledCommand { get; }
|
public ICommand ToggleOperatorEnabledCommand { get; }
|
||||||
|
|
||||||
|
public ICommand ExecuteToNodeCommand { get; }
|
||||||
|
|
||||||
|
public ICommand ClearExecutionRangeCommand { get; }
|
||||||
|
|
||||||
public ICommand MoveNodeUpCommand { get; }
|
public ICommand MoveNodeUpCommand { get; }
|
||||||
|
|
||||||
public ICommand MoveNodeDownCommand { get; }
|
public ICommand MoveNodeDownCommand { get; }
|
||||||
@@ -179,6 +196,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
LoadNodeParameters(node, null);
|
LoadNodeParameters(node, null);
|
||||||
PipelineNodes.Add(node);
|
PipelineNodes.Add(node);
|
||||||
SelectedNode = node;
|
SelectedNode = node;
|
||||||
|
UpdateExecutionRangeState();
|
||||||
PersistActiveModule($"已添加算子:{displayName}");
|
PersistActiveModule($"已添加算子:{displayName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +208,10 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
var removedIndex = PipelineNodes.IndexOf(node);
|
var removedIndex = PipelineNodes.IndexOf(node);
|
||||||
PipelineNodes.Remove(node);
|
PipelineNodes.Remove(node);
|
||||||
RenumberNodes();
|
RenumberNodes();
|
||||||
|
if (ReferenceEquals(ExecutionEndNode, node))
|
||||||
|
ExecutionEndNode = null;
|
||||||
|
else
|
||||||
|
UpdateExecutionRangeState();
|
||||||
SelectNeighborAfterRemoval(removedIndex);
|
SelectNeighborAfterRemoval(removedIndex);
|
||||||
|
|
||||||
PersistActiveModule($"已移除算子:{node.DisplayName}");
|
PersistActiveModule($"已移除算子:{node.DisplayName}");
|
||||||
@@ -206,6 +228,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
|
|
||||||
PipelineNodes.Move(index, index - 1);
|
PipelineNodes.Move(index, index - 1);
|
||||||
RenumberNodes();
|
RenumberNodes();
|
||||||
|
UpdateExecutionRangeState();
|
||||||
PersistActiveModule($"已上移算子:{node.DisplayName}");
|
PersistActiveModule($"已上移算子:{node.DisplayName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +248,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
var node = PipelineNodes[oldIndex];
|
var node = PipelineNodes[oldIndex];
|
||||||
PipelineNodes.Move(oldIndex, newIndex);
|
PipelineNodes.Move(oldIndex, newIndex);
|
||||||
RenumberNodes();
|
RenumberNodes();
|
||||||
|
UpdateExecutionRangeState();
|
||||||
SelectedNode = node;
|
SelectedNode = node;
|
||||||
PersistActiveModule($"已调整算子顺序:{node.DisplayName}");
|
PersistActiveModule($"已调整算子顺序:{node.DisplayName}");
|
||||||
}
|
}
|
||||||
@@ -240,6 +264,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
|
|
||||||
PipelineNodes.Move(index, index + 1);
|
PipelineNodes.Move(index, index + 1);
|
||||||
RenumberNodes();
|
RenumberNodes();
|
||||||
|
UpdateExecutionRangeState();
|
||||||
PersistActiveModule($"已下移算子:{node.DisplayName}");
|
PersistActiveModule($"已下移算子:{node.DisplayName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,6 +280,25 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
: $"已停用算子:{node.DisplayName}");
|
: $"已停用算子:{node.DisplayName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExecuteToNode(PipelineNodeViewModel node)
|
||||||
|
{
|
||||||
|
if (!HasActiveModule || node == null || !PipelineNodes.Contains(node))
|
||||||
|
return;
|
||||||
|
|
||||||
|
SelectedNode = node;
|
||||||
|
ExecutionEndNode = node;
|
||||||
|
PersistActiveModule($"已设置执行截止点:{node.DisplayName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearExecutionRange()
|
||||||
|
{
|
||||||
|
if (!HasActiveModule || ExecutionEndNode == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ExecutionEndNode = null;
|
||||||
|
PersistActiveModule("已切换为执行全部节点");
|
||||||
|
}
|
||||||
|
|
||||||
private void NewPipeline()
|
private void NewPipeline()
|
||||||
{
|
{
|
||||||
if (!HasActiveModule)
|
if (!HasActiveModule)
|
||||||
@@ -262,6 +306,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
|
|
||||||
PipelineNodes.Clear();
|
PipelineNodes.Clear();
|
||||||
SelectedNode = null;
|
SelectedNode = null;
|
||||||
|
ExecutionEndNode = null;
|
||||||
_currentFilePath = null;
|
_currentFilePath = null;
|
||||||
PipelineFileDisplayName = GetActivePipelineFileDisplayName();
|
PipelineFileDisplayName = GetActivePipelineFileDisplayName();
|
||||||
PersistActiveModule("已为当前检测模块新建空流水线。");
|
PersistActiveModule("已为当前检测模块新建空流水线。");
|
||||||
@@ -326,6 +371,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
{
|
{
|
||||||
PipelineNodes.Clear();
|
PipelineNodes.Clear();
|
||||||
SelectedNode = null;
|
SelectedNode = null;
|
||||||
|
ExecutionEndNode = null;
|
||||||
|
|
||||||
var orderedNodes = (pipeline?.Nodes ?? new List<PipelineNodeModel>())
|
var orderedNodes = (pipeline?.Nodes ?? new List<PipelineNodeModel>())
|
||||||
.OrderBy(node => node.Order)
|
.OrderBy(node => node.Order)
|
||||||
@@ -346,6 +392,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
}
|
}
|
||||||
|
|
||||||
SelectedNode = PipelineNodes.FirstOrDefault();
|
SelectedNode = PipelineNodes.FirstOrDefault();
|
||||||
|
UpdateExecutionRangeState();
|
||||||
if (string.IsNullOrEmpty(_currentFilePath))
|
if (string.IsNullOrEmpty(_currentFilePath))
|
||||||
PipelineFileDisplayName = GetActivePipelineFileDisplayName();
|
PipelineFileDisplayName = GetActivePipelineFileDisplayName();
|
||||||
StatusMessage = HasActiveModule
|
StatusMessage = HasActiveModule
|
||||||
@@ -423,7 +470,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:开始执行,节点数={Count}", PipelineNodes.Count);
|
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:开始执行,节点数={Count}", PipelineNodes.Count);
|
||||||
var result = await _executionService.ExecutePipelineAsync(PipelineNodes, sourceImage, null, token);
|
var result = await _executionService.ExecutePipelineAsync(GetNodesInExecutionScope(), sourceImage, null, token);
|
||||||
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:执行完成,推送结果图像");
|
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:执行完成,推送结果图像");
|
||||||
_mainViewportService.SetManualImage(result, string.Empty);
|
_mainViewportService.SetManualImage(result, string.Empty);
|
||||||
_eventAggregator?.GetEvent<PipelinePreviewUpdatedEvent>()
|
_eventAggregator?.GetEvent<PipelinePreviewUpdatedEvent>()
|
||||||
@@ -459,6 +506,15 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<PipelineNodeViewModel> GetNodesInExecutionScope()
|
||||||
|
{
|
||||||
|
var orderedNodes = PipelineNodes.OrderBy(node => node.Order);
|
||||||
|
if (ExecutionEndNode == null)
|
||||||
|
return orderedNodes;
|
||||||
|
|
||||||
|
return orderedNodes.Where(node => node.Order <= ExecutionEndNode.Order);
|
||||||
|
}
|
||||||
|
|
||||||
private string GetActivePipelineName()
|
private string GetActivePipelineName()
|
||||||
{
|
{
|
||||||
if (!HasActiveModule)
|
if (!HasActiveModule)
|
||||||
@@ -492,6 +548,19 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateExecutionRangeState()
|
||||||
|
{
|
||||||
|
if (_executionEndNode != null && !PipelineNodes.Contains(_executionEndNode))
|
||||||
|
_executionEndNode = null;
|
||||||
|
|
||||||
|
var endOrder = _executionEndNode?.Order;
|
||||||
|
foreach (var node in PipelineNodes)
|
||||||
|
{
|
||||||
|
node.IsExecutionEndNode = endOrder.HasValue && node.Order == endOrder.Value;
|
||||||
|
node.IsSkippedByExecutionRange = endOrder.HasValue && node.Order > endOrder.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SelectNeighborAfterRemoval(int removedIndex)
|
private void SelectNeighborAfterRemoval(int removedIndex)
|
||||||
{
|
{
|
||||||
if (PipelineNodes.Count == 0)
|
if (PipelineNodes.Count == 0)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
private string _icon;
|
private string _icon;
|
||||||
private bool _isExpanded = true;
|
private bool _isExpanded = true;
|
||||||
private NodeExecutionState _executionState = NodeExecutionState.Idle;
|
private NodeExecutionState _executionState = NodeExecutionState.Idle;
|
||||||
|
private double _executionProgressPercent;
|
||||||
|
|
||||||
/// <summary>执行后缓存的流水线输出图像(仅 InspectionModuleNode)</summary>
|
/// <summary>执行后缓存的流水线输出图像(仅 InspectionModuleNode)</summary>
|
||||||
public BitmapSource ResultImage { get; set; }
|
public BitmapSource ResultImage { get; set; }
|
||||||
@@ -72,6 +73,7 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
RaisePropertyChanged(nameof(IsRunningNode));
|
RaisePropertyChanged(nameof(IsRunningNode));
|
||||||
RaisePropertyChanged(nameof(IsSucceededNode));
|
RaisePropertyChanged(nameof(IsSucceededNode));
|
||||||
RaisePropertyChanged(nameof(IsFailedNode));
|
RaisePropertyChanged(nameof(IsFailedNode));
|
||||||
|
RaisePropertyChanged(nameof(IsDelayProgressVisible));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,6 +81,21 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
public bool IsRunningNode => ExecutionState == NodeExecutionState.Running;
|
public bool IsRunningNode => ExecutionState == NodeExecutionState.Running;
|
||||||
public bool IsSucceededNode => ExecutionState == NodeExecutionState.Succeeded;
|
public bool IsSucceededNode => ExecutionState == NodeExecutionState.Succeeded;
|
||||||
public bool IsFailedNode => ExecutionState == NodeExecutionState.Failed;
|
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 IsReferencePoint => _model is ReferencePointNode;
|
||||||
public bool IsSaveNode => _model is SaveNodeNode;
|
public bool IsSaveNode => _model is SaveNodeNode;
|
||||||
@@ -567,6 +584,9 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
RaisePropertyChanged(nameof(IsRunningNode));
|
RaisePropertyChanged(nameof(IsRunningNode));
|
||||||
RaisePropertyChanged(nameof(IsSucceededNode));
|
RaisePropertyChanged(nameof(IsSucceededNode));
|
||||||
RaisePropertyChanged(nameof(IsFailedNode));
|
RaisePropertyChanged(nameof(IsFailedNode));
|
||||||
|
RaisePropertyChanged(nameof(IsDelayProgressVisible));
|
||||||
|
RaisePropertyChanged(nameof(ExecutionProgressPercent));
|
||||||
|
RaisePropertyChanged(nameof(ExecutionProgressText));
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum MotionAxis
|
private enum MotionAxis
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
ICommand ToggleOperatorEnabledCommand { get; }
|
ICommand ToggleOperatorEnabledCommand { get; }
|
||||||
|
|
||||||
|
ICommand ExecuteToNodeCommand { get; }
|
||||||
|
|
||||||
|
ICommand ClearExecutionRangeCommand { get; }
|
||||||
|
|
||||||
ICommand MoveNodeUpCommand { get; }
|
ICommand MoveNodeUpCommand { get; }
|
||||||
|
|
||||||
ICommand MoveNodeDownCommand { get; }
|
ICommand MoveNodeDownCommand { get; }
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ namespace XplorePlane.ViewModels
|
|||||||
{
|
{
|
||||||
private readonly IImageProcessingService _imageProcessingService;
|
private readonly IImageProcessingService _imageProcessingService;
|
||||||
private string _searchText = string.Empty;
|
private string _searchText = string.Empty;
|
||||||
|
private OperatorGroupViewModel _selectedGroup;
|
||||||
|
|
||||||
// UI 元数据(分类 + 图标)由 ProcessorUiMetadata 统一提供,保持工具箱与流水线图标一致
|
// UI 元数据(分类 + 图标)由 ProcessorUiMetadata 统一提供,保持工具箱与流水线图标一致
|
||||||
|
|
||||||
@@ -52,6 +53,12 @@ namespace XplorePlane.ViewModels
|
|||||||
public ObservableCollection<OperatorDescriptor> FilteredOperators { get; }
|
public ObservableCollection<OperatorDescriptor> FilteredOperators { get; }
|
||||||
public ObservableCollection<OperatorGroupViewModel> FilteredGroups { get; }
|
public ObservableCollection<OperatorGroupViewModel> FilteredGroups { get; }
|
||||||
|
|
||||||
|
public OperatorGroupViewModel SelectedGroup
|
||||||
|
{
|
||||||
|
get => _selectedGroup;
|
||||||
|
set => SetProperty(ref _selectedGroup, value);
|
||||||
|
}
|
||||||
|
|
||||||
public string SearchText
|
public string SearchText
|
||||||
{
|
{
|
||||||
get => _searchText;
|
get => _searchText;
|
||||||
@@ -78,6 +85,7 @@ namespace XplorePlane.ViewModels
|
|||||||
{
|
{
|
||||||
FilteredOperators.Clear();
|
FilteredOperators.Clear();
|
||||||
FilteredGroups.Clear();
|
FilteredGroups.Clear();
|
||||||
|
SelectedGroup = null;
|
||||||
|
|
||||||
var filtered = string.IsNullOrWhiteSpace(SearchText)
|
var filtered = string.IsNullOrWhiteSpace(SearchText)
|
||||||
? AvailableOperators
|
? AvailableOperators
|
||||||
@@ -101,6 +109,8 @@ namespace XplorePlane.ViewModels
|
|||||||
Operators = new ObservableCollection<OperatorDescriptor>(group)
|
Operators = new ObservableCollection<OperatorDescriptor>(group)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectedGroup = FilteredGroups.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetCategoryOrder(string category) => category switch
|
private static int GetCategoryOrder(string category) => category switch
|
||||||
|
|||||||
@@ -32,13 +32,14 @@ namespace XplorePlane.ViewModels
|
|||||||
private PipelineNodeViewModel _selectedNode;
|
private PipelineNodeViewModel _selectedNode;
|
||||||
private BitmapSource _sourceImage;
|
private BitmapSource _sourceImage;
|
||||||
private BitmapSource _previewImage;
|
private BitmapSource _previewImage;
|
||||||
private string _pipelineName = "新建流水线";
|
private string _pipelineName = "新建模块";
|
||||||
private string _selectedDevice = string.Empty;
|
private string _selectedDevice = string.Empty;
|
||||||
private bool _isExecuting;
|
private bool _isExecuting;
|
||||||
private bool _isStatusError;
|
private bool _isStatusError;
|
||||||
private string _statusMessage = string.Empty;
|
private string _statusMessage = string.Empty;
|
||||||
private string _pipelineFileDisplayName = DefaultPipelineFileDisplayName;
|
private string _pipelineFileDisplayName = DefaultPipelineFileDisplayName;
|
||||||
private string _currentFilePath;
|
private string _currentFilePath;
|
||||||
|
private PipelineNodeViewModel _executionEndNode;
|
||||||
|
|
||||||
private CancellationTokenSource _executionCts;
|
private CancellationTokenSource _executionCts;
|
||||||
private CancellationTokenSource _debounceCts;
|
private CancellationTokenSource _debounceCts;
|
||||||
@@ -64,6 +65,8 @@ namespace XplorePlane.ViewModels
|
|||||||
ReorderOperatorCommand = new DelegateCommand<PipelineReorderArgs>(ReorderOperator);
|
ReorderOperatorCommand = new DelegateCommand<PipelineReorderArgs>(ReorderOperator);
|
||||||
ToggleOperatorEnabledCommand = new DelegateCommand<PipelineNodeViewModel>(ToggleOperatorEnabled);
|
ToggleOperatorEnabledCommand = new DelegateCommand<PipelineNodeViewModel>(ToggleOperatorEnabled);
|
||||||
ExecutePipelineCommand = new DelegateCommand(async () => await ExecutePipelineAsync(), () => !IsExecuting && SourceImage != null);
|
ExecutePipelineCommand = new DelegateCommand(async () => await ExecutePipelineAsync(), () => !IsExecuting && SourceImage != null);
|
||||||
|
ExecuteToNodeCommand = new DelegateCommand<PipelineNodeViewModel>(async node => await ExecuteToNodeAsync(node), CanExecuteToNode);
|
||||||
|
ClearExecutionRangeCommand = new DelegateCommand(async () => await ClearExecutionRangeAsync(), CanClearExecutionRange);
|
||||||
CancelExecutionCommand = new DelegateCommand(CancelExecution, () => IsExecuting);
|
CancelExecutionCommand = new DelegateCommand(CancelExecution, () => IsExecuting);
|
||||||
NewPipelineCommand = new DelegateCommand(NewPipeline);
|
NewPipelineCommand = new DelegateCommand(NewPipeline);
|
||||||
SavePipelineCommand = new DelegateCommand(async () => await SavePipelineAsync());
|
SavePipelineCommand = new DelegateCommand(async () => await SavePipelineAsync());
|
||||||
@@ -162,6 +165,20 @@ namespace XplorePlane.ViewModels
|
|||||||
private set => SetProperty(ref _pipelineFileDisplayName, value);
|
private set => SetProperty(ref _pipelineFileDisplayName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PipelineNodeViewModel ExecutionEndNode
|
||||||
|
{
|
||||||
|
get => _executionEndNode;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _executionEndNode, value))
|
||||||
|
{
|
||||||
|
UpdateExecutionRangeState();
|
||||||
|
ExecuteToNodeCommand.RaiseCanExecuteChanged();
|
||||||
|
ClearExecutionRangeCommand.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Commands ──────────────────────────────────────────────────
|
// ── Commands ──────────────────────────────────────────────────
|
||||||
|
|
||||||
public DelegateCommand<string> AddOperatorCommand { get; }
|
public DelegateCommand<string> AddOperatorCommand { get; }
|
||||||
@@ -169,6 +186,8 @@ namespace XplorePlane.ViewModels
|
|||||||
public DelegateCommand<PipelineReorderArgs> ReorderOperatorCommand { get; }
|
public DelegateCommand<PipelineReorderArgs> ReorderOperatorCommand { get; }
|
||||||
public DelegateCommand<PipelineNodeViewModel> ToggleOperatorEnabledCommand { get; }
|
public DelegateCommand<PipelineNodeViewModel> ToggleOperatorEnabledCommand { get; }
|
||||||
public DelegateCommand ExecutePipelineCommand { get; }
|
public DelegateCommand ExecutePipelineCommand { get; }
|
||||||
|
public DelegateCommand<PipelineNodeViewModel> ExecuteToNodeCommand { get; }
|
||||||
|
public DelegateCommand ClearExecutionRangeCommand { get; }
|
||||||
public DelegateCommand CancelExecutionCommand { get; }
|
public DelegateCommand CancelExecutionCommand { get; }
|
||||||
public DelegateCommand NewPipelineCommand { get; }
|
public DelegateCommand NewPipelineCommand { get; }
|
||||||
public DelegateCommand SavePipelineCommand { get; }
|
public DelegateCommand SavePipelineCommand { get; }
|
||||||
@@ -184,6 +203,8 @@ namespace XplorePlane.ViewModels
|
|||||||
ICommand IPipelineEditorHostViewModel.RemoveOperatorCommand => RemoveOperatorCommand;
|
ICommand IPipelineEditorHostViewModel.RemoveOperatorCommand => RemoveOperatorCommand;
|
||||||
ICommand IPipelineEditorHostViewModel.ReorderOperatorCommand => ReorderOperatorCommand;
|
ICommand IPipelineEditorHostViewModel.ReorderOperatorCommand => ReorderOperatorCommand;
|
||||||
ICommand IPipelineEditorHostViewModel.ToggleOperatorEnabledCommand => ToggleOperatorEnabledCommand;
|
ICommand IPipelineEditorHostViewModel.ToggleOperatorEnabledCommand => ToggleOperatorEnabledCommand;
|
||||||
|
ICommand IPipelineEditorHostViewModel.ExecuteToNodeCommand => ExecuteToNodeCommand;
|
||||||
|
ICommand IPipelineEditorHostViewModel.ClearExecutionRangeCommand => ClearExecutionRangeCommand;
|
||||||
ICommand IPipelineEditorHostViewModel.MoveNodeUpCommand => MoveNodeUpCommand;
|
ICommand IPipelineEditorHostViewModel.MoveNodeUpCommand => MoveNodeUpCommand;
|
||||||
ICommand IPipelineEditorHostViewModel.MoveNodeDownCommand => MoveNodeDownCommand;
|
ICommand IPipelineEditorHostViewModel.MoveNodeDownCommand => MoveNodeDownCommand;
|
||||||
ICommand IPipelineEditorHostViewModel.NewPipelineCommand => NewPipelineCommand;
|
ICommand IPipelineEditorHostViewModel.NewPipelineCommand => NewPipelineCommand;
|
||||||
@@ -234,6 +255,7 @@ namespace XplorePlane.ViewModels
|
|||||||
LoadNodeParameters(node);
|
LoadNodeParameters(node);
|
||||||
PipelineNodes.Add(node);
|
PipelineNodes.Add(node);
|
||||||
SelectedNode = node;
|
SelectedNode = node;
|
||||||
|
UpdateExecutionRangeState();
|
||||||
_logger.Info("节点已添加到 PipelineNodes:{Key} ({DisplayName}),当前节点数={Count}",
|
_logger.Info("节点已添加到 PipelineNodes:{Key} ({DisplayName}),当前节点数={Count}",
|
||||||
operatorKey, displayName, PipelineNodes.Count);
|
operatorKey, displayName, PipelineNodes.Count);
|
||||||
SetInfoStatus($"已添加算子:{displayName}");
|
SetInfoStatus($"已添加算子:{displayName}");
|
||||||
@@ -247,6 +269,10 @@ namespace XplorePlane.ViewModels
|
|||||||
var removedIndex = PipelineNodes.IndexOf(node);
|
var removedIndex = PipelineNodes.IndexOf(node);
|
||||||
PipelineNodes.Remove(node);
|
PipelineNodes.Remove(node);
|
||||||
RenumberNodes();
|
RenumberNodes();
|
||||||
|
if (ReferenceEquals(ExecutionEndNode, node))
|
||||||
|
ExecutionEndNode = null;
|
||||||
|
else
|
||||||
|
UpdateExecutionRangeState();
|
||||||
SelectNeighborAfterRemoval(removedIndex);
|
SelectNeighborAfterRemoval(removedIndex);
|
||||||
|
|
||||||
SetInfoStatus($"已移除算子:{node.DisplayName}");
|
SetInfoStatus($"已移除算子:{node.DisplayName}");
|
||||||
@@ -260,6 +286,7 @@ namespace XplorePlane.ViewModels
|
|||||||
if (index <= 0) return;
|
if (index <= 0) return;
|
||||||
PipelineNodes.Move(index, index - 1);
|
PipelineNodes.Move(index, index - 1);
|
||||||
RenumberNodes();
|
RenumberNodes();
|
||||||
|
UpdateExecutionRangeState();
|
||||||
TriggerDebouncedExecution();
|
TriggerDebouncedExecution();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,6 +297,7 @@ namespace XplorePlane.ViewModels
|
|||||||
if (index < 0 || index >= PipelineNodes.Count - 1) return;
|
if (index < 0 || index >= PipelineNodes.Count - 1) return;
|
||||||
PipelineNodes.Move(index, index + 1);
|
PipelineNodes.Move(index, index + 1);
|
||||||
RenumberNodes();
|
RenumberNodes();
|
||||||
|
UpdateExecutionRangeState();
|
||||||
TriggerDebouncedExecution();
|
TriggerDebouncedExecution();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +315,7 @@ namespace XplorePlane.ViewModels
|
|||||||
PipelineNodes.RemoveAt(oldIndex);
|
PipelineNodes.RemoveAt(oldIndex);
|
||||||
PipelineNodes.Insert(newIndex, node);
|
PipelineNodes.Insert(newIndex, node);
|
||||||
RenumberNodes();
|
RenumberNodes();
|
||||||
|
UpdateExecutionRangeState();
|
||||||
SelectedNode = node;
|
SelectedNode = node;
|
||||||
SetInfoStatus($"已调整算子顺序:{node.DisplayName}");
|
SetInfoStatus($"已调整算子顺序:{node.DisplayName}");
|
||||||
TriggerDebouncedExecution();
|
TriggerDebouncedExecution();
|
||||||
@@ -304,6 +333,34 @@ namespace XplorePlane.ViewModels
|
|||||||
TriggerDebouncedExecution();
|
TriggerDebouncedExecution();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CanExecuteToNode(PipelineNodeViewModel node) =>
|
||||||
|
node != null && PipelineNodes.Contains(node) && !IsExecuting && SourceImage != null;
|
||||||
|
|
||||||
|
private async Task ExecuteToNodeAsync(PipelineNodeViewModel node)
|
||||||
|
{
|
||||||
|
if (!CanExecuteToNode(node))
|
||||||
|
return;
|
||||||
|
|
||||||
|
SelectedNode = node;
|
||||||
|
ExecutionEndNode = node;
|
||||||
|
await ExecutePipelineAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanClearExecutionRange() =>
|
||||||
|
ExecutionEndNode != null && !IsExecuting;
|
||||||
|
|
||||||
|
private async Task ClearExecutionRangeAsync()
|
||||||
|
{
|
||||||
|
if (ExecutionEndNode == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ExecutionEndNode = null;
|
||||||
|
SetInfoStatus("已切换为执行全部节点");
|
||||||
|
|
||||||
|
if (SourceImage != null)
|
||||||
|
await ExecutePipelineAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private void RenumberNodes()
|
private void RenumberNodes()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < PipelineNodes.Count; i++)
|
for (int i = 0; i < PipelineNodes.Count; i++)
|
||||||
@@ -367,10 +424,15 @@ namespace XplorePlane.ViewModels
|
|||||||
_executionCts?.Cancel();
|
_executionCts?.Cancel();
|
||||||
_executionCts = new CancellationTokenSource();
|
_executionCts = new CancellationTokenSource();
|
||||||
var token = _executionCts.Token;
|
var token = _executionCts.Token;
|
||||||
|
var executionNodes = GetNodesInExecutionScope()
|
||||||
|
.Where(n => n.IsEnabled)
|
||||||
|
.OrderBy(n => n.Order)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
IsExecuting = true;
|
IsExecuting = true;
|
||||||
SetInfoStatus("正在执行流水线...");
|
SetInfoStatus(BuildExecutionStartMessage(executionNodes.Count));
|
||||||
_logger.Info("[图像链路] ExecutePipelineAsync:开始执行,节点数={Count}", PipelineNodes.Count);
|
_logger.Info("[图像链路] ExecutePipelineAsync:开始执行,范围节点数={Count},截止节点={Node}",
|
||||||
|
executionNodes.Count, ExecutionEndNode?.DisplayName ?? "<all>");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -378,10 +440,10 @@ namespace XplorePlane.ViewModels
|
|||||||
SetInfoStatus($"执行中:{p.CurrentOperator} ({p.CurrentStep}/{p.TotalSteps})"));
|
SetInfoStatus($"执行中:{p.CurrentOperator} ({p.CurrentStep}/{p.TotalSteps})"));
|
||||||
|
|
||||||
var result = await _executionService.ExecutePipelineAsync(
|
var result = await _executionService.ExecutePipelineAsync(
|
||||||
PipelineNodes, SourceImage, progress, token);
|
executionNodes, SourceImage, progress, token);
|
||||||
|
|
||||||
PreviewImage = result;
|
PreviewImage = result;
|
||||||
SetInfoStatus("流水线执行完成");
|
SetInfoStatus(BuildExecutionCompletedMessage(executionNodes.Count));
|
||||||
_logger.Info("[图像链路] ExecutePipelineAsync:执行完成,准备发布 PipelinePreviewUpdatedEvent");
|
_logger.Info("[图像链路] ExecutePipelineAsync:执行完成,准备发布 PipelinePreviewUpdatedEvent");
|
||||||
PublishPipelinePreviewUpdated(result, StatusMessage);
|
PublishPipelinePreviewUpdated(result, StatusMessage);
|
||||||
}
|
}
|
||||||
@@ -408,7 +470,7 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
private bool TryReportInvalidParameters()
|
private bool TryReportInvalidParameters()
|
||||||
{
|
{
|
||||||
var firstInvalidNode = PipelineNodes
|
var firstInvalidNode = GetNodesInExecutionScope()
|
||||||
.Where(n => n.IsEnabled)
|
.Where(n => n.IsEnabled)
|
||||||
.OrderBy(n => n.Order)
|
.OrderBy(n => n.Order)
|
||||||
.FirstOrDefault(n => n.Parameters.Any(p => !p.IsValueValid));
|
.FirstOrDefault(n => n.Parameters.Any(p => !p.IsValueValid));
|
||||||
@@ -549,6 +611,7 @@ namespace XplorePlane.ViewModels
|
|||||||
{
|
{
|
||||||
PipelineNodes.Clear();
|
PipelineNodes.Clear();
|
||||||
SelectedNode = null;
|
SelectedNode = null;
|
||||||
|
ExecutionEndNode = null;
|
||||||
PipelineName = "新建流水线";
|
PipelineName = "新建流水线";
|
||||||
PreviewImage = null;
|
PreviewImage = null;
|
||||||
_currentFilePath = null;
|
_currentFilePath = null;
|
||||||
@@ -583,7 +646,7 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
var dialog = new SaveFileDialog
|
var dialog = new SaveFileDialog
|
||||||
{
|
{
|
||||||
Filter = "XP 模块流水线 (*.xpm)|*.xpm",
|
Filter = "XP 模块 (*.xpm)|*.xpm",
|
||||||
DefaultExt = ".xpm",
|
DefaultExt = ".xpm",
|
||||||
AddExtension = true,
|
AddExtension = true,
|
||||||
FileName = PipelineName,
|
FileName = PipelineName,
|
||||||
@@ -635,7 +698,7 @@ namespace XplorePlane.ViewModels
|
|||||||
{
|
{
|
||||||
var dialog = new OpenFileDialog
|
var dialog = new OpenFileDialog
|
||||||
{
|
{
|
||||||
Filter = "XP 模块流水线 (*.xpm)|*.xpm",
|
Filter = "XP 模块 (*.xpm)|*.xpm",
|
||||||
DefaultExt = ".xpm",
|
DefaultExt = ".xpm",
|
||||||
InitialDirectory = GetPipelineDirectory()
|
InitialDirectory = GetPipelineDirectory()
|
||||||
};
|
};
|
||||||
@@ -648,6 +711,7 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
PipelineNodes.Clear();
|
PipelineNodes.Clear();
|
||||||
SelectedNode = null;
|
SelectedNode = null;
|
||||||
|
ExecutionEndNode = null;
|
||||||
|
|
||||||
PipelineName = model.Name;
|
PipelineName = model.Name;
|
||||||
SelectedDevice = model.DeviceId;
|
SelectedDevice = model.DeviceId;
|
||||||
@@ -676,6 +740,8 @@ namespace XplorePlane.ViewModels
|
|||||||
PipelineNodes.Add(node);
|
PipelineNodes.Add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateExecutionRangeState();
|
||||||
|
|
||||||
_logger.Info("流水线已加载:{Name},节点数={Count}", model.Name, PipelineNodes.Count);
|
_logger.Info("流水线已加载:{Name},节点数={Count}", model.Name, PipelineNodes.Count);
|
||||||
SetInfoStatus($"已加载流水线:{model.Name}({PipelineNodes.Count} 个节点)");
|
SetInfoStatus($"已加载流水线:{model.Name}({PipelineNodes.Count} 个节点)");
|
||||||
}
|
}
|
||||||
@@ -704,6 +770,44 @@ namespace XplorePlane.ViewModels
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private System.Collections.Generic.IEnumerable<PipelineNodeViewModel> GetNodesInExecutionScope()
|
||||||
|
{
|
||||||
|
var orderedNodes = PipelineNodes.OrderBy(n => n.Order);
|
||||||
|
if (ExecutionEndNode == null)
|
||||||
|
return orderedNodes;
|
||||||
|
|
||||||
|
return orderedNodes.Where(n => n.Order <= ExecutionEndNode.Order);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateExecutionRangeState()
|
||||||
|
{
|
||||||
|
if (_executionEndNode != null && !PipelineNodes.Contains(_executionEndNode))
|
||||||
|
_executionEndNode = null;
|
||||||
|
|
||||||
|
var endOrder = _executionEndNode?.Order;
|
||||||
|
foreach (var node in PipelineNodes)
|
||||||
|
{
|
||||||
|
node.IsExecutionEndNode = endOrder.HasValue && node.Order == endOrder.Value;
|
||||||
|
node.IsSkippedByExecutionRange = endOrder.HasValue && node.Order > endOrder.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildExecutionStartMessage(int executionCount)
|
||||||
|
{
|
||||||
|
if (ExecutionEndNode == null)
|
||||||
|
return "正在执行流水线...";
|
||||||
|
|
||||||
|
return $"正在执行到“{ExecutionEndNode.DisplayName}” ({executionCount} 个有效节点)...";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string BuildExecutionCompletedMessage(int executionCount)
|
||||||
|
{
|
||||||
|
if (ExecutionEndNode == null)
|
||||||
|
return "流水线执行完成";
|
||||||
|
|
||||||
|
return $"已执行到“{ExecutionEndNode.DisplayName}” ({executionCount} 个有效节点)";
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetPipelineDirectory()
|
private static string GetPipelineDirectory()
|
||||||
{
|
{
|
||||||
var dir = Path.Combine(
|
var dir = Path.Combine(
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ namespace XplorePlane.ViewModels
|
|||||||
private int _order;
|
private int _order;
|
||||||
private bool _isSelected;
|
private bool _isSelected;
|
||||||
private bool _isEnabled = true;
|
private bool _isEnabled = true;
|
||||||
|
private bool _isExecutionEndNode;
|
||||||
|
private bool _isSkippedByExecutionRange;
|
||||||
|
|
||||||
public PipelineNodeViewModel(string operatorKey, string displayName, string iconPath = null)
|
public PipelineNodeViewModel(string operatorKey, string displayName, string iconPath = null)
|
||||||
{
|
{
|
||||||
@@ -51,9 +53,49 @@ namespace XplorePlane.ViewModels
|
|||||||
public bool IsEnabled
|
public bool IsEnabled
|
||||||
{
|
{
|
||||||
get => _isEnabled;
|
get => _isEnabled;
|
||||||
set => SetProperty(ref _isEnabled, value);
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isEnabled, value))
|
||||||
|
RaisePropertyChanged(nameof(NodeStateText));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<ProcessorParameterVM> Parameters { get; }
|
public ObservableCollection<ProcessorParameterVM> Parameters { get; }
|
||||||
|
|
||||||
|
public bool IsExecutionEndNode
|
||||||
|
{
|
||||||
|
get => _isExecutionEndNode;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isExecutionEndNode, value))
|
||||||
|
RaisePropertyChanged(nameof(NodeStateText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSkippedByExecutionRange
|
||||||
|
{
|
||||||
|
get => _isSkippedByExecutionRange;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _isSkippedByExecutionRange, value))
|
||||||
|
RaisePropertyChanged(nameof(NodeStateText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NodeStateText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsExecutionEndNode && !IsEnabled)
|
||||||
|
return "执行到此(停用)";
|
||||||
|
if (IsExecutionEndNode)
|
||||||
|
return "执行到此";
|
||||||
|
if (!IsEnabled)
|
||||||
|
return "已停用";
|
||||||
|
if (IsSkippedByExecutionRange)
|
||||||
|
return "未参与本次执行";
|
||||||
|
return "已启用";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,15 @@ namespace XplorePlane.ViewModels
|
|||||||
public string ParameterType { get; }
|
public string ParameterType { get; }
|
||||||
public bool HasOptions => Options is { Length: > 0 };
|
public bool HasOptions => Options is { Length: > 0 };
|
||||||
public bool IsBool => ParameterType == "bool";
|
public bool IsBool => ParameterType == "bool";
|
||||||
public bool IsTextInput => !IsBool && !HasOptions;
|
public bool IsNumeric => ParameterType is "int" or "double";
|
||||||
|
public bool HasRange => IsNumeric && MinValue != null && MaxValue != null;
|
||||||
|
public bool IsSliderInput => HasRange;
|
||||||
|
public bool IsTextInput => !IsBool && !HasOptions && !IsSliderInput;
|
||||||
|
|
||||||
|
public double SliderMinimum => TryConvertToDouble(MinValue, out var minValue) ? minValue : 0d;
|
||||||
|
public double SliderMaximum => TryConvertToDouble(MaxValue, out var maxValue) ? maxValue : 100d;
|
||||||
|
public double SliderTickFrequency => ResolveTickFrequency();
|
||||||
|
public bool IsIntegerSlider => ParameterType == "int";
|
||||||
|
|
||||||
public bool IsValueValid
|
public bool IsValueValid
|
||||||
{
|
{
|
||||||
@@ -60,10 +68,46 @@ namespace XplorePlane.ViewModels
|
|||||||
RaisePropertyChanged(nameof(Value));
|
RaisePropertyChanged(nameof(Value));
|
||||||
RaisePropertyChanged(nameof(BoolValue));
|
RaisePropertyChanged(nameof(BoolValue));
|
||||||
RaisePropertyChanged(nameof(SelectedOption));
|
RaisePropertyChanged(nameof(SelectedOption));
|
||||||
|
RaisePropertyChanged(nameof(SliderValue));
|
||||||
|
RaisePropertyChanged(nameof(DisplayValueText));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double SliderValue
|
||||||
|
{
|
||||||
|
get => TryConvertToDouble(_value, out var sliderValue) ? sliderValue : SliderMinimum;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!IsSliderInput)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value = ParameterType == "int"
|
||||||
|
? (object)Convert.ToInt32(Math.Round(value, MidpointRounding.AwayFromZero), CultureInfo.InvariantCulture)
|
||||||
|
: Math.Round(value, ResolveDecimalPlaces(), MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DisplayValueText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (ParameterType == "int" && TryConvertToInt(_value, out var intValue))
|
||||||
|
{
|
||||||
|
return intValue.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ParameterType == "double" && TryConvertToDouble(_value, out var doubleValue))
|
||||||
|
{
|
||||||
|
return doubleValue.ToString($"F{ResolveDecimalPlaces()}", CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Convert.ToString(_value, CultureInfo.InvariantCulture) ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool BoolValue
|
public bool BoolValue
|
||||||
{
|
{
|
||||||
get => ParameterType == "bool" && TryConvertToBool(_value, out var boolValue) && boolValue;
|
get => ParameterType == "bool" && TryConvertToBool(_value, out var boolValue) && boolValue;
|
||||||
@@ -154,6 +198,58 @@ namespace XplorePlane.ViewModels
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double ResolveTickFrequency()
|
||||||
|
{
|
||||||
|
if (!HasRange)
|
||||||
|
{
|
||||||
|
return 1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ParameterType == "int")
|
||||||
|
{
|
||||||
|
return 1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
double range = SliderMaximum - SliderMinimum;
|
||||||
|
if (range <= 1d)
|
||||||
|
{
|
||||||
|
return 0.01d;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range <= 10d)
|
||||||
|
{
|
||||||
|
return 0.1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ResolveDecimalPlaces()
|
||||||
|
{
|
||||||
|
if (ParameterType == "int")
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double tick = SliderTickFrequency;
|
||||||
|
if (tick >= 1d)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tick >= 0.1d)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tick >= 0.01d)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
private static string NormalizeNumericText(string value)
|
private static string NormalizeNumericText(string value)
|
||||||
{
|
{
|
||||||
return value.Trim().TrimEnd('、', ',', ',', '。', '.', ';', ';', ':', ':');
|
return value.Trim().TrimEnd('、', ',', ',', '。', '.', ';', ';', ':', ':');
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace XplorePlane.ViewModels
|
|||||||
{
|
{
|
||||||
public class MainViewModel : BindableBase
|
public class MainViewModel : BindableBase
|
||||||
{
|
{
|
||||||
private const double CncEditorHostWidth = 402d;
|
private const double CncEditorHostWidth = 452d;
|
||||||
|
|
||||||
private readonly ILoggerService _logger;
|
private readonly ILoggerService _logger;
|
||||||
private readonly IContainerProvider _containerProvider;
|
private readonly IContainerProvider _containerProvider;
|
||||||
@@ -205,7 +205,7 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
|
|
||||||
LoadImageCommand = new DelegateCommand(ExecuteLoadImage);
|
LoadImageCommand = new DelegateCommand(ExecuteLoadImage);
|
||||||
OpenPipelineEditorCommand = new DelegateCommand(() => ShowWindow(new Views.PipelineEditorWindow(), "流水线编辑器"));
|
|
||||||
OpenCncEditorCommand = new DelegateCommand(ExecuteOpenCncEditor);
|
OpenCncEditorCommand = new DelegateCommand(ExecuteOpenCncEditor);
|
||||||
OpenMatrixEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.MatrixEditorWindow(), "矩阵编排"));
|
OpenMatrixEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.MatrixEditorWindow(), "矩阵编排"));
|
||||||
OpenToolboxCommand = new DelegateCommand(ExecuteOpenToolbox);
|
OpenToolboxCommand = new DelegateCommand(ExecuteOpenToolbox);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
xmlns:views="clr-namespace:XplorePlane.Views"
|
xmlns:views="clr-namespace:XplorePlane.Views"
|
||||||
xmlns:vm="clr-namespace:XplorePlane.ViewModels.Cnc"
|
xmlns:vm="clr-namespace:XplorePlane.ViewModels.Cnc"
|
||||||
d:DesignHeight="760"
|
d:DesignHeight="760"
|
||||||
d:DesignWidth="402"
|
d:DesignWidth="452"
|
||||||
prism:ViewModelLocator.AutoWireViewModel="True"
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
@@ -96,11 +96,26 @@
|
|||||||
<Setter Property="FontFamily" Value="{StaticResource UiFont}" />
|
<Setter Property="FontFamily" Value="{StaticResource UiFont}" />
|
||||||
<Setter Property="FontSize" Value="11" />
|
<Setter Property="FontSize" Value="11" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style x:Key="TreeToolbarButtonCompact" TargetType="Button" BasedOn="{StaticResource TreeToolbarButton}">
|
||||||
|
<Setter Property="Width" Value="28" />
|
||||||
|
<Setter Property="Height" Value="28" />
|
||||||
|
<Setter Property="MinWidth" Value="28" />
|
||||||
|
<Setter Property="Margin" Value="0,0,4,4" />
|
||||||
|
<Setter Property="Padding" Value="0" />
|
||||||
|
<Setter Property="FontSize" Value="10.5" />
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="TreeToolbarIcon" TargetType="Image">
|
||||||
|
<Setter Property="Width" Value="14" />
|
||||||
|
<Setter Property="Height" Value="14" />
|
||||||
|
<Setter Property="Margin" Value="0" />
|
||||||
|
<Setter Property="Stretch" Value="Uniform" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
</Style>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
<Border
|
<Border
|
||||||
Width="402"
|
Width="452"
|
||||||
MinWidth="402"
|
MinWidth="452"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Background="{StaticResource PanelBg}"
|
Background="{StaticResource PanelBg}"
|
||||||
BorderBrush="{StaticResource PanelBorder}"
|
BorderBrush="{StaticResource PanelBorder}"
|
||||||
@@ -108,7 +123,7 @@
|
|||||||
CornerRadius="4">
|
CornerRadius="4">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="200" />
|
<ColumnDefinition Width="180" />
|
||||||
<ColumnDefinition Width="1" />
|
<ColumnDefinition Width="1" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -125,24 +140,109 @@
|
|||||||
Background="{StaticResource HeaderBg}"
|
Background="{StaticResource HeaderBg}"
|
||||||
BorderBrush="{StaticResource SeparatorBrush}"
|
BorderBrush="{StaticResource SeparatorBrush}"
|
||||||
BorderThickness="0,0,0,1">
|
BorderThickness="0,0,0,1">
|
||||||
<WrapPanel>
|
<StackPanel>
|
||||||
|
<WrapPanel/>
|
||||||
|
<WrapPanel Margin="0,4,0,0" Visibility="Collapsed">
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding NewProgramCommand}"
|
Command="{Binding InsertReferencePointCommand}"
|
||||||
Content="新建"
|
Content="参考点"
|
||||||
Style="{StaticResource TreeToolbarButton}" />
|
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding SaveProgramCommand}"
|
Command="{Binding InsertSavePositionCommand}"
|
||||||
Content="保存"
|
Content="添加位置"
|
||||||
Style="{StaticResource TreeToolbarButton}" />
|
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding LoadProgramCommand}"
|
Command="{Binding InsertInspectionModuleCommand}"
|
||||||
Content="加载"
|
Content="检测模块"
|
||||||
Style="{StaticResource TreeToolbarButton}" />
|
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding ExportCsvCommand}"
|
Command="{Binding InsertInspectionMarkerCommand}"
|
||||||
Content="导出"
|
Content="检测标记"
|
||||||
Style="{StaticResource TreeToolbarButton}" />
|
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertPauseDialogCommand}"
|
||||||
|
Content="消息弹窗"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertWaitDelayCommand}"
|
||||||
|
Content="插入等待"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertCompleteProgramCommand}"
|
||||||
|
Content="完成"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
|
<WrapPanel Margin="0,4,0,0">
|
||||||
|
<WrapPanel.Resources>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Visibility" Value="Collapsed" />
|
||||||
|
</Style>
|
||||||
|
</WrapPanel.Resources>
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertReferencePointCommand}"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||||
|
ToolTip="参考点">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image Source="/Assets/Icons/reference.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||||
|
<TextBlock Text="参考点" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertSavePositionCommand}"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||||
|
ToolTip="添加位置">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image Source="/Assets/Icons/add-pos.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||||
|
<TextBlock Text="添加位置" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertInspectionModuleCommand}"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||||
|
ToolTip="检测模块">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image Source="/Assets/Icons/Module.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||||
|
<TextBlock Text="检测模块" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertInspectionMarkerCommand}"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||||
|
ToolTip="检测标记">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image Source="/Assets/Icons/mark.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||||
|
<TextBlock Text="检测标记" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertPauseDialogCommand}"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||||
|
ToolTip="消息弹窗">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image Source="/Assets/Icons/message.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||||
|
<TextBlock Text="消息弹窗" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertWaitDelayCommand}"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||||
|
ToolTip="插入等待">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image Source="/Assets/Icons/wait.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||||
|
<TextBlock Text="插入等待" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Command="{Binding InsertCompleteProgramCommand}"
|
||||||
|
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||||
|
ToolTip="完成">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image Source="/Assets/Icons/finish.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||||
|
<TextBlock Text="完成" />
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</WrapPanel>
|
||||||
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<TreeView
|
<TreeView
|
||||||
@@ -180,6 +280,7 @@
|
|||||||
FontFamily="{StaticResource UiFont}"
|
FontFamily="{StaticResource UiFont}"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
FontWeight="SemiBold"
|
FontWeight="SemiBold"
|
||||||
|
x:Name="ProgramRootNameText"
|
||||||
Text="{Binding DisplayName}"
|
Text="{Binding DisplayName}"
|
||||||
TextTrimming="CharacterEllipsis" />
|
TextTrimming="CharacterEllipsis" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -188,6 +289,7 @@
|
|||||||
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="True">
|
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="True">
|
||||||
<Setter TargetName="ProgramRootCard" Property="Background" Value="#E7F1FB" />
|
<Setter TargetName="ProgramRootCard" Property="Background" Value="#E7F1FB" />
|
||||||
<Setter TargetName="ProgramRootCard" Property="BorderBrush" Value="#9FC6E8" />
|
<Setter TargetName="ProgramRootCard" Property="BorderBrush" Value="#9FC6E8" />
|
||||||
|
<Setter TargetName="ProgramRootNameText" Property="Foreground" Value="#111111" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
</DataTemplate.Triggers>
|
</DataTemplate.Triggers>
|
||||||
</HierarchicalDataTemplate>
|
</HierarchicalDataTemplate>
|
||||||
@@ -205,6 +307,10 @@
|
|||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="4">
|
CornerRadius="4">
|
||||||
<Grid x:Name="NodeRoot" MinHeight="23">
|
<Grid x:Name="NodeRoot" MinHeight="23">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="15" />
|
<ColumnDefinition Width="15" />
|
||||||
<ColumnDefinition Width="20" />
|
<ColumnDefinition Width="20" />
|
||||||
@@ -212,7 +318,7 @@
|
|||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Grid Grid.Column="0">
|
<Grid Grid.RowSpan="2" Grid.Column="0">
|
||||||
<Border
|
<Border
|
||||||
x:Name="ChildStem"
|
x:Name="ChildStem"
|
||||||
Width="1"
|
Width="1"
|
||||||
@@ -230,6 +336,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Border
|
<Border
|
||||||
|
Grid.RowSpan="2"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="16"
|
Width="16"
|
||||||
Height="16"
|
Height="16"
|
||||||
@@ -246,6 +353,7 @@
|
|||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="NodeNameText"
|
x:Name="NodeNameText"
|
||||||
|
Grid.Row="0"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="3,0,0,0"
|
Margin="3,0,0,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -257,6 +365,7 @@
|
|||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
x:Name="NodeActions"
|
x:Name="NodeActions"
|
||||||
|
Grid.Row="0"
|
||||||
Grid.Column="3"
|
Grid.Column="3"
|
||||||
Margin="0,0,2,0"
|
Margin="0,0,2,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -275,6 +384,31 @@
|
|||||||
FontSize="10"
|
FontSize="10"
|
||||||
ToolTip="删除" />
|
ToolTip="删除" />
|
||||||
</StackPanel>
|
</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>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
<DataTemplate.Triggers>
|
<DataTemplate.Triggers>
|
||||||
@@ -287,22 +421,30 @@
|
|||||||
<Setter TargetName="NodeActions" Property="Visibility" Value="Visible" />
|
<Setter TargetName="NodeActions" Property="Visibility" Value="Visible" />
|
||||||
<Setter TargetName="NodeCard" Property="Background" Value="#DCEEFF" />
|
<Setter TargetName="NodeCard" Property="Background" Value="#DCEEFF" />
|
||||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#71A9DB" />
|
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#71A9DB" />
|
||||||
|
<Setter TargetName="NodeNameText" Property="Foreground" Value="#1F2D3D" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Running">
|
<DataTrigger Binding="{Binding ExecutionState}" Value="Running">
|
||||||
<Setter TargetName="NodeCard" Property="Background" Value="#FF1E6FD9" />
|
<Setter TargetName="NodeCard" Property="Background" Value="#FFD54F" />
|
||||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF1E6FD9" />
|
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#C89B00" />
|
||||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
|
<Setter TargetName="NodeNameText" Property="Foreground" Value="#1F1F1F" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Succeeded">
|
<DataTrigger Binding="{Binding ExecutionState}" Value="Succeeded">
|
||||||
<Setter TargetName="NodeCard" Property="Background" Value="#FF2E7D32" />
|
<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" />
|
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Failed">
|
<DataTrigger Binding="{Binding ExecutionState}" Value="Failed">
|
||||||
<Setter TargetName="NodeCard" Property="Background" Value="#FFC62828" />
|
<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" />
|
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
|
||||||
</DataTrigger>
|
</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>
|
</DataTemplate.Triggers>
|
||||||
</HierarchicalDataTemplate>
|
</HierarchicalDataTemplate>
|
||||||
</TreeView.Resources>
|
</TreeView.Resources>
|
||||||
@@ -469,6 +611,20 @@
|
|||||||
<StackPanel Margin="10,8,10,6">
|
<StackPanel Margin="10,8,10,6">
|
||||||
<TextBlock Style="{StaticResource LabelStyle}" Text="延时 (ms)" />
|
<TextBlock Style="{StaticResource LabelStyle}" Text="延时 (ms)" />
|
||||||
<TextBox Style="{StaticResource EditorBox}" Text="{Binding SelectedNode.DelayMilliseconds, UpdateSourceTrigger=LostFocus}" />
|
<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>
|
</StackPanel>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|||||||
@@ -19,14 +19,6 @@ namespace XplorePlane.Views.Cnc
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class CncPageView : UserControl
|
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 CncInspectionModulePipelineViewModel _inspectionModulePipelineViewModel;
|
||||||
private readonly Dictionary<TextBox, Label> _textDisplayLabels = new();
|
private readonly Dictionary<TextBox, Label> _textDisplayLabels = new();
|
||||||
private readonly Dictionary<CheckBox, Label> _checkDisplayLabels = new();
|
private readonly Dictionary<CheckBox, Label> _checkDisplayLabels = new();
|
||||||
@@ -186,24 +178,9 @@ namespace XplorePlane.Views.Cnc
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.IsSelected)
|
card.ClearValue(Border.BackgroundProperty);
|
||||||
{
|
card.ClearValue(Border.BorderBrushProperty);
|
||||||
card.Background = SelectedNodeBackground;
|
ClearNodeTextForeground(card);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,14 +295,11 @@ namespace XplorePlane.Views.Cnc
|
|||||||
panel.Children.Insert(index + 1, companionControl);
|
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))
|
foreach (var textBlock in FindVisualDescendants<TextBlock>(card))
|
||||||
{
|
{
|
||||||
if (textBlock.Visibility == Visibility.Visible)
|
textBlock.ClearValue(TextBlock.ForegroundProperty);
|
||||||
{
|
|
||||||
textBlock.Foreground = foreground;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
<Setter Property="FontWeight" Value="Bold" />
|
<Setter Property="FontWeight" Value="Bold" />
|
||||||
<Setter Property="Foreground" Value="#1c1c1b" />
|
<Setter Property="Foreground" Value="#1c1c1b" />
|
||||||
<Setter Property="BorderBrush" Value="#cdcbcb" />
|
<Setter Property="BorderBrush" Value="#cdcbcb" />
|
||||||
<Setter Property="Padding" Value="4,6,4,4" />
|
<Setter Property="Padding" Value="3,4,3,3" />
|
||||||
</Style>
|
</Style>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
<!-- 右侧:算子选择 + 参数配置 -->
|
<!-- 右侧:算子选择 + 参数配置 -->
|
||||||
<Border Grid.Column="3" Style="{StaticResource PanelBorderStyle}">
|
<Border Grid.Column="3" Style="{StaticResource PanelBorderStyle}">
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||||
<StackPanel Margin="10,8,10,10">
|
<StackPanel Margin="8,6,8,8">
|
||||||
|
|
||||||
<GroupBox Margin="0,0,0,8" Header="选择算子">
|
<GroupBox Margin="0,0,0,8" Header="选择算子">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
@@ -204,4 +204,4 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -100,6 +100,7 @@
|
|||||||
<TabControl x:Name="ToolboxListBox"
|
<TabControl x:Name="ToolboxListBox"
|
||||||
Margin="8"
|
Margin="8"
|
||||||
ItemContainerStyle="{StaticResource OperatorToolboxTabItemStyle}"
|
ItemContainerStyle="{StaticResource OperatorToolboxTabItemStyle}"
|
||||||
|
SelectedItem="{Binding SelectedGroup, Mode=TwoWay}"
|
||||||
ItemsSource="{Binding FilteredGroups}">
|
ItemsSource="{Binding FilteredGroups}">
|
||||||
<TabControl.ItemTemplate>
|
<TabControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:views="clr-namespace:XplorePlane.Views"
|
xmlns:views="clr-namespace:XplorePlane.Views"
|
||||||
Title="算子工具箱"
|
Title="算子工具箱"
|
||||||
Width="500" Height="560"
|
Width="460" Height="540"
|
||||||
|
MinWidth="420" MinHeight="500"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStyle="None"
|
WindowStyle="None"
|
||||||
|
|||||||
@@ -80,25 +80,25 @@
|
|||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding NewPipelineCommand}"
|
Command="{Binding NewPipelineCommand}"
|
||||||
Content="新建"
|
Content="新建配方"
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="新建流水线" />
|
ToolTip="新建配方" />
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding SavePipelineCommand}"
|
Command="{Binding SavePipelineCommand}"
|
||||||
Content="保存"
|
Content="保存配方"
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="保存当前流水线" />
|
ToolTip="保存当前配方" />
|
||||||
<Button
|
<Button
|
||||||
Width="64"
|
Width="64"
|
||||||
Command="{Binding SaveAsPipelineCommand}"
|
Command="{Binding SaveAsPipelineCommand}"
|
||||||
Content="另存为"
|
Content="另存为"
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="另存当前流水线" />
|
ToolTip="另存当前配方" />
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding LoadPipelineCommand}"
|
Command="{Binding LoadPipelineCommand}"
|
||||||
Content="加载"
|
Content="加载配方"
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="加载流水线" />
|
ToolTip="加载配方" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -132,10 +132,20 @@
|
|||||||
x:Name="NodeContainer"
|
x:Name="NodeContainer"
|
||||||
Margin="2"
|
Margin="2"
|
||||||
Padding="2"
|
Padding="2"
|
||||||
|
Tag="{Binding DataContext, ElementName=RootControl}"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
BorderBrush="Transparent"
|
BorderBrush="Transparent"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="3">
|
CornerRadius="3">
|
||||||
|
<Border.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="执行到此处"
|
||||||
|
Command="{Binding PlacementTarget.Tag.ExecuteToNodeCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||||||
|
CommandParameter="{Binding}" />
|
||||||
|
<MenuItem Header="执行全部"
|
||||||
|
Command="{Binding PlacementTarget.Tag.ClearExecutionRangeCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</Border.ContextMenu>
|
||||||
<Grid x:Name="NodeRoot" MinHeight="48">
|
<Grid x:Name="NodeRoot" MinHeight="48">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="44" />
|
<ColumnDefinition Width="44" />
|
||||||
@@ -193,7 +203,7 @@
|
|||||||
FontFamily="{StaticResource UiFont}"
|
FontFamily="{StaticResource UiFont}"
|
||||||
FontSize="10"
|
FontSize="10"
|
||||||
Foreground="#6E6E6E"
|
Foreground="#6E6E6E"
|
||||||
Text="已启用" />
|
Text="{Binding NodeStateText}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
@@ -209,13 +219,37 @@
|
|||||||
<Setter TargetName="IconBorder" Property="BorderBrush" Value="{StaticResource DisabledNodeLine}" />
|
<Setter TargetName="IconBorder" Property="BorderBrush" Value="{StaticResource DisabledNodeLine}" />
|
||||||
<Setter TargetName="IconBorder" Property="Background" Value="#ECECEC" />
|
<Setter TargetName="IconBorder" Property="Background" Value="#ECECEC" />
|
||||||
<Setter TargetName="NodeTitle" Property="Foreground" Value="{StaticResource DisabledNodeText}" />
|
<Setter TargetName="NodeTitle" Property="Foreground" Value="{StaticResource DisabledNodeText}" />
|
||||||
<Setter TargetName="NodeState" Property="Text" Value="已停用" />
|
|
||||||
<Setter TargetName="NodeState" Property="Foreground" Value="#9A6767" />
|
<Setter TargetName="NodeState" Property="Foreground" Value="#9A6767" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding IsExecutionEndNode}" Value="True">
|
||||||
|
<Setter TargetName="NodeContainer" Property="Background" Value="#E8F6EA" />
|
||||||
|
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#4F9D69" />
|
||||||
|
<Setter TargetName="IconBorder" Property="Background" Value="#E8F6EA" />
|
||||||
|
<Setter TargetName="IconBorder" Property="BorderBrush" Value="#4F9D69" />
|
||||||
|
<Setter TargetName="NodeState" Property="Foreground" Value="#2E7D32" />
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding IsSkippedByExecutionRange}" Value="True">
|
||||||
|
<Setter TargetName="NodeContainer" Property="Background" Value="#FAFAFA" />
|
||||||
|
<Setter TargetName="NodeContainer" Property="Opacity" Value="0.72" />
|
||||||
|
<Setter TargetName="TopLine" Property="Stroke" Value="#D0D0D0" />
|
||||||
|
<Setter TargetName="BottomLine" Property="Stroke" Value="#D0D0D0" />
|
||||||
|
<Setter TargetName="IconBorder" Property="BorderBrush" Value="#C8C8C8" />
|
||||||
|
<Setter TargetName="IconBorder" Property="Background" Value="#F4F4F4" />
|
||||||
|
<Setter TargetName="NodeTitle" Property="Foreground" Value="#909090" />
|
||||||
|
<Setter TargetName="NodeState" Property="Foreground" Value="#A0A0A0" />
|
||||||
|
</DataTrigger>
|
||||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True">
|
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True">
|
||||||
<Setter TargetName="NodeContainer" Property="Background" Value="#D9ECFF" />
|
<Setter TargetName="NodeContainer" Property="Background" Value="#D9ECFF" />
|
||||||
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#5B9BD5" />
|
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#5B9BD5" />
|
||||||
</DataTrigger>
|
</DataTrigger>
|
||||||
|
<MultiDataTrigger>
|
||||||
|
<MultiDataTrigger.Conditions>
|
||||||
|
<Condition Binding="{Binding IsExecutionEndNode}" Value="True" />
|
||||||
|
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True" />
|
||||||
|
</MultiDataTrigger.Conditions>
|
||||||
|
<Setter TargetName="NodeContainer" Property="Background" Value="#DFF0E3" />
|
||||||
|
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#3F8A58" />
|
||||||
|
</MultiDataTrigger>
|
||||||
<MultiDataTrigger>
|
<MultiDataTrigger>
|
||||||
<MultiDataTrigger.Conditions>
|
<MultiDataTrigger.Conditions>
|
||||||
<Condition Binding="{Binding IsEnabled}" Value="False" />
|
<Condition Binding="{Binding IsEnabled}" Value="False" />
|
||||||
@@ -225,6 +259,15 @@
|
|||||||
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#7E9AB6" />
|
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#7E9AB6" />
|
||||||
<Setter TargetName="NodeContainer" Property="Opacity" Value="1" />
|
<Setter TargetName="NodeContainer" Property="Opacity" Value="1" />
|
||||||
</MultiDataTrigger>
|
</MultiDataTrigger>
|
||||||
|
<MultiDataTrigger>
|
||||||
|
<MultiDataTrigger.Conditions>
|
||||||
|
<Condition Binding="{Binding IsSkippedByExecutionRange}" Value="True" />
|
||||||
|
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True" />
|
||||||
|
</MultiDataTrigger.Conditions>
|
||||||
|
<Setter TargetName="NodeContainer" Property="Background" Value="#EEF1F4" />
|
||||||
|
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#AAB4BF" />
|
||||||
|
<Setter TargetName="NodeContainer" Property="Opacity" Value="0.9" />
|
||||||
|
</MultiDataTrigger>
|
||||||
</DataTemplate.Triggers>
|
</DataTemplate.Triggers>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
@@ -274,9 +317,53 @@
|
|||||||
Text="{Binding DisplayName}"
|
Text="{Binding DisplayName}"
|
||||||
TextTrimming="CharacterEllipsis" />
|
TextTrimming="CharacterEllipsis" />
|
||||||
|
|
||||||
|
<Grid Grid.Column="1">
|
||||||
|
<Grid.Style>
|
||||||
|
<Style TargetType="Grid">
|
||||||
|
<Setter Property="Visibility" Value="Collapsed" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsSliderInput}" Value="True">
|
||||||
|
<Setter Property="Visibility" Value="Visible" />
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Grid.Style>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="58" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Slider
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsSnapToTickEnabled="{Binding IsIntegerSlider}"
|
||||||
|
LargeChange="{Binding SliderTickFrequency}"
|
||||||
|
Maximum="{Binding SliderMaximum}"
|
||||||
|
Minimum="{Binding SliderMinimum}"
|
||||||
|
SmallChange="{Binding SliderTickFrequency}"
|
||||||
|
TickFrequency="{Binding SliderTickFrequency}"
|
||||||
|
Value="{Binding SliderValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
|
||||||
|
<Border
|
||||||
|
Grid.Column="1"
|
||||||
|
Padding="2,2"
|
||||||
|
Background="#F8F8F8"
|
||||||
|
BorderBrush="#CDCBCB"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="2">
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontFamily="{StaticResource UiFont}"
|
||||||
|
FontSize="11"
|
||||||
|
Text="{Binding DisplayValueText}" />
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Padding="4,2"
|
Padding="2,2"
|
||||||
BorderBrush="#CDCBCB"
|
BorderBrush="#CDCBCB"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
FontFamily="{StaticResource UiFont}"
|
FontFamily="{StaticResource UiFont}"
|
||||||
|
|||||||
@@ -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.PreviewMouseRightButtonDown -= OnPreviewMouseRightButtonDown;
|
||||||
|
PipelineListBox.PreviewMouseRightButtonDown += OnPreviewMouseRightButtonDown;
|
||||||
PipelineListBox.MouseDoubleClick -= OnMouseDoubleClick;
|
PipelineListBox.MouseDoubleClick -= OnMouseDoubleClick;
|
||||||
PipelineListBox.MouseDoubleClick += OnMouseDoubleClick;
|
PipelineListBox.MouseDoubleClick += OnMouseDoubleClick;
|
||||||
PipelineListBox.PreviewKeyDown -= OnPreviewKeyDown;
|
PipelineListBox.PreviewKeyDown -= OnPreviewKeyDown;
|
||||||
@@ -133,6 +135,16 @@ namespace XplorePlane.Views
|
|||||||
ResetDragState();
|
ResetDragState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
var clickedNode = FindNodeFromOriginalSource(e.OriginalSource);
|
||||||
|
if (clickedNode == null || IsInteractiveChild(e.OriginalSource))
|
||||||
|
return;
|
||||||
|
|
||||||
|
PipelineListBox.SelectedItem = clickedNode;
|
||||||
|
PipelineListBox.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
var vm = DataContext as IPipelineEditorHostViewModel;
|
var vm = DataContext as IPipelineEditorHostViewModel;
|
||||||
|
|||||||
@@ -279,7 +279,7 @@
|
|||||||
Size="Large"
|
Size="Large"
|
||||||
SmallImage="/Assets/Icons/cnc.png"
|
SmallImage="/Assets/Icons/cnc.png"
|
||||||
Text="CNC 编辑" />
|
Text="CNC 编辑" />
|
||||||
|
<!--
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<telerik:RadRibbonButton
|
<telerik:RadRibbonButton
|
||||||
telerik:ScreenTip.Title="参考点"
|
telerik:ScreenTip.Title="参考点"
|
||||||
@@ -313,12 +313,7 @@
|
|||||||
Command="{Binding InsertInspectionModuleCommand}"
|
Command="{Binding InsertInspectionModuleCommand}"
|
||||||
SmallImage="/Assets/Icons/Module.png"
|
SmallImage="/Assets/Icons/Module.png"
|
||||||
Text="检测模块" />
|
Text="检测模块" />
|
||||||
<telerik:RadRibbonButton
|
|
||||||
telerik:ScreenTip.Title="全部保存"
|
|
||||||
Size="Medium"
|
|
||||||
Command="{Binding SaveCncProgramCommand}"
|
|
||||||
SmallImage="/Assets/Icons/saveall.png"
|
|
||||||
Text="全部保存" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<telerik:RadRibbonButton
|
<telerik:RadRibbonButton
|
||||||
@@ -334,6 +329,7 @@
|
|||||||
SmallImage="/Assets/Icons/wait.png"
|
SmallImage="/Assets/Icons/wait.png"
|
||||||
Text="插入等待" />
|
Text="插入等待" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
-->
|
||||||
|
|
||||||
<telerik:RadRibbonButton
|
<telerik:RadRibbonButton
|
||||||
telerik:ScreenTip.Description="打开矩阵编排窗口,配置多工件阵列检测方案"
|
telerik:ScreenTip.Description="打开矩阵编排窗口,配置多工件阵列检测方案"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<Page Remove="MainWindow.xaml" />
|
<Page Remove="MainWindow.xaml" />
|
||||||
<Page Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml" />
|
<Page Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml" />
|
||||||
<Page Remove="Views\ImageProcessing\ImageProcessingWindow.xaml" />
|
<Page Remove="Views\ImageProcessing\ImageProcessingWindow.xaml" />
|
||||||
|
<Page Remove="Views\ImageProcessing\PipelineEditorWindow.xaml" />
|
||||||
<Page Remove="Views\Main\MainWindowB.xaml" />
|
<Page Remove="Views\Main\MainWindowB.xaml" />
|
||||||
<Page Remove="Views\Main\NavigationPanelView.xaml" />
|
<Page Remove="Views\Main\NavigationPanelView.xaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -149,8 +150,13 @@
|
|||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
<Link>Libs\Hardware\zh-TW\%(Filename)%(Extension)</Link>
|
<Link>Libs\Hardware\zh-TW\%(Filename)%(Extension)</Link>
|
||||||
</None>
|
</None>
|
||||||
|
<Compile Remove="Views\Hardware\**" />
|
||||||
|
<EmbeddedResource Remove="Views\Hardware\**" />
|
||||||
|
<None Remove="Views\Hardware\**" />
|
||||||
|
<Page Remove="Views\Hardware\**" />
|
||||||
<Compile Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml.cs" />
|
<Compile Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml.cs" />
|
||||||
<Compile Remove="Views\ImageProcessing\ImageProcessingWindow.xaml.cs" />
|
<Compile Remove="Views\ImageProcessing\ImageProcessingWindow.xaml.cs" />
|
||||||
|
<Compile Remove="Views\ImageProcessing\PipelineEditorWindow.xaml.cs" />
|
||||||
<Compile Remove="Views\Main\MainWindowB.xaml.cs" />
|
<Compile Remove="Views\Main\MainWindowB.xaml.cs" />
|
||||||
<Compile Remove="Views\Main\NavigationPanelView.xaml.cs" />
|
<Compile Remove="Views\Main\NavigationPanelView.xaml.cs" />
|
||||||
<Content Include="XplorerPlane.ico">
|
<Content Include="XplorerPlane.ico">
|
||||||
|
|||||||
Reference in New Issue
Block a user