270 lines
12 KiB
Markdown
270 lines
12 KiB
Markdown
# 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()
|
||
```
|