Files
XplorePlane/XplorePlane/Doc/CNC多检测结果归档.md
T
2026-04-23 07:14:10 +08:00

14 KiB
Raw Blame History

CNC 多检测结果归档与报告取数说明

1. 目标

为 CNC 执行结果提供一套适合报告模块直接取数的归档结构。

设计目标:

  • 以“一次工件检测实例”作为主归档单位
  • 同时保留到“检测节点级别”的明细
  • 支持保存:
    • CNC 程序名
    • 工件号 / 序列号
    • 检测节点信息
    • 节点使用的 Pipeline / 配方快照
    • 原图、节点输入图、节点最终结果图
    • 节点输出的多个数值结果
    • 节点判定和整次实例判定
  • 方便后续报告模块直接读取,不依赖运行时最新配方

当前实现采用:

  • SQLite 保存结构化索引数据
  • 文件系统保存图片资产和 manifest.json

2. 总体设计

一次检测实例会生成:

  1. 一组数据库记录
  2. 一组文件目录和图像文件
  3. 一份 manifest.json 快照文件

归档核心对象:

  • InspectionRunRecord
    • 一次完整检测实例
  • InspectionNodeResult
    • 一个 CNC 检测节点的结果
  • InspectionMetricResult
    • 节点输出的数值结果
  • InspectionAssetRecord
    • 图像或附件索引
  • PipelineExecutionSnapshot
    • 节点执行时使用的 Pipeline 快照

默认图片保留策略:

  • 整次实例原图
  • 每个节点的输入图
  • 每个节点的最终结果图

不默认保存每个算法步骤的中间图。


3. 文件存储结构

3.1 根目录

默认根目录:

%AppData%\XplorePlane\InspectionResults

每次检测实例按日期和 RunId 分层:

Results/{yyyy}/{MM}/{dd}/{RunId}/

3.2 示例目录

假设:

  • ProgramName = NewCncProgram
  • WorkpieceId = QFN_1
  • SerialNumber = SN-001
  • RunId = 7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a
  • 检测节点共 2 个

则文件结构大致为:

%AppData%\XplorePlane\InspectionResults\
└─ Results\
   └─ 2026\
      └─ 04\
         └─ 21\
            └─ 7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a\
               ├─ manifest.json
               ├─ run\
               │  └─ source.bmp
               └─ nodes\
                  ├─ 001_检测节点1\
                  │  ├─ input.bmp
                  │  └─ result_overlay.bmp
                  └─ 002_检测节点2\
                     └─ result_overlay.bmp

3.3 文件说明

  • run/source.bmp
    • 本次工件检测实例的原始输入图
  • nodes/001_检测节点1/input.bmp
    • 节点 1 输入图
  • nodes/001_检测节点1/result_overlay.bmp
    • 节点 1 最终结果图
  • nodes/002_检测节点2/result_overlay.bmp
    • 节点 2 最终结果图
  • manifest.json
    • 本次检测完整快照,便于离线查看、调试和导出

4. 数据库表设计

当前实现包含 5 张主表。

4.1 inspection_runs

用途:保存一次完整检测实例的主记录。

字段 类型 说明
run_id TEXT 主键,检测实例 IDGUID
program_name TEXT CNC 程序名
workpiece_id TEXT 工件号
serial_number TEXT 序列号
started_at TEXT 开始时间,ISO 8601
completed_at TEXT 结束时间,ISO 8601,可空
overall_pass INTEGER 整体判定,0/1
source_image_path TEXT 原图相对路径
result_root_path TEXT 本次结果包根目录相对路径
node_count INTEGER 节点数量

样例数据:

run_id program_name workpiece_id serial_number started_at completed_at overall_pass source_image_path result_root_path node_count
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a NewCncProgram QFN_1 SN-001 2026-04-21T10:00:00.0000000Z 2026-04-21T10:00:03.2000000Z 0 Results/2026/04/21/7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a/run/source.bmp Results/2026/04/21/7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 2

4.2 inspection_node_results

用途:保存一次检测实例中的节点级结果。

字段 类型 说明
run_id TEXT 所属检测实例 ID
node_id TEXT 节点 IDGUID
node_index INTEGER 节点序号
node_name TEXT 节点名称
pipeline_id TEXT Pipeline IDGUID
pipeline_name TEXT Pipeline 名称
pipeline_version_hash TEXT Pipeline 快照 hash
node_pass INTEGER 节点判定,0/1
source_image_path TEXT 节点输入图相对路径
result_image_path TEXT 节点结果图相对路径
status TEXT 节点状态:Succeeded / Failed / PartialSuccess / AssetMissing
duration_ms INTEGER 节点耗时

样例数据:

