突出检测模块和检测标记的父子节点关系
This commit is contained in:
@@ -33,6 +33,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
private CncNodeViewModel _selectedNode;
|
||||
private bool _isModified;
|
||||
private string _programName;
|
||||
private Guid? _preferredSelectedNodeId;
|
||||
|
||||
public CncEditorViewModel(
|
||||
ICncProgramService cncProgramService,
|
||||
@@ -136,8 +137,9 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
try
|
||||
{
|
||||
var node = _cncProgramService.CreateNode(nodeType);
|
||||
int afterIndex = SelectedNode?.Index ?? _currentProgram.Nodes.Count - 1;
|
||||
int afterIndex = ResolveInsertAfterIndex(nodeType);
|
||||
_currentProgram = _cncProgramService.InsertNode(_currentProgram, afterIndex, node);
|
||||
_preferredSelectedNodeId = node.Id;
|
||||
|
||||
OnProgramEdited();
|
||||
_logger.Info("Inserted node: Type={NodeType}", nodeType);
|
||||
@@ -155,7 +157,19 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
|
||||
try
|
||||
{
|
||||
_currentProgram = _cncProgramService.RemoveNode(_currentProgram, SelectedNode.Index);
|
||||
if (SelectedNode.IsSavePosition)
|
||||
{
|
||||
var nodes = _currentProgram.Nodes.ToList();
|
||||
int startIndex = SelectedNode.Index;
|
||||
int endIndex = GetSavePositionBlockEndIndex(startIndex);
|
||||
nodes.RemoveRange(startIndex, endIndex - startIndex + 1);
|
||||
_currentProgram = ReplaceProgramNodes(nodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentProgram = _cncProgramService.RemoveNode(_currentProgram, SelectedNode.Index);
|
||||
}
|
||||
|
||||
OnProgramEdited();
|
||||
_logger.Info("Deleted node at index: {Index}", SelectedNode.Index);
|
||||
}
|
||||
@@ -179,10 +193,18 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
|
||||
try
|
||||
{
|
||||
_currentProgram = _cncProgramService.MoveNode(_currentProgram, nodeVm.Index, nodeVm.Index - 1);
|
||||
if (IsSavePositionChild(nodeVm.NodeType))
|
||||
{
|
||||
MoveSavePositionChild(nodeVm, moveDown: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveRootBlock(nodeVm, moveDown: false);
|
||||
}
|
||||
|
||||
OnProgramEdited();
|
||||
}
|
||||
catch (ArgumentOutOfRangeException ex)
|
||||
catch (Exception ex) when (ex is ArgumentOutOfRangeException or InvalidOperationException)
|
||||
{
|
||||
_logger.Warn("Move node up failed: {Message}", ex.Message);
|
||||
}
|
||||
@@ -195,10 +217,18 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
|
||||
try
|
||||
{
|
||||
_currentProgram = _cncProgramService.MoveNode(_currentProgram, nodeVm.Index, nodeVm.Index + 1);
|
||||
if (IsSavePositionChild(nodeVm.NodeType))
|
||||
{
|
||||
MoveSavePositionChild(nodeVm, moveDown: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveRootBlock(nodeVm, moveDown: true);
|
||||
}
|
||||
|
||||
OnProgramEdited();
|
||||
}
|
||||
catch (ArgumentOutOfRangeException ex)
|
||||
catch (Exception ex) when (ex is ArgumentOutOfRangeException or InvalidOperationException)
|
||||
{
|
||||
_logger.Warn("Move node down failed: {Message}", ex.Message);
|
||||
}
|
||||
@@ -349,12 +379,12 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
|
||||
private void RefreshNodes()
|
||||
{
|
||||
var selectedId = SelectedNode?.Id;
|
||||
var selectedId = _preferredSelectedNodeId ?? SelectedNode?.Id;
|
||||
var expansionState = Nodes.ToDictionary(node => node.Id, node => node.IsExpanded);
|
||||
|
||||
var flatNodes = new List<CncNodeViewModel>();
|
||||
var rootNodes = new List<CncNodeViewModel>();
|
||||
CncNodeViewModel currentModule = null;
|
||||
CncNodeViewModel currentSavePosition = null;
|
||||
|
||||
if (_currentProgram?.Nodes != null)
|
||||
{
|
||||
@@ -367,21 +397,21 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
|
||||
flatNodes.Add(vm);
|
||||
|
||||
if (vm.IsInspectionModule)
|
||||
if (vm.IsSavePosition)
|
||||
{
|
||||
rootNodes.Add(vm);
|
||||
currentModule = vm;
|
||||
currentSavePosition = vm;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentModule != null && IsModuleChild(vm.NodeType))
|
||||
if (currentSavePosition != null && IsSavePositionChild(vm.NodeType))
|
||||
{
|
||||
currentModule.Children.Add(vm);
|
||||
currentSavePosition.Children.Add(vm);
|
||||
continue;
|
||||
}
|
||||
|
||||
rootNodes.Add(vm);
|
||||
currentModule = null;
|
||||
currentSavePosition = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,13 +421,199 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
SelectedNode = selectedId.HasValue
|
||||
? Nodes.FirstOrDefault(node => node.Id == selectedId.Value) ?? Nodes.LastOrDefault()
|
||||
: Nodes.LastOrDefault();
|
||||
|
||||
_preferredSelectedNodeId = null;
|
||||
}
|
||||
|
||||
private static bool IsModuleChild(CncNodeType type)
|
||||
private int ResolveInsertAfterIndex(CncNodeType nodeType)
|
||||
{
|
||||
if (_currentProgram == null || _currentProgram.Nodes.Count == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!IsSavePositionChild(nodeType))
|
||||
{
|
||||
return SelectedNode?.Index ?? _currentProgram.Nodes.Count - 1;
|
||||
}
|
||||
|
||||
int? savePositionIndex = FindOwningSavePositionIndex(SelectedNode?.Index);
|
||||
if (!savePositionIndex.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException("请先选择一个“保存位置”节点,再插入标记点或检测模块。");
|
||||
}
|
||||
|
||||
return GetSavePositionBlockEndIndex(savePositionIndex.Value);
|
||||
}
|
||||
|
||||
private void MoveSavePositionChild(CncNodeViewModel nodeVm, bool moveDown)
|
||||
{
|
||||
int? parentIndex = FindOwningSavePositionIndex(nodeVm.Index);
|
||||
if (!parentIndex.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException("当前子节点未归属于任何保存位置,无法移动。");
|
||||
}
|
||||
|
||||
int childStartIndex = parentIndex.Value + 1;
|
||||
int childEndIndex = GetSavePositionBlockEndIndex(parentIndex.Value);
|
||||
int targetIndex = moveDown ? nodeVm.Index + 1 : nodeVm.Index - 1;
|
||||
|
||||
if (targetIndex < childStartIndex || targetIndex > childEndIndex)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentProgram = _cncProgramService.MoveNode(_currentProgram, nodeVm.Index, targetIndex);
|
||||
_preferredSelectedNodeId = nodeVm.Id;
|
||||
}
|
||||
|
||||
private void MoveRootBlock(CncNodeViewModel nodeVm, bool moveDown)
|
||||
{
|
||||
var blocks = BuildRootBlocks(_currentProgram.Nodes);
|
||||
int blockIndex = blocks.FindIndex(block => block.Start == nodeVm.Index);
|
||||
if (blockIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!moveDown && blockIndex == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (moveDown && blockIndex >= blocks.Count - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentBlock = blocks[blockIndex];
|
||||
var targetBlock = blocks[moveDown ? blockIndex + 1 : blockIndex - 1];
|
||||
var nodes = _currentProgram.Nodes.ToList();
|
||||
var movingNodes = nodes.GetRange(currentBlock.Start, currentBlock.End - currentBlock.Start + 1);
|
||||
|
||||
nodes.RemoveRange(currentBlock.Start, movingNodes.Count);
|
||||
|
||||
int insertAt = moveDown
|
||||
? targetBlock.End - movingNodes.Count + 1
|
||||
: targetBlock.Start;
|
||||
|
||||
nodes.InsertRange(insertAt, movingNodes);
|
||||
|
||||
_currentProgram = ReplaceProgramNodes(nodes);
|
||||
_preferredSelectedNodeId = nodeVm.Id;
|
||||
}
|
||||
|
||||
private int? FindOwningSavePositionIndex(int? startIndex)
|
||||
{
|
||||
if (!startIndex.HasValue || _currentProgram == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = startIndex.Value;
|
||||
if (index < 0 || index >= _currentProgram.Nodes.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var selectedType = _currentProgram.Nodes[index].NodeType;
|
||||
if (_currentProgram.Nodes[index].NodeType == CncNodeType.SavePosition)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
if (!IsSavePositionChild(selectedType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = index - 1; i >= 0; i--)
|
||||
{
|
||||
var type = _currentProgram.Nodes[i].NodeType;
|
||||
if (type == CncNodeType.SavePosition)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
if (!IsSavePositionChild(type))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int GetSavePositionBlockEndIndex(int savePositionIndex)
|
||||
{
|
||||
if (_currentProgram == null)
|
||||
{
|
||||
return savePositionIndex;
|
||||
}
|
||||
|
||||
int endIndex = savePositionIndex;
|
||||
for (int i = savePositionIndex + 1; i < _currentProgram.Nodes.Count; i++)
|
||||
{
|
||||
if (!IsSavePositionChild(_currentProgram.Nodes[i].NodeType))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
endIndex = i;
|
||||
}
|
||||
|
||||
return endIndex;
|
||||
}
|
||||
|
||||
private static List<(int Start, int End)> BuildRootBlocks(IReadOnlyList<CncNode> nodes)
|
||||
{
|
||||
var blocks = new List<(int Start, int End)>();
|
||||
|
||||
for (int i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
if (IsSavePositionChild(nodes[i].NodeType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int end = i;
|
||||
if (nodes[i].NodeType == CncNodeType.SavePosition)
|
||||
{
|
||||
for (int j = i + 1; j < nodes.Count; j++)
|
||||
{
|
||||
if (!IsSavePositionChild(nodes[j].NodeType))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
end = j;
|
||||
}
|
||||
}
|
||||
|
||||
blocks.Add((i, end));
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
private CncProgram ReplaceProgramNodes(List<CncNode> nodes)
|
||||
{
|
||||
var renumberedNodes = nodes
|
||||
.Select((node, index) => node with { Index = index })
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
|
||||
return _currentProgram with
|
||||
{
|
||||
Nodes = renumberedNodes,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsSavePositionChild(CncNodeType type)
|
||||
{
|
||||
return type is CncNodeType.InspectionMarker
|
||||
or CncNodeType.PauseDialog
|
||||
or CncNodeType.WaitDelay;
|
||||
or CncNodeType.InspectionModule;
|
||||
}
|
||||
|
||||
private void PublishProgramChanged()
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Background="#F0F0F0" BorderBrush="#DDDDDD" BorderThickness="0,0,0,1">
|
||||
<TextBlock Margin="4,2" HorizontalAlignment="Left" VerticalAlignment="Center"
|
||||
FontWeight="SemiBold" Foreground="#333333" Text="图像" />
|
||||
FontWeight="SemiBold" Foreground="#333333" Text="图像处理" />
|
||||
</Border>
|
||||
<ContentControl Grid.Row="1" Content="{Binding ImagePanelContent}" />
|
||||
</Grid>
|
||||
|
||||
@@ -312,11 +312,11 @@
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<telerik:RadRibbonButton
|
||||
telerik:ScreenTip.Title="标记"
|
||||
telerik:ScreenTip.Title="检测标记"
|
||||
Size="Medium"
|
||||
Command="{Binding InsertInspectionMarkerCommand}"
|
||||
SmallImage="/Assets/Icons/mark.png"
|
||||
Text="标记" />
|
||||
Text="检测标记" />
|
||||
<telerik:RadRibbonButton
|
||||
telerik:ScreenTip.Title="模块"
|
||||
Size="Medium"
|
||||
|
||||
Reference in New Issue
Block a user