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

12 KiB
Raw Blame History

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(可选) 射线源控制

执行流程

入口方法

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 — 运动控制

private Task<MotionResult> MoveToPositionAsync(MotionState target, CancellationToken ct)
  • MotionState 中的微米(μm)坐标转换为毫米(mm)后发送给运动控制服务
  • 依次移动:StageX → StageY → SourceZ → DetectorZ → DetectorSwing → StageRotation → FixtureRotation
  • 任一轴移动失败立即返回错误
  • 运动服务不可用时返回成功(优雅降级)

WaitForAxesSettledAsync — 等待轴到位

private async Task<bool> WaitForAxesSettledAsync(CancellationToken ct)
  • 每 50ms 轮询所有线性轴和旋转轴的状态
  • 所有轴 Status == Idle 时返回 true
  • 超时 30 秒返回 false(不中断执行,继续后续步骤)

TryGetSourceImage — 图像获取

private BitmapSource TryGetSourceImage()

图像源优先级:

  1. MainViewportService.LatestManualImage(用户手动加载的图像)
  2. MainViewportService.CurrentDisplayImage(当前显示的实时图像)
  3. AppStateService.LatestDetectorFrame(原始探测器帧,Gray16 → BitmapSource

ExecuteInspectionNodeAsync — 检测模块执行

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

private IEnumerable<PipelineNodeViewModel> BuildPipelineNodeViewModels(PipelineModel pipeline)
  • PipelineModel.Nodes(数据模型)转换为 PipelineNodeViewModel(执行模型)
  • 加载每个算子的参数定义和保存值
  • 处理 JSON 参数值的类型转换(JsonElementint/double/bool/string

ExecuteWaitDelayWithProgressAsync — 延时等待

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

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()