run_id node_id node_index node_name pipeline_name pipeline_version_hash node_pass source_image_path result_image_path status duration_ms
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 11111111-1111-1111-1111-111111111111 1 检测节点1 Recipe-A A1B2C3... 1 Results/2026/04/21/.../nodes/001_检测节点1/input.bmp Results/2026/04/21/.../nodes/001_检测节点1/result_overlay.bmp Succeeded 135
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 22222222-2222-2222-2222-222222222222 2 检测节点2 Recipe-B D4E5F6... 0 `` Results/2026/04/21/.../nodes/002_检测节点2/result_overlay.bmp Failed 240

4.3 inspection_metric_results

用途:保存节点输出的结构化数值结果。

字段 类型 说明
run_id TEXT 所属检测实例 ID
node_id TEXT 所属节点 ID
metric_key TEXT 指标 key
metric_name TEXT 指标名称
metric_value REAL 指标值
unit TEXT 单位
lower_limit REAL 下限,可空
upper_limit REAL 上限,可空
is_pass INTEGER 单指标判定,0/1
display_order INTEGER 展示顺序

样例数据:

run_id node_id metric_key metric_name metric_value unit lower_limit upper_limit is_pass display_order
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 11111111-1111-1111-1111-111111111111 bridge.rate Bridge Rate 0.12 % 0.2 1 1
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 11111111-1111-1111-1111-111111111111 void.area Void Area 5.6 px 8 1 2
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 22222222-2222-2222-2222-222222222222 solder.height Solder Height 1.7 mm 1.8 0 1

4.4 inspection_assets

用途:保存文件资产索引。

字段 类型 说明
run_id TEXT 所属检测实例 ID
node_id TEXT 所属节点 ID,可空
asset_type TEXT 资产类型
relative_path TEXT 相对路径
file_format TEXT 文件格式
width INTEGER 宽度
height INTEGER 高度

约定的 asset_type

  • RunSourceImage
  • NodeInputImage
  • NodeResultImage

样例数据:

run_id node_id asset_type relative_path file_format width height
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a RunSourceImage Results/2026/04/21/.../run/source.bmp bmp 0 0
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 11111111-1111-1111-1111-111111111111 NodeInputImage Results/2026/04/21/.../nodes/001_检测节点1/input.bmp bmp 0 0
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 11111111-1111-1111-1111-111111111111 NodeResultImage Results/2026/04/21/.../nodes/001_检测节点1/result_overlay.bmp bmp 0 0
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 22222222-2222-2222-2222-222222222222 NodeResultImage Results/2026/04/21/.../nodes/002_检测节点2/result_overlay.bmp bmp 0 0

4.5 pipeline_execution_snapshots

用途:保存节点执行时的 Pipeline 快照,避免后续配方修改影响历史报告。

字段 类型 说明
run_id TEXT 所属检测实例 ID
node_id TEXT 所属节点 ID
pipeline_name TEXT Pipeline 名称
pipeline_definition_json TEXT Pipeline 序列化 JSON
pipeline_hash TEXT Pipeline JSON 的 SHA-256

样例数据:

run_id node_id pipeline_name pipeline_hash
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 11111111-1111-1111-1111-111111111111 Recipe-A A1B2C3...
7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a 22222222-2222-2222-2222-222222222222 Recipe-B D4E5F6...

5. manifest.json 示例

每次 CompleteRunAsync 后,会在结果包目录下生成 manifest.json

示例:

{
  "Run": {
    "RunId": "7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a",
    "ProgramName": "NewCncProgram",
    "WorkpieceId": "QFN_1",
    "SerialNumber": "SN-001",
    "StartedAt": "2026-04-21T10:00:00.0000000Z",
    "CompletedAt": "2026-04-21T10:00:03.2000000Z",
    "OverallPass": false,
    "SourceImagePath": "Results/2026/04/21/7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a/run/source.bmp",
    "ResultRootPath": "Results/2026/04/21/7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a",
    "NodeCount": 2
  },
  "Nodes": [
    {
      "RunId": "7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a",
      "NodeId": "11111111-1111-1111-1111-111111111111",
      "NodeIndex": 1,
      "NodeName": "检测节点1",
      "PipelineName": "Recipe-A",
      "PipelineVersionHash": "A1B2C3...",
      "NodePass": true,
      "SourceImagePath": "Results/2026/04/21/7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a/nodes/001_检测节点1/input.bmp",
      "ResultImagePath": "Results/2026/04/21/7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a/nodes/001_检测节点1/result_overlay.bmp",
      "Status": 0,
      "DurationMs": 135
    },
    {
      "RunId": "7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a",
      "NodeId": "22222222-2222-2222-2222-222222222222",
      "NodeIndex": 2,
      "NodeName": "检测节点2",
      "PipelineName": "Recipe-B",
      "PipelineVersionHash": "D4E5F6...",
      "NodePass": false,
      "SourceImagePath": "",
      "ResultImagePath": "Results/2026/04/21/7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a/nodes/002_检测节点2/result_overlay.bmp",
      "Status": 1,
      "DurationMs": 240
    }
  ],
  "Metrics": [
    {
      "RunId": "7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a",
      "NodeId": "11111111-1111-1111-1111-111111111111",
      "MetricKey": "bridge.rate",
      "MetricName": "Bridge Rate",
      "MetricValue": 0.12,
      "Unit": "%",
      "LowerLimit": null,
      "UpperLimit": 0.2,
      "IsPass": true,
      "DisplayOrder": 1
    },
    {
      "RunId": "7d7d8d7d-1234-4567-89ab-9f0e1d2c3b4a",
      "NodeId": "22222222-2222-2222-2222-222222222222",
      "MetricKey": "solder.height",
      "MetricName": "Solder Height",
      "MetricValue": 1.7,
      "Unit": "mm",
      "LowerLimit": 1.8,
      "UpperLimit": null,
      "IsPass": false,
      "DisplayOrder": 1
    }
  ]
}

