Files
XplorePlane/XplorePlane/Doc/CNC执行机制说明.md
T
zhengxuan.zhang cdd0db95ff 调试CNC执行
2026-05-26 13:18:29 +08:00

270 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CNC 执行机制说明
## 概述
`CncExecutionService` 是 CNC 程序的核心执行引擎,负责按节点顺序执行整个检测程序。它协调运动控制、图像采集、流水线处理和结果归档等子系统,实现自动化多位置检测流程。
---
## 类定义
| 类 | 文件 | 职责 |
|----|------|------|
| `CncExecutionService` | `Services/Cnc/CncExecutionService.cs` | CNC 程序执行引擎 |
| `ICncExecutionService` | `Services/Cnc/ICncExecutionService.cs` | 执行服务接口 |
| `CncEditorViewModel` | `ViewModels/Cnc/CncEditorViewModel.cs` | 调用执行服务的 ViewModel |
---
## 依赖注入
`CncExecutionService` 通过构造函数注入以下服务:
| 依赖 | 接口 | 用途 |
|------|------|------|
| 检测结果存储 | `IInspectionResultStore` | 归档检测运行记录和节点结果 |
| 日志 | `ILoggerService` | 结构化日志记录 |
| 主视口 | `IMainViewportService` | 获取实时图像、推送结果图像 |
| 应用状态 | `IAppStateService` | 获取探测器帧数据 |
| 流水线执行 | `IPipelineExecutionService` | 执行图像处理流水线 |
| 图像处理 | `IImageProcessingService` | 获取算子定义和参数 |
| 事件聚合器 | `IEventAggregator` | 订阅探测器断连事件 |
| 图像持久化 | `IImagePersistenceService` | 保存采集图像到磁盘 |
| 运动控制 | `IMotionControlService`(可选) | 控制各轴移动到目标位置 |
| 运动系统 | `IMotionSystem`(可选) | 查询轴状态(是否到位) |
| 射线源 | `IRaySourceService`(可选) | 射线源控制 |
---
## 执行流程
### 入口方法
```csharp
Task ExecuteAsync(CncProgram program, IProgress<CncNodeExecutionProgress> progress, CancellationToken cancellationToken)
```
### 执行阶段
```
┌─────────────────────────────────────────────────────────────┐
│ 1. 初始化阶段 │
│ - 创建 LinkedCancellationTokenSource(支持探测器断连取消) │
│ - 获取初始源图像 │
│ - 调用 _store.BeginRunAsync() 创建检测运行记录 │
├─────────────────────────────────────────────────────────────┤
│ 2. 多位置执行循环(SavePositionNode
│ 对每个 SavePositionNode 按顺序执行: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Step 0: 运动到目标位置 (MoveToPositionAsync) │ │
│ │ Step 1: 图像采集(探测器实时帧 或 手动图像文件) │ │
│ │ Step 2: 图像保存(如果 SaveImage=true │ │
│ │ Step 3: 流水线执行(如果下一节点是 InspectionModule │ │
│ │ Step 4: 报告节点执行状态 │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 3. 批次结果汇总 │
│ - 构建 BatchCaptureResult │
│ - 调用 _imagePersistenceService.WriteSummaryAsync() │
├─────────────────────────────────────────────────────────────┤
│ 4. 非位置节点执行 │
│ 按 Index 顺序处理剩余节点: │
│ - ReferencePointNode: 记录参考点参数 │
│ - SaveNodeNode / SaveNodeWithImageNode: 记录设备状态 │
│ - WaitDelayNode: 延时等待(带进度报告) │
│ - PauseDialogNode: 弹出暂停对话框 │
│ - InspectionModuleNode: 执行图像处理流水线 │
│ - CompleteProgramNode: 标记程序完成,退出循环 │
├─────────────────────────────────────────────────────────────┤
│ 5. 完成阶段 │
│ - 调用 _store.CompleteRunAsync() 标记运行结束 │
│ - 清理 CancellationTokenSource │
│ - 通知 MainViewportService CNC 执行结束 │
└─────────────────────────────────────────────────────────────┘
```
---
## 核心方法说明
### `ExecuteAsync` — 主执行入口
| 参数 | 类型 | 说明 |
|------|------|------|
| `program` | `CncProgram` | 待执行的 CNC 程序(含所有节点) |
| `progress` | `IProgress<CncNodeExecutionProgress>` | 进度回调(通知 UI 更新节点状态) |
| `cancellationToken` | `CancellationToken` | 外部取消令牌(用户点击停止) |
**关键行为:**
- 使用 `LinkedCancellationTokenSource` 将外部取消和探测器断连事件合并
- 每个节点执行前检查取消状态
- 失败节点不中断整体执行(容错设计),仅标记为 Failed 并继续
---
### `MoveToPositionAsync` — 运动控制
```csharp
private Task<MotionResult> MoveToPositionAsync(MotionState target, CancellationToken ct)
```
-`MotionState` 中的微米(μm)坐标转换为毫米(mm)后发送给运动控制服务
- 依次移动:StageX → StageY → SourceZ → DetectorZ → DetectorSwing → StageRotation → FixtureRotation
- 任一轴移动失败立即返回错误
- 运动服务不可用时返回成功(优雅降级)
---
### `WaitForAxesSettledAsync` — 等待轴到位
```csharp
private async Task<bool> WaitForAxesSettledAsync(CancellationToken ct)
```
- 每 50ms 轮询所有线性轴和旋转轴的状态
- 所有轴 `Status == Idle` 时返回 `true`
- 超时 30 秒返回 `false`(不中断执行,继续后续步骤)
---
### `TryGetSourceImage` — 图像获取
```csharp
private BitmapSource TryGetSourceImage()
```
**图像源优先级:**
1. `MainViewportService.LatestManualImage`(用户手动加载的图像)
2. `MainViewportService.CurrentDisplayImage`(当前显示的实时图像)
3. `AppStateService.LatestDetectorFrame`(原始探测器帧,Gray16 → BitmapSource
---
### `ExecuteInspectionNodeAsync` — 检测模块执行
```csharp
private async Task<BitmapSource> ExecuteInspectionNodeAsync(
Guid runId, InspectionModuleNode inspectionNode, BitmapSource sourceImage, CancellationToken ct)
```
**执行步骤:**
1. 创建 `InspectionNodeResult` 记录
2. 序列化 Pipeline 定义为 JSON 快照
3. 保存输入图像为资产
4. 调用 `BuildPipelineNodeViewModels()` 构建流水线 ViewModel
5. 调用 `_pipelineExecutionService.ExecutePipelineAsync()` 执行流水线
6. 推送结果图像到主视口
7. 推送检测叠加层数据(轮廓、标注)
8. 合成背景图 + 叠加层,保存结果截图
9. 调用 `_store.AppendNodeResultAsync()` 归档节点结果
---
### `BuildPipelineNodeViewModels` — 构建流水线 ViewModel
```csharp
private IEnumerable<PipelineNodeViewModel> BuildPipelineNodeViewModels(PipelineModel pipeline)
```
-`PipelineModel.Nodes`(数据模型)转换为 `PipelineNodeViewModel`(执行模型)
- 加载每个算子的参数定义和保存值
- 处理 JSON 参数值的类型转换(`JsonElement``int/double/bool/string`
---
### `ExecuteWaitDelayWithProgressAsync` — 延时等待
```csharp
private static async Task ExecuteWaitDelayWithProgressAsync(
WaitDelayNode waitNode, IProgress<CncNodeExecutionProgress> progress, CancellationToken ct)
```
- 每 50ms 报告一次进度百分比
- 支持取消(抛出 `OperationCanceledException`
---
## 取消机制
| 取消源 | 触发方式 | 处理 |
|--------|----------|------|
| 用户点击停止 | `CancellationToken` 从 ViewModel 传入 | 各节点执行前检查 |
| 探测器断连 | `DetectorDisconnectedEvent``_executionCts.Cancel()` | 通过 LinkedCTS 传播 |
| 运动失败 | `MoveToPositionAsync` 返回失败 | 标记节点 Failed,继续下一个 |
---
## 进度报告
通过 `IProgress<CncNodeExecutionProgress>` 回调通知 UI
```csharp
public record CncNodeExecutionProgress(
Guid NodeId,
NodeExecutionState State,
double? ProgressPercent = null,
BitmapSource ResultImage = null,
int? PositionIndex = null,
int? TotalPositions = null);
```
ViewModel 收到回调后更新 `CncNodeViewModel.ExecutionState`,触发树形节点的颜色变化。
---
## 结果归档
| 方法 | 时机 | 存储内容 |
|------|------|----------|
| `_store.BeginRunAsync()` | 执行开始 | 运行记录 + 源图像 |
| `_store.AppendNodeResultAsync()` | 每个检测模块执行后 | 节点结果 + Pipeline 快照 + 输入/输出图像 |
| `_store.CompleteRunAsync()` | 执行结束 | 标记运行完成/失败 |
| `_imagePersistenceService.WriteSummaryAsync()` | 多位置循环结束 | 批次结果 JSON 摘要 |
---
## 容错设计
- **图像采集失败**:标记节点 Failed,跳过该位置,继续下一个
- **图像保存失败**:标记节点 Failed,但仍继续执行流水线
- **流水线执行异常**:捕获异常,标记 Failed,不中断整体执行
- **运动服务不可用**:返回成功(优雅降级,适用于仿真模式)
- **探测器断连**:通过事件取消整个执行
---
## 调用关系图
```
CncEditorViewModel
├── RunCncCommand → ExecuteRunAsync()
│ │
│ └── CncExecutionService.ExecuteAsync()
│ │
│ ├── IInspectionResultStore.BeginRunAsync()
│ │
│ ├── [循环] SavePositionNode
│ │ ├── MoveToPositionAsync() → IMotionControlService
│ │ ├── WaitForAxesSettledAsync() → IMotionSystem
│ │ ├── TryGetSourceImage() → IMainViewportService / IAppStateService
│ │ ├── IImagePersistenceService.SaveImageAsync()
│ │ └── ExecuteInspectionNodeAsync()
│ │ ├── BuildPipelineNodeViewModels()
│ │ ├── IPipelineExecutionService.ExecutePipelineAsync()
│ │ ├── IMainViewportService.SetCncResultImage()
│ │ ├── IMainViewportService.PushDetectionOverlay()
│ │ └── IInspectionResultStore.AppendNodeResultAsync()
│ │
│ ├── [循环] 非位置节点
│ │ ├── WaitDelayNode → ExecuteWaitDelayWithProgressAsync()
│ │ ├── PauseDialogNode → MessageBox
│ │ ├── InspectionModuleNode → ExecuteInspectionNodeAsync()
│ │ └── CompleteProgramNode → 退出
│ │
│ ├── IImagePersistenceService.WriteSummaryAsync()
│ └── IInspectionResultStore.CompleteRunAsync()
└── StopCncCommand → CancellationTokenSource.Cancel()
```