说明:

  • manifest.json 是文件侧的完整快照
  • SQLite 是主索引
  • 报告模块可以优先查库,再按需读取文件

6. 报告模块如何取数

6.1 首页列表 / 历史查询

建议通过:

  • inspection_runs
  • 必要时联查 inspection_node_results

可支持筛选条件:

  • 时间范围
  • 程序名
  • 工件号
  • 序列号
  • Pipeline 名称

6.2 单份报告生成

建议按 RunId 调用:

  • GetRunDetailAsync(runId)

得到:

  • Run
  • Nodes
  • Metrics
  • Assets
  • PipelineSnapshots

即可直接组装报告:

  • 报告首页
    • 工件号、序列号、程序名、开始/结束时间、整体判定
  • 节点章节
    • 节点名称
    • 配方名
    • 结果图
    • 关键指标值
    • 节点判定
  • 追溯信息
    • Pipeline 快照 hash
    • 原图路径
    • 结果图路径

6.3 离线导出

若后续需要将单个检测实例导出给第三方或留档,可直接复制:

  • 整个 Results/{yyyy}/{MM}/{dd}/{RunId} 目录

这样会同时带走:

  • manifest.json
  • 原图
  • 节点图

7. 当前实现接口

当前服务接口:

  • BeginRunAsync(...)
    • 创建实例记录和结果目录
  • AppendNodeResultAsync(...)
    • 写入节点结果、指标、图片索引、Pipeline 快照
  • CompleteRunAsync(...)
    • 回填结束时间、整体判定,并写出 manifest.json
  • QueryRunsAsync(...)
    • 查询检测实例列表
  • GetRunDetailAsync(...)
    • 查询单个实例的完整报告数据

当前 DI 注册:

  • IInspectionResultStore -> InspectionResultStore

8. 设计约束与说明

8.1 为什么不直接扩展 MeasurementDataService

因为原有 MeasurementRecord 只适合:

  • 单值统计
  • 简单的 pass/fail 汇总

它不适合承载:

  • 多节点
  • 多图像
  • 多指标
  • Pipeline 快照
  • 报告导出

所以当前设计中:

  • MeasurementDataService 继续保留给旧统计用途
  • InspectionResultStore 作为报告归档主通道

8.2 为什么图片不直接进 SQLite

因为图片数据量大,直接入库会带来:

  • 数据库膨胀
  • 查询性能下降
  • 迁移和备份成本变高

因此使用:

  • SQLite 存索引
  • 文件系统存图片

这是更适合报告场景的折中方案。

8.3 为什么要保存 Pipeline 快照

因为报告要可追溯。

如果只保存 PipelineName,后续配方被修改后,历史报告就无法复原当时的真实算法链。
因此需要保存:

  • pipeline_definition_json
  • pipeline_hash

9. 后续可扩展方向

后续可继续扩展:

  1. 增加 manifest.json 中的设备上下文

    • 运动位置
    • 射线源状态
    • 探测器状态
  2. 增加中间图可选保留策略

    • 默认关闭
    • 调试模式开启
  3. 增加结果导出包

    • ZIP 打包
    • 单份报告 PDF
  4. 增加报告模板字段映射

    • MetricKey 映射到报告模板占位符
  5. 增加数据清理策略

    • 保留天数
    • 自动清理旧图片
    • 保留数据库索引或同时删除

10. 结论

当前这套归档设计的核心特点是:

  • 以“检测实例”为主组织数据
  • 以“检测节点”为明细展开
  • 以“结构化指标 + 图片文件 + Pipeline 快照”支撑报告
  • 通过 SQLite 和文件系统混合存储兼顾查询效率和图片落盘

对于后续报告模块,这套结构已经可以直接支持:

  • 历史列表查询
  • 单次检测报告生成
  • 结果图展示
  • 节点级指标展示
  • 历史结果可追溯