Tool / ToolFlow 基建设计
www 工作副本(2026-10-21 Asia/Shanghai) 双轨:正文迁自 mvdocs toolflow(v1.9)。语义仍以该拍板为准;本页不改 T9 / Diagnostic 码表。 实现边界:
include/vyntek/toolflow/**为 NON-STABLE / STUB。RunSession::Run可在工具仅报NotImplemented时仍为Succeeded。GetOutput空串 不是 KeepLatest。观察面在output_inspector.hpp。RequestCancel不停采集。详见文末对拍。 关联:cpp-headers · host-integration · output-inspection · session-lifecycle · csharp-binding · examples
Tool / ToolFlow 基建设计
本轮产品模型(优先)。目标设计草案,尚未实施。总体边界见设计概述。
醒目声明:心智上借用 VisionPro 的 Tool /(原 CogToolBlock)容器思路,自有命名为 Tool / ToolFlow。除该心智模型外,整体设计与 VisionPro 完全不一样——架构、服务边界、实现、持久化、与 UI 关系等均为自研重建,不以复刻 VisionPro 为设计目标,不承诺任何兼容。基建以 UI 无关的 C++ 底层服务实现;上层可接 C# 编辑/显示、其它 UI,以及 纯编排运行(无 UI)。
明确不接入:现有
MPF_ToolBlock/InsToolBlock合入方案。 旧「DAG 图像流转」专题已废止并移除文件;勿再作编排对照或历史权威阅读。编排专题目径统一为 Tool / ToolFlow(本文)。
关联(成像与显示基建,非编排执行模型):图像模型 · 内存 · 显示 · 输出观察( T10 集合事件) · C++ 头文件 · C# 绑定( T11) · 宿主集成( R7) · 测试契约表( R4 · §10.1 全文; Arden · R13 T13 索引对齐) · 术语终检( R8;撞号含 R13)
1.1 目标
| 目标 | 说明 |
|---|---|
| Tool / ToolFlow 心智 + 自研重建 | 借用「原子工具 + 可复用容器 + 终端 + 顺序 Run + 数据链接」的心智;产品类型名为 Tool / ToolFlow。除该心智外与 VisionPro 完全不同;不承诺 API/二进制兼容。 |
| 基建重建 | 以 C++ 实现核心对象模型与执行引擎;不在既有 MPF/.NET 合入实现上嫁接;编排专题目径为 ToolFlow,不以已废止的 DAG 文为准。 |
| 多宿主共用同一核心 | 同一 ToolFlow 定义可被:C# 编辑器、其它 UI、headless runner、产线服务进程加载并 Run。 |
| 工具流内流水线为主 | 用户心智是「打开工具流 → 有序列表 + 属性/终端连线」,不是「先画一张全局抽象 DAG」。 |
| 可复用组件 | ToolFlow 对外只暴露 Inputs/Outputs 终端,可嵌套、可持久化为配方/组件再加载。 |
1.2 非目标
| 非目标 | 说明 |
|---|---|
| 接入 / 合入现有 MPF_ToolBlock | 不写合入、迁移或兼容层方案;历史实现仅作概念对照,不作为本轮依赖。 |
| 沿用已废止的 DAG 文为编排模型 | 旧 DAG 文已废止;不把「全局拓扑调度 + 只读边」当作编排权威;成像只读语义见 image-model / memory / toolflow 图像句柄段落。 |
| VisionPro / Cognex API 或设计复刻 | 不宣称 .vpp、COM/.NET 类型或 Designer Task 同构;不以对齐/复刻 VisionPro 为设计目标。 |
| 通用工作流引擎 | 不做 BPMN、条件分支一等公民、并行子图调度器;条件逻辑后续靠禁用 + 脚本钩子。 |
| 许可证绕过或逆向 | 仅公开文档级心智借用;不涉及厂商二进制。 |
| 本轮定死进程形态细节 | 进程内库 vs 本地 RPC 服务可择一倾向(见 §7),MVP 先保证可嵌入的 C++ API。 |
| 本轮实现完整脚本语言 | Script 钩子列入后续;MVP 可不实现。 |
1.3 设计立场(一句话)
C++ 服务层拥有 Tool / ToolFlow / Terminal / Link / Run 的真相;UI 只是编辑与可视化绑定;执行主模型是工具流(或组)内自上而下顺序 Run + 链接赋值,连续采集用多次 Run,不用环。除 Tool / 容器心智外,与 VisionPro 完全不同。
2. 术语
心智上借用 VisionPro 的 Tool / CogToolBlock 容器思路;名称、架构与实现均为自有。下表中英对照;不假称与 Cognex API 同名同义兼容,也不以复刻 VisionPro 为目标。
| 本方案用语 | 英文 | 心智对照(仅概念) | 职责(一句话) |
|---|---|---|---|
| 工具 | Tool | Vision Tool | 可独立 Run 的原子视觉单元;暴露属性(输入图、ROI、结果等)。 |
| 算子(可选) | Operator | Operator | 工具内部算法实现;对外经 Tool 属性访问。MVP 可不单独暴露。 |
| 工具组(可选内部) | ToolGroup | CogToolGroup | 工具的有序集合;Run = 自上而下依次 Run 子项;组内可建链接。可作内部基类;产品对外主推 Tool + ToolFlow。 |
| 工具流 | ToolFlow | (原 CogToolBlock 容器心智) | 可复用容器;额外暴露 Inputs / Outputs 终端;产品主容器类型名(勿再称 ToolBlock / 工具块)。 |
| 终端 / 针脚 | Terminal / Pin | Terminal / Pin | 工具流边界上命名、类型化的输入或输出端口;流外只连终端。 |
| 链接 / 绑定 | Link / Binding | Data link / DataBindings | 源属性或终端 → 兼容目标属性或终端;执行时传值/赋引用。 |
| 作业 / 运行会话 | Job / RunSession | Job / 一次 Run | 一次检测或一次完整 Run 的边界(含顶层工具流、输入注入、状态与结果)。 |
| 禁用 | Disabled | DisabledTools | 本次 Run 跳过的工具;不参与执行,链接策略见执行模型。 |
| 嵌套 | Nested | Nested ToolGroup / ToolFlow | 工具流/组内再含工具流/组;内层只通过自身终端或公开属性对外可见。 |
| 脚本钩子(后续) | Script Hook | Simple/Advanced Script | 子工具序列前后的扩展逻辑;能读写终端与下级工具属性。MVP 不做。 |
| Tool / ToolFlow 基建(产品总称) | — | — | 本编排核心:对象模型 + 顺序执行 + 链接 + 持久化 + 多宿主 API。类型名 ToolFlow 专指容器,勿与总称混用。 |
Tool vs ToolFlow
- Tool:做一件视觉事;接口是自身属性。
- ToolFlow:把多件视觉事收成可复用黑盒;对外只有终端契约;
Run= 跑完内部有序工具(及日后脚本)。
3.1 核心类型与职责
| 类型(示意) | 职责 |
|---|---|
ITool | 统一可 Run 单元:身份、名称、启用标志、属性袋、Run(ctx)、上次运行状态。 |
Tool(原子) | 具体算法工具;持有参数与运行时结果属性;可选内部 Operator。 |
ToolGroup | 有序 tools[]、组内 links[]、禁用集合、失败是否中止;Run 按列表序。可选内部基类/无终端有序组;对外主推 ToolFlow。 |
ToolFlow | ToolGroup + inputs / outputs 终端集合;对外契约与持久化复用单元;产品主容器。 |
Terminal | 名称、方向(In/Out)、 TypeDesc、当前值、稳定 id(便于链接与序列化)。 |
Link | 源端点 → 目标端点;端点为「工具属性路径」或「工具流终端」。 |
PropertyBag / Value | 类型化值容器(图像句柄、标量、几何、字符串、枚举等)。 |
RunContext | 单次 Run 的取消令牌、日志/诊断槽、可选追踪 id;不跨 Run 隐式共享可变业务状态。 |
RunSession / Job | 顶层宿主:持有根 ToolFlow(或 ToolGroup)、注入 Inputs、触发 Run、收集 Outputs 与状态。 |
ToolRegistry | 按类型 id 创建工具实例(工厂);与 UI 工具箱、反序列化共用。 |
示意关系(非 UML 定稿):
RunSession
└── root ToolFlow
├── Terminals: Inputs[], Outputs[]
├── tools[]: Tool | ToolFlow | ToolGroup (有序)
├── links[]: Link
└── disabled: set of tool ids
3.2 所有权与生命周期原则
自洽原则(旧 DAG 文已废止,不以之为权威):
- 工具流拥有子工具:
ToolFlow/ToolGroup拥有tools[]的生命周期;移除或销毁工具流时销毁子树(除非显式「抽出」到另一所有者,MVP 不做共享所有权)。 - 链接不拥有数据:
Link只描述赋值关系;值落在属性或终端上。 - 图像值有明确句柄语义(与 image-model.md / memory-management.md 用语对齐):属性/终端持有成像基建句柄;交给下游(经链接赋值写入目标输入)前须 稳定发布为
Access=ReadOnly(典型路径:Complete/CompleteAndOpenRead/ShareImage等,见成像专题)。已交下游的输入句柄不得再改写;工具不得在Run结束后继续写已交给下游的稳定结果。链接赋值只传句柄/值,不是 DAG 边调度。细节实现复用成像包契约,编排模型仍以本文顺序 Run 为准。交接路径与可测断言见 §3.4(T6)。 - 一次 Run 的结果可见性:
Run返回后,Outputs 终端与工具输出属性可读,直到下次Run覆盖或工具流被重置;UI「最近结果」是观察层,不是第二次执行的隐式输入源。 - 线程:MVP 规定 同一
RunSession同时只允许一次Run;并发第二次调用 拒绝(明确错误)。排队留到后阶段。跨会话并行由宿主多开 Session。
3.3 ToolFlow:对外终端与对内列表
| 侧面 | 内容 |
|---|---|
| 对外 | 仅 Inputs / Outputs 终端集合;工具流作为 ITool 被父级 Run 时,父级只链接这些终端(或工具流的选定公开属性,若有)。 |
| 对内 | 有序工具列表;工具属性 ↔ 工具属性、工具属性 ↔ 本工具流终端 的链接。 |
| 编辑心智 | 打开工具流 = 编辑内部流水线与 I/O 页;关闭后外层只见黑盒针脚。 |
| 嵌套 | 子项可以是另一 ToolFlow;外层链接内层终端,不可直接绑内层私有工具属性(封装边界)。 |
3.4 PixelImageHandle 交接对照表(T6 · P0)
Alive 拍板(T6):工具流内图像交接以本表 + §3.2 + image-model.md / memory-management.md 为准。 三条硬规则:(1)交给下游前须 稳定发布为
Access=ReadOnly;(2)已交下游的输入句柄不得再改写;(3)链接赋值只传句柄/值,不是 DAG 边调度(执行仍是 §4 列表序 Run)。 执行器检查发布契约,不为工具代申请像素权限或代 Complete;生产者负责在输出可被链接拉走之前完成稳定发布。
下列路径对齐成像公开表面(Share / Complete / CompleteAndOpenRead / ShareImage / Clone 等)。「下游」指经 Link 赋值写入目标输入属性/终端 后、即将被后续工具 Run 消费的句柄。
勾选约定(勿误勾):下表「契约勾选」列以 test-contracts.md 为权威(列标题即
→ test-contracts TC-H*),本表不独立验收。该文件 line 3 与四份薄摘明确规定「smoke 与 Controls.Tests 不等于本表通过」「外形 ≠ 验收」:验收另需供应商 SDK 实机与真实渲染/GPU/context 重建等条件。tests/orchestration_contracts.cpp已覆盖 TC-H1…H10 的单测断言,但不构成勾选依据,故本表保持全[ ]。单测覆盖情况见 test-contracts.md 文末《单测覆盖度记录》。
| # | 交接路径 | 典型用法(工具流内) | 结果能力 | 可测断言(须能单测/headless) | 契约勾选(→ test-contracts TC-H*) |
|---|---|---|---|---|---|
| H1 | WritableView::Complete | 工具写完自有输出后提交;再 OpenRead→ShareImage(或等价)得到可交接句柄 | 写权限结束;Generation 推进;内容有效后可读 | Complete 后同一 writer 上再 Complete/GetPixelRow → 失败(Disposed/InvalidOperation);未 Complete 就 Dispose → 内容 Invalid,不得作为下游输入 | [ ] H1 |
| H2 | WritableView::CompleteAndOpenRead | 提交并在同一原子边界取得读视图,再 ShareImage 写入输出属性 | 原子:提交与读授予之间不能插入另一写者;返回 ReadOnlyView(及后续 ReadOnly 句柄) | 并发/串行探测:CompleteAndOpenRead 完成前无第二写者获授;返回 view 的 version() 与提交代次一致;由该 view ShareImage() 得到 access()==ReadOnly | [ ] H2 |
| H3 | ReadOnlyView::ShareImage | 已有稳定读权限时发布独立 ReadOnly 句柄供链接/观察/显示 | Access=ReadOnly;同 BufferId/内容代次;不复制像素;不消费原 view | ShareImage 句柄 OpenWrite → NotSupported(或等价);原 view Dispose 后,已 ShareImage 的句柄在仍持有读引用期间仍可读;字节未因 ShareImage 而复制(同 BufferId) | [ ] H3 |
| H4 | PixelImageHandle::Share(源已是 ReadOnly) | 多下游/观察者各持一份 ReadOnly 句柄;链接可赋 Share 结果 | 仍为 ReadOnly;共用稳定内容;各句柄独立关闭 | Share 后两句柄皆 access()==ReadOnly;任一关闭不影响另一的 OpenRead;对 Share 结果 OpenWrite 立即拒绝 | [ ] H4 |
| H5 | PixelImageHandle::Share(源为 ReadWrite 根) | 仅保活分配 / 流程外协作;不能当作「已稳定发布」交给工具流下游输入 | Share 不固定内容;能力仍反映可写根(非稳定快照) | ReadWrite 根上 Share 的结果不得通过「仅改 Access 返回值」冒充 ReadOnly;若未再经 Complete/ShareImage 稳定发布,执行器/校验拒绝对下游输入的不稳定交接(或 Run 失败可断言) | [ ] H5 |
| H6 | 链接赋值写入下游输入 | §4.2:目标工具即将 Run 前,从源属性/终端 拉值写入目标输入 | 目标输入持有句柄值;不是调度边 | 赋值后目标输入 access()==ReadOnly;目标 OpenWrite → 立即拒绝;列表序仍自上而下,无「边就绪即跑」;打断链接不改变 tools[] 序 | [ ] H6 |
| H7 | 已交下游不得改写 | 生产者 Run 已结束且输出经链接进入下游输入后 | 下游所见内容在其 Run 期间稳定 | 生产者在交下游后对同一已发布存储再 OpenWrite/改像素 → 被稳定读引用挡住或失败;下游 Run 中两次 OpenRead 所见 Generation/逻辑像素一致;工具不得在 Run 返回后继续写已交给下游的稳定结果 | [ ] H7 |
| H8 | Clone 后可写 | 输入为 ReadOnly,算法需改像素:显式 Clone → 新 ReadWrite → OpenWrite → Complete(…) → 再 ReadOnly 发布 | 新 BufferId;Generation 从 1;已初始化可写;格式相同(非格式转换) | Clone 结果 access() 允许 OpenWrite;buffer_id() ≠ 源;源 ReadOnly 仍不可写;Clone 失败不留下半份可交接输出 | [ ] H8 |
| H9 | ROI GetRegion(ReadOnly 源) | 下游只消费子区;仍须保持稳定只读 | ROI 句柄 ReadOnly;与根共用内容稳定性 | ROI OpenWrite 拒绝;对 ROI 的写入企图不得把根退化为可写;ROI 与根 Share 语义一致(独立关闭) | [ ] H9 |
| H10 | 失败/中止写入 | 工具失败:丢弃未 Complete 的输出,或勿把 Invalid 句柄链给下游 | 无 Complete → Invalid;不可稳定发布 | 失败路径下下游拉值不得得到「假装成功」的 ReadOnly 输出;abort_on_tool_failure=true 时后续工具不 Run(与 §4.3 / §10.1 执行条一致) | [ ] H10 |
与成像文档的对齐要点(勿另造语义)
- 稳定发布路径:
Complete/CompleteAndOpenRead+ShareImage,或工厂/Copy直接得到的 ReadOnly 句柄;见 image-model「ShareImage / Clone」段与 memory「写入提交」。 - 工具流输入统一按 ReadOnly 消费;公共表面可有
OpenWrite,输入以能力上限立即拒绝。 - 链接 ≠ DAG:本表只约束句柄能力与发布时机;调度权威仍是 §4.1 顺序 Run。
4.1 主模型:自上而下顺序 Run
本方案权威调度(自研定稿;非 VisionPro 复刻):
运行工具组/工具流时,按列表自上而下依次调用每个(未禁用)子工具的
Run。
这是本方案的权威调度模型,不是「由全局依赖边推导就绪即跑」的 DAG 引擎。
伪代码:
ToolGroup::Run(ctx):
for tool in tools: // 列表序
if tool in disabled: continue
apply_links_into(tool) // 见 §4.2
status = tool.Run(ctx) // 若 tool 是嵌套工具流,递归同样模型
if failed(status) and abort_on_tool_failure:
record_and_abort
return
apply_links_to_flow_outputs() // 工具流级 Outputs
4.2 链接何时推 / 拉
外部公开材料未钉死实现细节;本方案自研定稿如下(可落地):
| 时机 | 行为 |
|---|---|
| 子工具即将 Run 之前 | 拉(pull):将该工具作为目标的所有链接,从源端点取值写入目标属性/终端。 |
| 子工具 Run 成功之后 | 源端点已更新;下游尚未 Run,其输入在各自「即将 Run 前」再拉。 |
| 整工具流 Run 结束前 | 将指向本工具流 Outputs 的链接再拉一次(或等价地在最后一次源 Ready 后推),保证对外终端完整。 |
失败中止时:已跑工具的输出可保留供诊断;未跑工具不更新;工具流 Outputs 标记不完整/失败状态。
推模型等价实现:若实现选择「源 Run 后立即推到所有目标」,须保证与「目标 Run 前拉」对同一次顺序表结果一致,并处理禁用导致的目标跳过。
4.3 失败、禁用、嵌套、连续采集
| 话题 | 规则 |
|---|---|
| 失败中止 | 工具流/组级标志 abort_on_tool_failure(默认 true)。失败则停止后续子工具,向上返回失败状态。 |
| 禁用 | 禁用工具不 Run;以其为目标的链接本轮不写入;以其为源的链接在下游拉值时得到「源未更新」——下游应失败、跳过或使用终端上一次值,由工具流级策略枚举(MVP:默认 失败/无效绑定,与「严格链接」一致)。 |
| 嵌套 Run | 子 ToolFlow 的 Run 完整执行其内部顺序表;对外表现为一个 ITool::Run。 |
| 连续采集 | 多次独立 Run(或多次 RunSession);图/工具流内禁止用环表达相机流。采集工具每次 Run 取一帧(或按参数取),由宿主循环调用。 |
| 条件逻辑 | MVP 无分支节点;用禁用集合或(后续)脚本钩子。 |
4.4 与「流程图 UI」的关系
- 编辑器核心可以是 树/列表 + 属性连线 + I/O 终端页(对应工具流编辑心智)。
- 允许另做画布视图,但执行真相仍是有序列表 + 链接,画布不得引入与列表序冲突的隐式并行,除非未来单独立项并改本文。
5. 链接与类型系统
Alive 拍板(T3):TypeDesc / Value / PropertyBag 以本节 MVP 子集为准;属性路径不做表达式/脚本;图像跨格式禁止;几何/ROI/Fixture 仅为工具语义字段,不是第二套图模型。命名与 cpp-headers.md / §7.4 一致:类型描述符一律
TypeDesc(勿另造TypeDescriptor/PortType)。
5.1 端口与属性路径
端点定位(编辑期与持久化共用同一语法;无表达式、脚本、函数调用路径):
| 端点形态 | 语法 | 示例 |
|---|---|---|
| 工具属性 | tool_id.prop[.sub...] | Align.OutputImage、Blob.Result.Count、Fix.Pose.X |
| 工具流输入终端 | flow.Inputs.name | flow.Inputs.InputImage |
| 工具流输出终端 | flow.Outputs.name | flow.Outputs.DefectCount |
规则:
| 规则 | 说明 |
|---|---|
| 段分隔 | 仅 .;首段为工具稳定 id(或显示名解析后的 id)或字面 flow。 |
| prop / sub | 标识符 [A-Za-z_][A-Za-z0-9_]*;嵌套仅表达 PropertyBag 子字段(如 Result.Count),不是脚本。 |
| 禁止 | 算术/比较表达式、条件、数组下标脚本、eval、跨工具相对路径(../)、隐式「当前工具」省略。 |
| 终端 | flow.Inputs / flow.Outputs 之后恰好一段名称;不再嵌套 .sub(终端值整体类型化;若需子字段,在工具属性侧拆)。 |
| 稳定 id | 持久化优先写工具/终端 稳定 id;显示名可变,加载后按 id 恢复链接。 |
终端与属性都带 TypeDesc(运行时与 .avtf 共用)。
5.2 TypeDesc MVP 子集
TypeDesc 描述「可链接 / 可持久化」的值类型;与 §7.4 示意 TypeDesc{Type::…} / vyntek::toolflow::TypeDesc 同名。
5.2.1 Type 枚举(MVP 闭集)
Type(C++ 示意) | 类别 | 持久化/JSON 字面(示意) | 对齐 |
|---|---|---|---|
Bool | 标量 | "Bool" | |
Int32 | 标量 | "Int32" | |
Int64 | 标量 | "Int64" | |
Float32 | 标量 | "Float32" | |
Float64 | 标量 | "Float64" | |
String | 标量 | "String" | |
ImageGray8 | 图像句柄 | "ImageGray8" | image-model Gray8 |
ImageRgb24 | 图像句柄 | "ImageRgb24" | Rgb24 |
ImageBgr24 | 图像句柄 | "ImageBgr24" | Bgr24 |
ImageDepth16 | 图像句柄 | "ImageDepth16" | Depth16 |
Point2d | 几何 | "Point2d" | §5.5 |
Line2d | 几何 | "Line2d" | |
Circle2d | 几何 | "Circle2d" | |
Rect2d | 几何 | "Rect2d" | 轴对齐矩形;ROI 常用 |
Affine2d | 几何 | "Affine2d" | 2D 仿射 |
Pose2d | 几何 | "Pose2d" | 定位位姿(可含角度) |
Region | 区域/ROI | "Region" | 工具语义,非拓扑节点 |
Fixture | 坐标系/夹具 | "Fixture" | 空间名 + 变换;非第二图模型 |
RunStatus | 枚举 | "RunStatus" | OK / NG / Failed / … |
Enum | 枚举(工具自定义) | "Enum" + enum_id | TypeDesc 可带 enum_id 字符串 |
MVP 不纳入:任意 blob、脚本闭包、多态 any、未登记像素格式、表达式类型。
示意:
// 与 §7.4 / cpp-headers 一致;非已发布 ABI
namespace vyntek::toolflow {
enum class Type : uint16_t {
Bool, Int32, Int64, Float32, Float64, String,
ImageGray8, ImageRgb24, ImageBgr24, ImageDepth16,
Point2d, Line2d, Circle2d, Rect2d, Affine2d, Pose2d,
Region, Fixture, RunStatus, Enum
};
struct TypeDesc {
Type type;
// Enum 时可选:std::string enum_id;
};
}
5.3 Value / PropertyBag
| 类型 | 职责 |
|---|---|
Value | 单个类型化运行时值:持有 TypeDesc + 载荷(标量 / 句柄 / 几何结构 / 枚举判别式)。链接赋值、终端注入、GetOutput 都经 Value(或等价强类型 API 糖)。 |
PropertyBag | 工具/终端上的命名属性:name → (TypeDesc, Value 槽, 读写标志, 是否配置态可持久化)。嵌套子字段用 子 PropertyBag 或固定结构字段(如 Result.Count),路径见 §5.1。 |
约束:
- 图像载荷为
vyntek::imaging公开句柄(如PixelImageHandle),不是像素拷贝;交给下游前须 ReadOnly 发布(§3.2)。 - 配置态属性可写入
.avtf;结果态属性默认不持久化(§6.4)。 PropertyBag::Get/Set按TypeDesc校验;类型不符 →DiagnosticCode::TypeMismatch(§7.4.6)。- 已知 type、缺必填配置属性:
Validate报MissingRequiredProperty(T8);不得在 Load/Validate 用静默默认值把缺失必填糊成成功。可选属性可缺省;与「未知type_id→ LoadUnknownTypeId」分轨。
// 示意
struct Value {
TypeDesc type;
// variant: bool/int32/…/PixelImageHandle/Point2d/…
};
class PropertyBag {
public:
bool Has(std::string_view name) const;
TypeDesc GetType(std::string_view path) const; // 支持 a.b
expected<Value, Diagnostic> Get(std::string_view path) const;
expected<void, Diagnostic> Set(std::string_view path, const Value& v);
};
5.4.1 可链接类型表(MVP)
源 TypeDesc | 可连目标 | 备注 |
|---|---|---|
任意 T | 同 T | 精确匹配(含四图像格式各自精确) |
Int32 | Int64 / Float64 | 见提升白名单 |
Int64 | Float64 | |
Float32 | Float64 | |
Int32 | Float32 | 允许;注意精度 |
图像 Image* | 仅同格式 | 跨格式 → 拒绝;须显式转换工具 |
Enum | 同 enum_id 的 Enum | enum_id 不同 → TypeMismatch |
Region / Fixture / 几何 | 仅同 Type | 不做隐式几何↔标量 |
未列出的组合一律 不兼容。
5.4.2 数值提升白名单(正向;反向禁止)
| 源 | 目标 | MVP |
|---|---|---|
Int32 → Int64 | 允许 | |
Int32 → Float32 | 允许 | |
Int32 → Float64 | 允许 | |
Int64 → Float64 | 允许 | |
Float32 → Float64 | 允许 | |
Float64 → Float32 / 整数 | 禁止 | |
| 整数 ← 浮点 | 禁止 | |
Bool ↔ 数值 | 禁止 | |
String ↔ 其它 | 禁止 |
5.4.3 图像格式:精确匹配
| 规则 | 说明 |
|---|---|
| 同格式 | ImageGray8→ImageGray8 等可连。 |
| 跨格式 | ImageGray8→ImageRgb24、ImageRgb24→ImageBgr24、ImageDepth16→任一可见光等 一律禁止;编辑期拒绝建链,Validate/Run 报 TypeMismatch。 |
| 转换 | 只通过 显式转换工具(另作 Tool),不在 Link 层静默转格式或转句柄类型。 |
5.4.4 方向、序与其它
| 规则 | 说明 |
|---|---|
| 不兼容 | 编辑期标红/拒绝保存为合法链接;运行期若仍存在则 Run 失败(TypeMismatch / InvalidBinding)。 |
| 方向 | 只能 源(产出)→ 目标(消耗);禁止双向边。 |
| 无环 / 列表序 | 列表序必须能解释所有链接:源工具位于目标工具之上;工具流终端例外见下。 |
| 封装 | 外层不得绑内层私有工具属性(Encapsulation)。 |
工具流终端与序:
Inputs终端视为在所有子工具之前已就绪(宿主或父级注入)。Outputs终端只能由内部源写入,且在工具流结束时固化。- 子工具之间:若存在 A→B 的链接,则列表中 A 必须在 B 之前(编辑器自动排序或拒绝非法序 →
OrderViolation)。
5.5 几何 / ROI / Fixture 最小字段
与历史 §5.4 口径一致并写死字段:定位 → 夹具 → 测量 是工具链语义,不是第二套编排图 / Region 图模型 / 坐标系拓扑单元类型。
5.5.1 语义链(非图模型)
- 定位工具输出
Pose2d/Affine2d; - Fixture 工具输出
Fixture(空间名 + 变换),在图像坐标解释上附着新空间; - 下游工具的
Region落在 Fixture 空间;输入图像句柄常仍为原图像(同像素,不同空间解释)。
5.5.2 最小字段(MVP)
Type | 最小字段(示意) | 说明 |
|---|---|---|
Point2d | x: Float64, y: Float64 | 像素或当前空间坐标 |
Line2d | x0,y0,x1,y1 或 point + angle(实现择一,文档与工具登记表固定) | 不做隐式极坐标双表示并存 |
Circle2d | cx, cy, r | |
Rect2d | x, y, width, height(轴对齐) | ROI 常用;旋转矩形 MVP 不做或归工具私有扩展 |
Affine2d | m00,m01,m02,m10,m11,m12(行优先 2×3) | |
Pose2d | x, y, angle_rad | 与 Affine2d 可经工具转换, Link 不自动转换 |
Region | space: String(可选,空=图像默认空间), `shape: Rect2d | …` |
Fixture | space_name: String, transform: Affine2d, parent_space: String(可选) | Fixture 工具输出;下游 Region.space 引用 space_name |
禁止:
- 引入「坐标系节点 / Region 边 / 几何图」作为编排一等公民;
- 把 Fixture 当成第二套图像句柄模型;
- 在属性路径中写脚本计算 ROI。
6.1 需求(先定需求,不臆造二进制)
| 需求 | 说明 |
|---|---|
| ToolFlow 是复用单元 | 可保存为组件/配方片段,再插入其它工具流或作为 Job 根。 |
| 完整保存 | 子工具类型与参数、列表序、禁用集、终端定义、链接、工具流元数据(名称、版本号字段)。 |
| 稳定身份 | 终端与工具实例有持久 id,便于链接在重命名后仍可恢复;显示名可改。 |
| 可版本化 | 文件/文档含 schema_version(过高 → Load 拒绝);可选 component_version 不拦 Load(T8)。 |
| 图像像素默认不进配方 | 配方存参数与结构;演示图/金样图可作为可选附件引用,不默认嵌入巨大像素。 |
| 可读交换格式优先 | MVP 倾向 JSON/UBJSON/或等价结构化文本+附件,便于 diff 与审查;是否另定二进制加速格式后期再定。 |
6.2 非目标(持久化)
- 不承诺兼容 Cognex
.vpp。 - 不承诺兼容 MPF 既有序列化。
- 不在本文规定加密与签名(若产线需要,另文)。
- 加载器不得尝试「静默迁移」 MPF / VisionPro 文件为
.avtf;遇异格式应明确失败。
6.3 文档 / 配方格式目标
| 项 | 定稿倾向(MVP) |
|---|---|
| 扩展名 | .avtf(Vyntek ToolFlow 文档格式;扩展名 .avtf 历史保留,已定稿,文档与 API 对齐此扩展名) |
| 载体 | 结构化文本优先(JSON 或等价);大附件(金样图等)用旁路文件 / 包内相对引用(assets[],见 §6.6),不默认内嵌巨像素 |
| 文档根对象 | 一个可加载的 ToolFlow(或顶层 Job 包装,内含根 ToolFlow);MVP 以「单根 ToolFlow 文档」为主 |
| 编码 | UTF-8;路径分隔在文档内用正斜杠约定 |
顶层字段写法(已定稿倾向;实现以本结构为准,不要来回改名):
{
"schema_version": 1,
"kind": "toolflow",
"meta": { "name": "InspectA", "created": "...", "author": "...", "component_version": "…" },
"flow": { /* inputs[]、outputs[]、tools[]、links[]、disabled、flags — 嵌套见 §6.6 */ },
"assets": [ /* 可选;kind/path;见 §6.6.3 */ ]
}
6.4 必须持久化 vs 运行时态
| 类别 | 必须写入文档 | 不写入(运行时态) |
|---|---|---|
| 结构 | 工具类型 id、实例稳定 id、显示名、列表序 | — |
| 参数 | 工具可编辑属性的当前配置值(标量/几何/枚举/ROI 等) | 上次 Run 的结果属性、中间图句柄 |
| 终端 | Inputs/Outputs 的名称、方向、 TypeDesc、稳定 id | 终端上的当前注入/输出值(值由宿主在 Run 前注入、Run 后读取) |
| 链接 | 源/目标端点(稳定 id + 属性路径或终端名)、链接稳定 id | — |
| 策略 | disabled 集合、abort_on_tool_failure 等工具流级标志 | 取消令牌、诊断缓冲、追踪 id |
| 元数据 | schema_version、名称、可选组件版本字段 | RunStatus / LastRun 时间戳 |
原则:配方描述「是什么、怎么连」;不描述「上一次跑成什么样」。 演示图/金样若需要,以可选附件引用表达,默认不嵌入。旁路字段见 §6.6:assets[].path + kind: "golden_image"。
6.5 版本字段、向前兼容、未知工具与缺属性(Alive 拍板 · T8)
两条版本轴互不替代:
schema_version管文档形状门禁;component_version管组件/配方信息标签。缺属性与未知 type 分属 Validate / Load,不得用静默默认值糊弄。
| 话题 | 规则(MVP) |
|---|---|
schema_version | 文档必有;与 component_version 独立。加载器对过高主版本 拒绝 Load → DiagnosticCode::SchemaTooNew(§7.4.6)。同主版本或声明支持的旧主版本可向前读。 |
component_version | 可选(通常在 meta;未来可有工具级)。缺失、空串、无法解析均不拦 Load;不作 schema 门禁;不与 schema_version 比较升降。仅信息/展示/宿主策略。 |
| 向前兼容 | 新加载器可读旧文档(同主版本或声明支持的旧主版本);读到未知可选字段 → 保留(可告警、不剥掉),不得静默改写语义。 |
| 未知工具类型 | 反序列化时 ToolRegistry 无对应 type_id → 严格失败 → UnknownTypeId(MVP)。编辑器「占位只读、不可 Run」归后续,不做进 MVP。 |
| 已知 type、缺属性 | type_id 已注册,但配置态必填属性在 props/属性袋中缺失或为空槽 → 不在 Load 用静默默认值补齐糊弄;Validate 失败 → MissingRequiredProperty(§7.4.6)。可选属性缺省可空,不得冒充必填。 |
| 未知链接端点 | 端点 id 在文档内不存在 → 加载失败或校验失败(与 §5.4 / §7.4.6 一致)。 |
| 不兼容声明 | 不读写 .vpp;不读写 MPF 序列化;异格式魔数/扩展名直接拒绝,不猜测转换。 |
对照(勿混):
| 情况 | 阶段 | 码 / 结果 |
|---|---|---|
schema_version 过高 | Load | SchemaTooNew;拒绝 |
缺 / 坏 component_version | Load | 通过(可 Warning);与 schema 无关 |
type_id 未注册 | Load / Create | UnknownTypeId;严格失败 |
type_id 已知,缺必填配置属性 | Validate(亦可在 Run 前强制 Validate) | MissingRequiredProperty;禁止静默默认值让 Validate/Run「假装成功」 |
6.6 .avtf 嵌套 schema(Alive 拍板 · T5)
不改已拍板顶层名:
schema_version/kind/meta/flow(见 §6.3)。本节展开嵌套对象与assets[];像素不嵌入文档。
6.6.1 顶层与 flow 嵌套字段
| 路径 | 类型(示意) | 必填 | 说明 |
|---|---|---|---|
schema_version | int | 是 | 文档 schema 主版本(T8);过高 → Load SchemaTooNew(§7.4.6);与 component_version 独立 |
kind | string | 是 | 固定 "toolflow"(MVP) |
meta | object | 否 | name / created / author 等;未知子字段保留 |
meta.component_version | string | 否 | 可选;仅信息/展示(T8)。与 schema_version 独立; Load 不因缺失或无法解析而失败 |
flow | object | 是 | 根 ToolFlow 本体 |
flow.id | string | 否 | 稳定 id;缺省可由加载器生成 |
flow.name | string | 否 | 显示名 |
flow.abort_on_tool_failure | bool | 否 | 默认 true |
flow.inputs[] | array | 否 | 输入终端;见下 |
flow.outputs[] | array | 否 | 输出终端 |
flow.tools[] | array | 是(可空) | 有序列表;序即执行序 |
flow.links[] | array | 否 | 链接 |
flow.disabled[] | string[] | 否 | 禁用工具稳定 id |
assets[] | array | 否 | 旁路附件引用;见 §6.6.3 |
终端元素(inputs[] / outputs[]):
| 字段 | 必填 | 说明 |
|---|---|---|
id | 是 | 稳定 id |
name | 是 | 同方向内唯一 |
type | 是 | TypeDesc 字面:标量为 "Int32" 等;Enum 为 { "type":"Enum", "enum_id":"…" } 或等价 |
| (值) | — | 不持久化运行时注入/输出值 |
工具元素(tools[]):
| 字段 | 必填 | 说明 |
|---|---|---|
id | 是 | 实例稳定 id |
type_id | 是 | 注册表类型 id;未知 → UnknownTypeId |
name | 否 | 显示名 |
props | 否 | 配置态属性袋(标量/几何/枚举/ROI 等 JSON);结果态不写 |
flow | 条件 | 当该项为嵌套 ToolFlow时出现:内层同构 inputs/outputs/tools/links/disabled;外层只经终端链接 |
链接元素(links[]):
| 字段 | 必填 | 说明 |
|---|---|---|
id | 否 | 链接稳定 id |
src | 是 | 端点路径:tool_id.prop[.sub…] 或 flow.Inputs.name / flow.Outputs.name(§5.1) |
dst | 是 | 同上 |
6.6.2 正样例(最小合法)
{
"schema_version": 1,
"kind": "toolflow",
"meta": {
"name": "InspectA",
"component_version": "1.2.0"
},
"flow": {
"id": "flow_inspect_a",
"name": "InspectA",
"abort_on_tool_failure": true,
"inputs": [
{ "id": "tin_img", "name": "InputImage", "type": "ImageGray8" }
],
"outputs": [
{ "id": "tout_cnt", "name": "DefectCount", "type": "Int32" },
{ "id": "tout_ok", "name": "OkNg", "type": "Bool" }
],
"tools": [
{
"id": "acq",
"type_id": "acq_fifo",
"name": "Acq",
"props": { "TimeoutMs": 1000 }
},
{
"id": "align",
"type_id": "pm_align",
"name": "Align",
"props": { "AcceptThreshold": 0.6 }
},
{
"id": "blob",
"type_id": "blob",
"name": "Blob",
"props": {
"Region": { "space": "", "shape": { "x": 10, "y": 10, "width": 200, "height": 120 } }
}
}
],
"links": [
{ "id": "l1", "src": "flow.Inputs.InputImage", "dst": "acq.PassthroughImage" },
{ "id": "l2", "src": "acq.OutputImage", "dst": "align.InputImage" },
{ "id": "l3", "src": "align.OutputImage", "dst": "blob.InputImage" },
{ "id": "l4", "src": "blob.Count", "dst": "flow.Outputs.DefectCount" }
],
"disabled": []
},
"assets": [
{
"kind": "golden_image",
"path": "assets/golden/board_a.png",
"role": "template",
"tool_id": "align"
}
]
}
嵌套 ToolFlow 正样(节选):外层 tools[] 中一项带内层 flow,外层 links 只连内层终端名(经内层工具 id + Outputs.* 或文档约定的终端路径),禁止写内层私有子工具属性路径。
6.6.3 assets[](像素不嵌入)
| 字段 | 必填 | 说明 |
|---|---|---|
kind | 是 | MVP:"golden_image"(其它 kind 后增;未知 kind → 告警保留或按实现策略,不得当像素嵌入) |
path | 是 | 相对 .avtf 的旁路路径(正斜杠);禁止把像素 base64/数组写进 JSON |
role | 否 | 如 "template" / "demo";仅元数据 |
tool_id | 否 | 关联工具稳定 id(如对齐模板所属工具) |
规则:
- 像素不嵌入:
.avtf正文无图像字节;金样/演示图一律旁路文件。 - 缺失旁路文件:Load 结构仍可成功;Run/Validate 若工具需要该资产再报
IoFailure/ 工具级失败(实现择一,须进诊断列表)。 component_version(meta或未来工具级)可选;缺失、空串、非 semver 均不拦 Load(T8;不作 schema 门禁)。
6.6.4 反样例(须拒绝或校验失败)
| 反例 | 期望 |
|---|---|
缺 schema_version / kind≠"toolflow" / 缺 flow | Load 失败(格式错误 / IoFailure 或专用解析码) |
schema_version 高于加载器支持 | SchemaTooNew( Load 拒绝) |
缺 meta.component_version / 无法解析 | Load 成功(可 Warning);不与 schema 比较 |
tools[].type_id 未注册 | UnknownTypeId( Load/Create 严格失败) |
type_id 已注册,但缺工具声明的必填配置属性(props 缺键或空槽) | Validate 失败 → MissingRequiredProperty;禁止静默填默认值后当通过 |
links[].src 指向不存在的 tool_id 或属性 | InvalidBinding |
| 链接类型跨图像格式(如 Gray8→Rgb24) | TypeMismatch |
| 列表序:源工具在目标之下 | OrderViolation |
外层 link 写内层私有 inner.child.Prop | Encapsulation |
JSON 内嵌 "pixels":[…] / base64 大图冒充资产 | 拒绝(非 schema;像素不得进配方) |
顶层改名(如用 toolblock / graph 代替 flow) | 拒绝;不静默迁移 |
7.1 C++ 核心提供什么
| 能力 | 说明 |
|---|---|
| 对象模型 CRUD | 创建/删除工具与终端、调序、建删链接、设禁用。 |
| 校验 | 类型兼容、列表序与链接一致性、环检测(按 §5.4)。 |
| 执行 | RunSession::Run、取消、状态与诊断事件。 |
| 注册表 | 工具类型注册与工厂。 |
| 序列化 | 加载/保存 ToolFlow 文档(§6)。 |
| 观察钩子 | Run 前后、每工具起止、属性变更通知(供 UI / 日志);不把观察者当执行边。 |
7.2 进程形态倾向
| 方案 | 优点 | 代价 | 倾向 |
|---|---|---|---|
进程内 C++ 库(libtoolflow) | 延迟低、嵌入简单、headless 同链 | 崩溃隔离弱;语言绑定需谨慎 | MVP 首选 |
| 本地服务 + IPC | 隔离、多语言 UI 易挂接 | 图像句柄跨进程成本高 | 作为演进选项;若上 IPC,图像宜共享内存句柄,另文 |
MVP:先交付可静态/动态链接的 C++ API;C# 绑定同一库时 MVP = PInvoke(C++/CLI 非默认;细则见 csharp-binding.md);headless runner 直接链库。若后续要独立进程,保持 同一领域 API 外形,仅换传输。
7.3 上层分工
| 层 | 职责 | 不职责 |
|---|---|---|
| C++ Tool / ToolFlow 核心 | 模型、执行、校验、持久化 | UI 控件、Avalonia/WPF 布局 |
| C# 显示/编辑封装 | 树/列表编辑、终端连线 UI、属性网格、图像预览绑定 | 私自实现第二套执行引擎 |
| 其它 UI | 同:绑定核心 API / 通知 | 分叉对象模型 |
| Headless runner | 加载文档、注入 Inputs、循环 Run、写出 Outputs/日志 | 依赖任何 GUI 程序集 |
依赖方向:
UI / Runner / 产线宿主 → Tool / ToolFlow C++ API(vyntek::toolflow) → vyntek::imaging(C++ 像素/句柄权威)
成像与显示基建文档(image-model / memory / display)继续作为像素与显示契约来源;像素/句柄权威在 C++ vyntek::imaging,C# 仅为绑定/显示投影。不把历史上的「DAG 编排」叙述带回本编排核心。
宿主集成(Arden · R7):headless / C# 显示宿主同核、示意 C 导出(vyntek_toolflow_* / vyntek_imaging_*,非稳定 ABI)、跨边界 opaque / Diagnostic 上抛 / UI 只消费投影 / 观察不驱动 Run、停止矩阵宿主侧分派——见专文 host-integration.md。本节 §7.2–7.4 语义与 Diagnostic 不因 R7 改动(保持 v1.9)。
7.4 公共 API 表面(示意)
示意 API,非已发布头文件。 类型名、命名空间、错误码与头文件路径均可在实现期调整;与 §9.4 手感一致,用于钉住宿主能力边界,不是稳定 ABI,也不承诺与 VisionPro / MPF 同形。
7.4.1 创建、注册、AddTool、终端
| 能力 | 示意表面 | 约束 |
|---|---|---|
| 注册 | ToolRegistry::Register(type_id, factory);register_builtin_tools(registry) | 进程内一份注册表;反序列化与 UI 工具箱共用 |
| 创建空流 | ToolFlow::Create(name) | 返回拥有所有权的根(或 unique_ptr) |
| 加载 | ToolFlow::Load(path, registry) | 见 §6;未知 type id 默认失败 |
| 添加工具 | flow->AddTool(tool) / AddTool(registry.Create(type_id, name)) | 所有权转入 flow;返回非拥有观察指针或 tool id |
| 调序 | Reorder(tool_id, new_index) / MoveBefore | 调序后须能通过链接序校验 |
| 定义终端 | AddInput(name, TypeDesc) / AddOutput(name, TypeDesc) | 名称在同方向内唯一;赋稳定 id |
| 移除 | RemoveTool / RemoveTerminal | 级联删除相关链接;MVP 不做跨流共享所有权 |
// 示意、非已发布 ABI。命名空间 vyntek::toolflow;头文件见 cpp-headers.md
#include <vyntek/toolflow/toolflow.hpp>
#include <vyntek/toolflow/registry.hpp>
using namespace vyntek::toolflow;
ToolRegistry registry;
register_builtin_tools(registry);
auto flow = ToolFlow::Create("InspectA"); // 拥有所有权的根
auto* acq = flow->AddTool(registry.Create("acq_fifo", "Acq")); // 所有权转入 flow
// ToolId id = acq->id(); // 对外稳定标识
flow->AddInput("InputImage", TypeDesc{Type::ImageGray8});
flow->AddOutput("DefectCount", TypeDesc{Type::Int32});
7.4.2 Link 建立与校验
| 能力 | 示意表面 | 校验(失败则拒绝建立或 Validate 报错) |
|---|---|---|
| 建链接 | flow->Link(src_tool, src_prop, dst_tool, dst_prop);Link(src, flow->Output(name));端点亦可为 flow->Input(name) | 见下表 |
| 删链接 | Unlink(link_id) / 按端点拆 | |
| 校验 | Validate() → 诊断列表;ValidateOrThrow() | 编辑保存与 Run 前均可调用;含缺必填属性 → MissingRequiredProperty(T8) |
Link 校验规则(与 §5.4 / §4.3 对齐,API 层必须执行):
| 检查 | 行为 |
|---|---|
| 类型兼容 | 精确匹配或白名单数值提升;图像跨格式禁止;不兼容 → 拒绝 / 标红 |
| 方向 | 仅源(产出)→ 目标(消耗);禁止双向 |
| 无环 / 列表序 | 子工具间:源工具在列表中必须位于目标之上;Inputs 视为先于一切子工具;Outputs 仅在流结束时由内部源写入 |
| 禁用策略 | 链接可指向已禁用工具; Run 时:目标禁用则本轮不写入;源禁用则下游拉值得「源未更新」——MVP 默认 严格:视为无效绑定 / Run 失败(与 §4.3 一致) |
| 封装 | 外层不得 Link 到内层 ToolFlow 的私有子工具属性;只连内层终端 |
校验失败路径(示意):
#include <vyntek/toolflow/link.hpp>
// 建链:类型或列表序不合规则拒绝(不留下半条边)
auto link_result = flow->Link(acq, "OutputImage", align, "InputImage");
if (!link_result) {
// link_result.error(): TypeMismatch / OrderViolation / Encapsulation …
return;
}
// 批量编辑后统一校验
std::vector<Diagnostic> diags;
if (!flow->Validate(diags)) {
for (const auto& d : diags) {
// d.severity / d.tool_id / d.message — 编辑器标红或日志
}
return;
}
// 或:flow->ValidateOrThrow(); // 抛 ToolFlowValidationError
7.4.3 Run、RunSession、取消与失败中止
| 能力 | 示意表面 | 规则 |
|---|---|---|
| 会话 | RunSession session(flow) | 持有根 ToolFlow(非拥有或共享约定由实现定;MVP 倾向 session 不抢所有权) |
| 注入输入 | session.SetInput(name, value) | Run 前写入对应 Inputs 终端 |
| 运行 | RunResult Run();可选 Run(RunContext) | 同一 session 同时只允许一次 Run;并发第二次 → 拒绝(排队留后阶段) |
| 取消 | RunContext 带取消令牌;session.RequestCancel() | 协作式(Alive · T9):当前工具 Run 返回后检查令牌;不再启动后续 Tool;诊断码 Cancelled。工具内抢占不做进 MVP(后议) |
| 失败中止 | 工具流标志 abort_on_tool_failure(默认 true) | 子工具失败则停止后续;RunResult 含 ok、failed_tool_id、message、已跑范围可诊断;码 Aborted |
| 连续采集 | 宿主循环 session.Run() | 禁止用链接环表达相机流;停相机 ≠ 自动 Cancel Run(见下停止矩阵) |
#include <vyntek/toolflow/run_session.hpp>
#include <vyntek/imaging/handle.hpp> // 只依赖成像公开句柄头
RunSession session(flow.get()); // MVP:session 不抢 flow 所有权
session.SetInput("InputImage", image_handle); // vyntek::imaging::PixelImageHandle(ReadOnly)
RunContext ctx; // 可选:取消令牌、截止时间
RunResult r = session.Run(ctx);
if (!r.ok) {
// r.failed_tool_id / r.message / diagnostics(含 Cancelled / Aborted)
// abort_on_tool_failure=true 时后续子工具未 Run
}
// 同 session 并发第二次 Run → 拒绝(Busy)
# 停止矩阵(RunSession / 采集 / 显示 · Alive 拍板 · T9)
三层勿混成一层:编排取消(
RunSession)≠ 宿主停相机(采集)≠ 显示解除引用(ImageDisplay)。双向对齐: session-lifecycle.md · camera-acquisition.md · image-display.md。
| 动作 | 编排(Run / RunSession) | 采集(相机 / 入口队列) | 显示(ImageDisplay) |
|---|---|---|---|
RequestCancel / 取消令牌 | 协作式:当前 Tool 返回后不再启动后续;RunResult.ok==false;码 Cancelled | 不自动 Stop 相机;在途帧/SDK 归还仍按采集协议 | 不因 Cancel 而强制卸图;仍只作观察者 |
| 宿主停相机(Stop/Close 采集) | 不自动 RequestCancel 已在跑的 Run;在途 Run 继续至工具边界结束或自行 Cancel | 停止产生新帧;按 SDK 协议排空/归还(见采集/会话专题) | 可能因无新帧而停留在最后一帧;不停编排 |
失败中止(abort_on_tool_failure) | 后续 Tool 不 Run;码 Aborted | 不隐含停相机 | 不停显示 |
| 显示关闭 / 卸 Source / Dispose 控件 | 不停编排;不 Cancel Run | 不停相机 | 只释放显示自有引用/快照;观察层退出 |
| 会话级排空/停流(session-lifecycle) | 可显式 Cancel 在途 Run,再进入 Quiescing→…→Stopped | 停止接受新工作并归还 SDK | 会话拥有的预览引用须解除;导出到会话外的窗口不由会话强收 |
硬规则(Alive):
- Cancel Run = 协作式 + 不再启动后续 Tool(码
Cancelled);不做工具内抢占进 MVP。 - 宿主停相机 ≠ 自动 Cancel 已在跑的 Run(两层写清;宿主若要两端都停,须显式既停采集又
RequestCancel)。 - 显示只观察、不停编排(也不停相机)。
7.4.4 只读查询(上次状态与 Outputs)
| 查询 | 示意表面 | 语义 |
|---|---|---|
| 会话结果 | session.GetOutput (name);session.LastResult() | Run 返回后可读,直至下次 Run 覆盖或 Reset |
| 工具状态 | tool->LastRunStatus();tool->GetProperty (path) | 观察用;UI LastRun 图订阅同一数据,不得当作下一次 Run 的隐式输入源 |
| 结构只读 | flow->Tools();flow->Links();flow->Inputs() / Outputs() | 编辑器与诊断遍历 |
| 重置 | session.ResetOutputs() / flow->ClearLastRun()(示意) | 清运行时态,不动配方结构 |
7.4.5 加载 .avtf 与查询(示意)
#include <vyntek/toolflow/serialize.hpp>
auto loaded = ToolFlow::Load("recipes/inspect_a.avtf", registry);
if (!loaded) {
// loaded.error(): UnknownTypeId / SchemaTooNew / IoFailure …
return;
}
std::unique_ptr<ToolFlow> flow = std::move(*loaded);
RunSession session(flow.get());
session.SetInput("InputImage", grab_one_frame());
RunResult r = session.Run();
if (r.ok) {
int count = session.GetOutput<int>("DefectCount");
(void)count;
}
持久化字段分层与未知类型策略见 §6.3–6.6;Diagnostic 见 §7.4.6;未知工具 type id → 严格失败。
7.4.6 Diagnostic(Alive 拍板 · T4)
对齐本示意 API(§7.4)与 MVP 退出(§10.1):
Validate/Run/Load均返回结构化Diagnostic列表(可与expected/RunResult并存;不得只丢一条裸字符串)。
# 结构
// 示意、非已发布 ABI;命名空间 vyntek::toolflow
enum class Severity : uint8_t { Error, Warning, Info };
enum class DiagnosticCode : uint16_t {
TypeMismatch, // 链接/赋值/Set 类型不兼容(含图像跨格式)
OrderViolation, // 列表序无法解释链接(源不在目标之上)
Encapsulation, // 跨越 ToolFlow 封装边界(绑内层私有属性)
Busy, // 同 RunSession 重入 / 并发第二次 Run
UnknownTypeId, // 注册表无此 type_id(Load/Create)
SchemaTooNew, // schema_version 高于加载器
IoFailure, // 文件读写/旁路资产打开失败等
SourceDisabled, // 拉值时源工具本轮禁用(MVP 严格失败)
InvalidBinding, // 端点不存在、方向非法、残缺链接等
MissingRequiredProperty, // 已知 type 缺必填配置属性(Validate;T8)
Aborted, // abort_on_tool_failure 中止后续
Cancelled, // 协作式取消后未再启动后续
// 实现可增内部码;对外文档以本表为准
};
struct Diagnostic {
DiagnosticCode code;
Severity severity; // Error / Warning / Info
std::string message; // 人类可读;可本地化键另议
std::string tool_id; // 可选;相关工具稳定 id
std::string path; // 可选;属性路径或终端路径(§5.1)
std::string link_id; // 可选
};
| 字段 | 说明 |
|---|---|
code | 机器可读;UI/日志按码分支 |
severity | Error:阻止成功完成该次 Validate/Run/Load; Warning:可继续但须可见; Info:提示 |
message / 定位字段 | 便于编辑器标红与 headless 日志;定位字段可空 |
# DiagnosticCode 表(MVP)
| Code | 典型 severity | 出现于 | 说明 |
|---|---|---|---|
TypeMismatch | Error | Validate / Link / Run 拉值 / PropertyBag::Set | 含数值提升白名单外、图像跨格式、Enum enum_id 不同 |
OrderViolation | Error | Validate / Link | 子工具间源不在目标列表之上 |
Encapsulation | Error | Validate / Link | 外层绑内层私有工具属性 |
Busy | Error | Run | 同 session 已有 Run 在途;拒绝第二次 |
UnknownTypeId | Error | Load / Create | MVP 严格失败;无占位成功 |
SchemaTooNew | Error | Load | 过高 schema_version |
IoFailure | Error | Load / Save / 资产打开 | 路径不可读、截断、权限等 |
SourceDisabled | Error(MVP) | Run 拉值 | 源在 disabled;与 §4.3 严格策略一致 |
InvalidBinding | Error | Validate / Load / Run | 端点缺失、方向错误、残链 |
MissingRequiredProperty | Error | Validate(亦可 Run 前强制) | 已知 type_id 缺必填配置属性;禁止静默默认值糊弄(T8);与 UnknownTypeId 分轨 |
Aborted | Error | Run | 子工具失败且 abort_on_tool_failure;后续未跑 |
Cancelled | Error 或 Warning* | Run | 协作式取消:当前工具返回后不再启动后续;*若部分结果仍交出,可用 Warning,但 RunResult.ok==false(T9) |
未列出的内部码不得替代上表语义;新增对外码须改本文。
# 返回约定
| API | 约定 |
|---|---|
Validate / Validate(diags) | 填满 vector ;存在任一 Error → 校验失败(返回 false / error) |
Run / RunResult | RunResult 含 ok 与 diagnostics(或会话可查询的同次列表);Busy/Aborted/Cancelled/SourceDisabled/TypeMismatch 等按上表 |
Load | 失败时 error 侧带 Diagnostic(至少一个);成功时仍可附带 Warning/Info(如未知可选字段保留告警) |
Link(建链) | 拒绝时返回单条或多条 Diagnostic,不留下半条边 |
与 §10.1 / test-contracts.md 对齐的可测点:类型/序/禁用源/重入 Busy/过高 schema/未知 type id/缺必填属性(MissingRequiredProperty)/取消与失败中止,均须能在诊断列表中断言对应 DiagnosticCode(契约表 §8 码覆盖清单)。
7.5 头文件与模块边界
完整 include 树、公开头列表、detail/ 边界及与 ImageDisplay(C#)分界见专文:
→ cpp-headers.md(命名空间 vyntek::toolflow / vyntek::imaging;公开前缀 / )
摘要:
| 要点 | 约定 |
|---|---|
| 公开头 | toolflow.hpp、tool.hpp、terminal.hpp、link.hpp、run_session.hpp、registry.hpp、serialize.hpp、diagnostic.hpp(或转发) |
| 成像消费 | ToolFlow 只依赖 imaging 公开句柄头;不反向依赖;链接赋值 ≠ DAG |
| 内部头 | detail/ / internal/ 不对宿主暴露 |
| UI | C++ 不 include 任何 UI;显示经句柄 → 绑定层 → C# ImageDisplay |
7.6 Tool 作者最小契约与 MVP 测试替身(T7 · P0)
Alive 拍板(T7):MVP 工具作者(含内置示意工具与 fake 替身)必须满足本节最小契约;注册约定与反序列化共用同一
ToolRegistry。fake 清单供 §9.1 / §10.1 headless 验收,不要求真实 Acq/PMAlign/相机 SDK。
7.6.1 作者必须实现什么
| 项 | 要求 | 说明 |
|---|---|---|
| 类型 id | 稳定、进程内唯一的 type_id 字符串 | 与 .avtf tools[].type_id、注册表键一致;未知 id → Load 严格失败(§6.5) |
| 身份与名称 | 实例 id(流内稳定)+ 可编辑显示名 | AddTool 后由 ToolFlow 拥有;作者不在外部双拥有 |
| 启用标志 | 可被工具流禁用集合跳过 | 禁用时本轮不 Run;作源时下游策略见 §4.3 |
| 属性袋 | 声明输入/输出/参数属性:名称、TypeDesc、可链接性 | 图像属性载荷为 vyntek::imaging 公开句柄;交给下游前遵守 §3.2 / §3.4 |
Run(RunContext) | 实现一次原子工具语义;读写自身属性 | 执行器在 Run 前拉入链接、Run 后供下游再拉;作者不自己遍历 Link 表 |
| 失败语义 | 失败返回明确失败状态;可附诊断 | 不得把未 Complete / Invalid 图像冒充成功输出;abort_on_tool_failure 时由容器中止后续(§4.3) |
| 取消(协作) | 在工具边界可查询 RunContext 取消令牌 | MVP 不要求工具内抢占;长循环建议定期检查 |
| LastRun / 状态 | Run 后输出属性与运行状态可读,直到下次覆盖或重置 | UI/观察层只读;不得作为隐式二次输入源(§3.2-4) |
| 成像纪律 | 输入按 ReadOnly 消费;需改像素则 Clone;输出稳定发布后再可被链接 | 禁止别名/native 指针绕过成像契约;对齐 §3.4 |
示意表面(非已发布 ABI):
// 示意:作者实现 ITool;勿当作已定头文件
struct ITool {
ToolId id() const;
std::string_view type_id() const;
std::string_view name() const;
bool enabled() const;
// 属性袋:按名/路径读写 TypeDesc 约束下的 Value
Span<const PropertyDesc> properties() const;
expected<Value, Status> GetProperty(std::string_view name) const;
Status SetProperty(std::string_view name, const Value& v);
// 一次 Run:失败用 Status / 结构化诊断,勿抛未文档化异常穿过边界(实现期可定)
ToolRunStatus Run(RunContext& ctx);
// LastRun 状态:ok / failed / skipped / cancelled …
};
7.6.2 Registry 注册约定
| 约定 | 规则 |
|---|---|
| 注册 | ToolRegistry::Register(type_id, factory);factory(name) → unique_ptr (示意) |
| 内置入口 | register_builtin_tools(registry) 注册产品示意工具;测试另有 register_fake_tools(registry)(或测试包内等价),勿把 fake 默认链进生产配方 |
| 共用 | 同一 registry 服务:UI 工具箱、Create、.avtf Load;禁止「反序列化旁路工厂」 |
| 重复注册 | 同 type_id 重复 Register → 拒绝(或测试中显式 Replace;生产默认拒绝) |
| Create | registry.Create(type_id, name);未知 → UnknownTypeId |
| 所有权 | flow->AddTool(...) 后所有权在 ToolFlow;registry 不保留实例 |
| 版本 | 工具逻辑版本可进 meta / 可选 component_version;不用其拦 Load(§6.5/§6.6 · T8);拦 Load 的是过高 schema_version 与未知 type id;缺必填属性走 Validate |
7.6.3 MVP fake 替身清单(headless §9.1 / §10.1)
示意名;实现可放测试库。目标:无相机/无真实算法即可勾选 §10.1「执行 / 注册」相关项,并覆盖 §3.4 关键断言。
示意 type_id | 示意类名 | 行为 | 主要验收用途 |
|---|---|---|---|
fake_acq | FakeAcq | 无外部设备;每次 Run 产出固定或序号可配置的 Gray8 ReadOnly OutputImage(工厂 Copy/预置句柄) | §9.1 流水线头;H3/H4/H6 |
fake_pm_align | FakePMAlign | 读 InputImage(ReadOnly);写 Pose(标量/几何最小字段);可选 OutputImage=Share(输入) | §9.1 第二级;链接类型与序 |
fake_passthrough | Passthrough | InputImage → OutputImage,仅 Share/ShareImage,不改像素 | H4/H6/H7;证明链接非拷贝调度 |
fake_clone_write | CloneThenWrite | 对输入 Clone → OpenWrite 填充常数 → CompleteAndOpenRead → ShareImage 输出 | H2/H8 |
fake_fail_once | FailOnce | 第 1 次 Run 失败,之后成功(可配置 N) | abort_on_tool_failure;H10 |
fake_fail_always | FailAlways | 恒失败并给出可断言诊断 | 失败中止 / Diagnostic |
fake_constant | ConstantScalar | 参数决定,输出固定 int32/bool | Outputs 终端;非图像链接 |
fake_cancel_check | CancelCheck | Run 内轮询取消令牌,合作退出 | §10.1 取消条(协作式) |
fake_busy_probe | BusyProbe(可选) | 故意慢 Run,供同 session 重入 → Busy | §10.1 重入拒绝 |
最小 headless 拼法(等价 §9.1):FakeAcq → FakePMAlign → Passthrough(或 Blob 的 ConstantScalar 替身),链接图像与 Pose;断言 Outputs 与 §3.4 H6。真实工具落地后,fake 仍保留作回归。
8. 与现有文档关系
| 文档 | 关系 |
|---|---|
| overview.md | 入口索引: Tool / ToolFlow 为编排专题;旧 DAG 文标为已废止。 |
| cpp-headers.md | C++ 头文件与模块边界:公开/内部头、include 树、与成像句柄及 ImageDisplay(C#)分界。 |
| image-model.md / memory-management.md | 成像与内存基建;工具属性中的图像句柄应对齐其格式与寿命思想;交接对照见本文 §3.4。 |
| image-display.md / 控件文档 | 显示消费者;编辑器预览与 LastRun 图可订阅观察钩子。 |
| output-inspection.md | 调试观察可对照「最近输出」心智; T10 集合事件;执行不得依赖观察存储;观察永不驱动 Run。 |
| examples-scenarios.md | 成体系示例场景目录(S01–S08; R6 S09a–d; Arden · R9–R12 S10–S13)与骨架;对齐本文 §3.4 / §6.6 / §7.4.6 / §7.6 / §9 / §10.1;契约片段仍见 examples.md。 |
| test-contracts.md | R4 可勾选测试契约表(全文):前置·动作·期望 code/状态·章节;§10.1 摘要回链至此;含 H1–H10 / Diagnostic / S01–S13 映射( R6 S09; Arden · R9–R12 TC-OUT/ACQ/SESS/MEM/DISP; Arden · R13 §10.1 T13 索引对齐)。 |
| host-integration.md | R7 宿主集成契约:headless / C# 显示宿主同核;示意 C 导出;停止矩阵宿主侧分派;回链本节 §7 / S07 / TC-CTL。不改本文 v1.9 语义与 §7.4.6 码表。 |
| glossary.md | R8 术语终检:核心术语 + Arden 回合 vs T15 风险撞号 + 禁混词;导读入口见 overview。不改本文 v1.9。 |
| examples.md | 成像/显示契约示例(C++ 核心 / C# ImageDisplay);文首链到场景规划。 |
调研稿 mvdocs-visionpro-toolblock-research.md | 外部心智参考;正式产品以本文为准。调研中「内核跟 DAG」的旧建议已被本轮拍板否定。 |
9.0 场景规划索引
成体系的可测场景目录(目的 / 前置 / 步骤骨架 / 期望断言 / 文档回链)见专文,避免把本节心智图与 §9.4 试想撑成验收剧本:
→ examples-scenarios.md(S01–S08; R6 S09a–d; Arden · R9–R12 S10–S13)
| 场景 | 对应本节 / 验收 |
|---|---|
| S01 单工具 Run | §7.4 / §9.4 最小闭环 |
| S02 多工具顺序链 | §4.1 / §9.1 列表序 |
| S03 fake 检测流水线 | §7.6.3 / §9.1 headless |
S04 .avtf Load/Validate 失败 | §6.5–6.6 / §7.4.6 |
| S05 句柄交接 + C# 显示 | §3.4;显示仅 C# |
| S06 Cancel / abort / Busy | §4.3 / §7.4.3 / §7.4.6 |
| S07 headless vs 显示宿主 | §7.2–7.3; host-integration.md( R7) |
| S08 Tool 作者 + fake 注册 | §7.6 |
| S09a 嵌套 + Encapsulation | §9.3 / §7.4.6 / §10.1 |
| S09b SourceDisabled / InvalidBinding | §4.3 / §7.4.6 |
| S09c IoFailure / 停相机≠Cancel | §7.4.3 / §7.4.6 |
| S09d 两流终端串联(可选) | §9.2 |
| S10 输出观察集合事件 | output-inspection.md T10;TC-OUT-*( Arden · R9) |
| S11 采集/会话停流 | camera-acquisition / session-lifecycle;TC-ACQ-* / TC-SESS-*( Arden · R10) |
| S12 存储/pin/归还 | memory-management;TC-MEM-*( Arden · R11) |
| S13 显示集成(仅 C#) | image-display;TC-DISP-*( Arden · R12;≠ T15 R10) |
与 §10.1 / T13 / test-contracts.md(TC-* ↔ S01–S13; Arden · R13 索引对齐)勾选对照;成像契约片段见 examples.md。
9.1 单工具流水线
ToolFlow "InspectA"
Inputs: (none; 内部 Acq 自取) 或 InputImage
Tools (序):
1. AcqFifoTool → OutputImage
2. PMAlignTool ← InputImage ← (1).OutputImage
→ Pose
3. FixtureTool ← InputImage ← (1).OutputImage
← Pose ← (2).Pose
→ OutputImage (同像素, Fixture 空间)
4. BlobTool ← InputImage ← (3).OutputImage
← Region in Fixture space
→ Count, Results
Outputs:
OkNg ← script 或判定工具 / 后续钩子;MVP 可用计数工具写 bool
Defects ← (4).Count
宿主:session.Run() 一次 = 一工具流内自上而下跑完。
MVP / headless:可用 §7.6.3 fake 替身等价拼装(如 fake_acq → fake_pm_align → fake_passthrough / fake_constant),无需真实 Acq/PMAlign/相机;句柄交接断言见 §3.4 / §10.1 H1–H10。
9.2 两工具流串联
可测骨架(可选正路径): examples-scenarios.md S09d · 契约
TC-RUN-07。
ToolFlow "Locate"
Outputs: FixturedImage, Pose
ToolFlow "Defect"
Inputs: Image, Pose
Tools: Fixture 或直接测 → Blob/Caliper …
Outputs: OkNg, DefectCount
Job:
tools: [Locate, Defect]
links: Locate.Outputs.FixturedImage → Defect.Inputs.Image
Locate.Outputs.Pose → Defect.Inputs.Pose
外层也可以是 ToolGroup/Job 级有序列表;同样自上而下,先 Locate.Run 再 Defect.Run。
9.3 嵌套工具流
可测骨架:合法嵌套 +
Encapsulation→ examples-scenarios.md S09a · 契约TC-RUN-06/TC-LNK-04。
ToolFlow "OuterMeasure"
Tools:
1. ToolFlow "InnerLocate" # 采集+定位+Fixture,对外只露 FixturedImage
2. CaliperTool / BlobTool ← Image ← InnerLocate.Outputs.FixturedImage
Outputs: Width, OkNg
排障时只打开 InnerLocate 看定位链;外层保持干净。
9.4 C++ 用法试想(设计示意,非已定 API)
下列为 试想中的宿主写法,用来钉住「创建 → 组流水线 → Run → 取结果」的手感。类型名、头文件、命名空间均可在实现期调整;不是已发布头文件,也不承诺与 VisionPro / MPF 同形。
目标手感(最小闭环):
- 创建(或加载)一个 ToolFlow
- AddTool 按序放入原子 Tool(或嵌套 ToolFlow)
- 建 Terminal / Link(或用便捷 API 连属性)
- Run
- 从 Outputs(或工具属性)取结果
// 示意、非已发布 ABI(与 §7.4 / cpp-headers.md 一致)
#include <vyntek/toolflow/toolflow.hpp>
#include <vyntek/toolflow/registry.hpp>
#include <vyntek/toolflow/run_session.hpp>
#include <vyntek/imaging/handle.hpp>
using namespace vyntek::toolflow;
int main() {
// 1) 注册表:按类型 id 创建工具(真实项目里通常进程启动时注册一遍)
ToolRegistry registry;
register_builtin_tools(registry); // Acq、PMAlign、Fixture、Blob…
// 2) 创建空工具流(也可 ToolFlow::Load("inspect.avtf"))
auto flow = ToolFlow::Create("InspectA");
// 3) 按列表序添加工具(返回非拥有观察指针 / id,所有权在 flow 内)
auto* acq = flow->AddTool(registry.Create("acq_fifo", "Acq"));
auto* align = flow->AddTool(registry.Create("pm_align", "Align"));
auto* fix = flow->AddTool(registry.Create("fixture", "Fixture"));
auto* blob = flow->AddTool(registry.Create("blob", "Blob"));
// 4) 对外终端(块级契约);内部也可先不建终端、只链工具属性
flow->AddInput ("InputImage", TypeDesc{Type::ImageGray8}); // 可选:由宿主注入
flow->AddOutput("DefectCount", TypeDesc{Type::Int32});
flow->AddOutput("OkNg", TypeDesc{Type::Bool});
// 5) 链接:源 → 目标(校验类型 + 「源在列表中须先于目标」)
flow->Link(acq, "OutputImage", align, "InputImage");
flow->Link(acq, "OutputImage", fix, "InputImage");
flow->Link(align,"Pose", fix, "Pose");
flow->Link(fix, "OutputImage", blob, "InputImage");
flow->Link(blob, "Count", flow->Output("DefectCount"));
// OkNg:MVP 可用简单阈值或后续判定工具;示意略
// 校验失败路径:也可 Validate(diags) 收集 Diagnostic 后返回
if (auto vr = flow->Validate(); !vr) {
// vr.error() / diagnostics:TypeMismatch、OrderViolation…
return 1;
}
// 6) 运行会话:同一 session 同时只允许一次 Run
RunSession session(flow.get());
// 若使用 InputImage 终端:
// session.SetInput("InputImage", image_handle);
RunResult r = session.Run();
if (!r.ok) {
// r.failed_tool_id / r.message / 已跑工具的 LastRun 可供诊断
return 1;
}
// 7) 取结果:工具流 Outputs 或工具属性
int count = session.GetOutput<int>("DefectCount");
// bool ok = session.GetOutput<bool>("OkNg");
// auto img = blob->GetProperty<vyntek::imaging::PixelImageHandle>("OutputImage"); // LastRun 观察
return count >= 0 ? 0 : 2;
}
嵌套工具流(示意):内层先组好再 AddTool 进外层,外层只链内层终端。
auto locate = ToolFlow::Create("InnerLocate");
// … AddTool + Link …
locate->AddOutput("FixturedImage", TypeDesc{Type::ImageGray8});
auto outer = ToolFlow::Create("OuterMeasure");
outer->AddTool(std::move(locate)); // 嵌套,所有权转入 outer
auto* caliper = outer->AddTool(registry.Create("caliper", "Width"));
outer->Link(outer->Tool("InnerLocate")->Output("FixturedImage"),
caliper, "InputImage");
Headless 加载配方(示意):
#include <vyntek/toolflow/serialize.hpp>
auto loaded = ToolFlow::Load("recipes/inspect_a.avtf", registry);
if (!loaded) {
// UnknownTypeId / SchemaTooNew — 严格失败,不静默成功
return 1;
}
RunSession session(loaded->get());
session.SetInput("InputImage", GrabOneFrame()); // ReadOnly 句柄
auto r = session.Run();
if (!r.ok) return 1;
auto n = session.GetOutput<int>("DefectCount");
设计约束提醒(与上文一致):
| 点 | 说明 |
|---|---|
| 所有权 | AddTool 后工具由 ToolFlow 拥有;不要双拥有。 |
| 顺序 | 列表序即执行序;Link 不得违反「源在目标之上」。 |
| 多次采集 | 宿主循环 session.Run(),不要在工具流里做环。 |
| API 形态 | MVP 倾向进程内 C++ 库;C# / 其它 UI 绑同一套( PInvoke 倾向;C++/CLI 非默认),而不是各写执行器。 |
10. 路线
| 阶段 | 交付 | 退出条件(摘要) |
|---|---|---|
| MVP | 核心类型(Tool/ToolGroup/ToolFlow/Terminal/Link);顺序 Run;终端与属性链接;校验;.avtf 读写; headless runner | 见下方 MVP 验收退出条件(可勾选) |
| 编辑 UI | C#(或其它)绑定:工具列表、I/O 页、连线、属性编辑、Run 与 LastRun 图 | 能可视化编辑 §9.2 并保存;与 headless 同一 .avtf |
| 嵌套与组件库 | 嵌套工具流、组件插入、工具注册表完善 | §9.3 可编辑可跑;组件可插入再保存 |
| 脚本 / 高级 | 工具流级脚本钩子、更细失败策略、可选 IPC、并行会话、产线 IO | 单独立项;不阻塞 MVP |
10.1 MVP 验收退出条件(可勾选 / 可测)
下列为 MVP 完成 的退出条件摘要;须能在 无 UI 环境下用单测 + headless 证明。与 §11 清单对齐:清单管「文档口径」,本表管「可执行验收摘要」。
全文可勾选测试契约表(R4) → test-contracts.md 每条 = 前置 · 动作 · 期望
DiagnosticCode/状态 · 对应章节;覆盖注册/AddTool/Link/Validate/顺序 Run/Cancel/失败中止/Busy/Load·SchemaTooNew·UnknownTypeId·MissingRequiredProperty、 H1–H10、fake 注册、 S01–S13 映射(含嵌套/Encapsulation、SourceDisabled、InvalidBinding、IoFailure、停相机≠Cancel; Arden · R9–R12 OUT/ACQ/SESS/MEM/DISP)。§10.1 以下保留摘要勾选;细节与 ID(TC-*)以契约表为准。不写覆盖率 KPI。
勾选约定(勿误勾):下列摘要勾选与 test-contracts.md 的
TC-*表同属一套验收口径,权威在契约表。契约表 line 3 及四份薄摘(tc-acq/tc-sess/tc-out/tc-disp)明确规定「smoke 与 Controls.Tests 不等于本表通过」「外形 ≠ 验收」——验收另需供应商 SDK 实机适配与真实渲染/GPU 预算/图形 context 重建等条件。仓内tests/orchestration_contracts.cpp、tests/smoke_headers.cpp与Vyntek.Controls.Tests已覆盖对应单测断言,但不构成勾选依据,故本清单保持全[ ]。覆盖情况与验收仍缺条件见 test-contracts.md 文末《单测覆盖度记录》。
对象模型与注册
-
ToolRegistry可注册/创建至少一组内置示意工具(或测试替身 Tool),AddTool后所有权在 ToolFlow 内。 - 可定义 Inputs/Outputs 终端(名称、类型、稳定 id);嵌套 ToolFlow 仅通过终端对外链接(封装单测;场景 S09a)。
- (T7)
register_fake_tools(或等价)可注册 §7.6.3 清单中至少:fake_acq、fake_pm_align、fake_passthrough、fake_fail_once;重复type_id注册被拒绝。 - (T7) 未知
type_id→UnknownTypeId;fake 与 builtin 共用同一 registry 契约(Create/Load)。
链接与校验
-
Link:类型兼容通过、不兼容拒绝;图像跨格式拒绝。 - 列表序:源在目标之下的链接被拒绝或
Validate失败(无环 / 序一致)。 - 指向禁用工具:Run 时符合 §4.3 / §7.4.2(MVP:源禁用 → 下游严格失败);诊断含
SourceDisabled(§7.4.6);场景 S09b。 -
Validate/Run/Load失败路径可断言DiagnosticCode(至少覆盖 TypeMismatch、OrderViolation、UnknownTypeId、SchemaTooNew、Busy、 MissingRequiredProperty)。 - (T8) 已知
type_id、缺必填配置属性 →Validate失败(MissingRequiredProperty);无静默默认值使校验通过。
执行
- headless 加载或构建后跑通 §9.1 等价流水线(可用测试替身代替真实 Acq/PMAlign),
Run后 Outputs 可读且值符合链接赋值。 -
abort_on_tool_failure=true:中途失败则后续子工具不 Run,RunResult标识失败工具。 - 同一
RunSession并发第二次Run→ 拒绝(有单测;不做排队)。 - 取消:
RequestCancel后不再启动后续子工具(协作式;码Cancelled);不测工具内抢占(T9)。 - (T9) 宿主停相机 不自动 Cancel 在途 Run;显示关闭 不停编排;停止矩阵三层可单测/文档对照(§7.4.3);场景 S09c。
- 连续两次
Run为两次独立执行;无环边;第二次覆盖上次 Outputs。
图像句柄交接(T6 · 对齐 §3.4 / §3.2)
- H1–H2:Complete / CompleteAndOpenRead 提交语义可测;未 Complete 不得作为下游 ReadOnly 输入。
- H3–H4:ShareImage / ReadOnly Share →
Access=ReadOnly;对下游输入OpenWrite立即拒绝。 - H5:ReadWrite 根上的 Share 不能冒充已稳定发布的下游输入。
- H6:链接赋值写入下游输入后句柄为 ReadOnly;链接不是 DAG 调度(序仍为列表 Run)。
- H7:已交下游的稳定结果在下游 Run 期间不可被生产者改写。
- H8:Clone 得新 BufferId 可写输出;源 ReadOnly 不变;再经稳定发布后可链接。
- H9–H10:ReadOnly ROI 不可写;失败/Invalid 输出不可假成功交给下游。
序列化
- 保存 → 再加载 → 结构(序、终端、链接、禁用、参数)一致;可再次 headless
Run。 - 含
schema_version;过高版本拒绝加载(SchemaTooNew)。 - (T8) 缺 / 坏
component_version不拦 Load;与schema_version门禁独立。 - 未知工具 type id → 严格失败;不静默当成功。
- 未知可选字段加载后仍保留在文档模型中(可告警、不剥掉)。
- 运行时态(LastRun 结果、注入值)默认不进配方;金样图不默认内嵌。
非目标门禁(MVP 不得误交付)
- 无脚本引擎、无 MPF 合入、无 DAG 拓扑调度器、无
.vpp/MPF 文件加载、无完整产线 IO 协议。
§10.1 ↔ 测试生命周期映射(T13 · 薄表 · Arden · R13 索引对齐)
验收勾选 ↔ 建议单测/集成测阶段。生命周期用语对齐工程文「注册→建流→Link/Validate→Run→Cancel/失败中止→Load 复载…」;可测、可勾。全文契约行(
TC-*)与 S01–S13 映射见 test-contracts.md(含 Arden · R9–R12 OUT/ACQ/SESS/MEM/DISP);本节为阶段索引。 Arden · R13 仅对齐索引/回链,不改 Diagnostic 码表 / T9 / v1.9 语义。
| 建议测试阶段 | 主要对准的 §10.1 勾选 | 关键码 / 节 | 契约表 |
|---|---|---|---|
| 注册 | 对象模型与注册:Registry / AddTool / fake 注册(T7) | UnknownTypeId;§7.6 | test-contracts §1 |
| 建流 | 终端定义、嵌套封装、列表序结构 | §3 / §7.4.1 | 同上 + §3 |
| Link / Validate | 链接与校验:类型/序/禁用源;缺必填属性(T8) | TypeMismatch / OrderViolation / SourceDisabled / MissingRequiredProperty | §2 |
| Run | 执行:headless §9.1、失败中止、重入 Busy、连续两次 Run | Aborted / Busy;§4 / §7.4.3 | §3 |
| Cancel / 失败中止 | 协作式取消;停止矩阵三层不混(T9) | Cancelled;§7.4.3 矩阵 | §4 |
| Load 复载 | 序列化:save→load 一致;过高 schema;未知 type;component_version 不拦(T8) | SchemaTooNew / UnknownTypeId;§6.5–6.6 | §5 |
| 句柄 H1–H10 | 图像句柄交接(T6) | §3.4 | §6 |
| 输出观察(T10) | 集合事件 / 空快照 / 失败保留;观察不驱动 Run | output-inspection;§7.4.3 观察侧 | §7 TC-OUT-*( Arden · R9 / S10) |
| 采集接入(阶段 D) | 持帧/Copy、TryPostOwned、归还泵、Latest、回调不堵 | camera-acquisition | §8 TC-ACQ-*( Arden · R10 / S11) |
| 会话停流(阶段 D) | 排空结束条件、外保留、超时续清、两端都停须显式 | session-lifecycle;§7.4.3 T9 | §9 TC-SESS-*( Arden · R10 / S11) |
| 存储/pin/归还(阶段 B) | 工厂接管、最后引用、pin、授予/取消、Invalid 恢复 | memory-management | §10 TC-MEM-*( Arden · R11 / S12) |
| 显示集成(阶段 F) | 登记≠授予、Share 不接管、回执四信号、失败保留、显示只观察 | image-display(仅 C#) | §11 TC-DISP-*( Arden · R12 / S13;≠ T15 R10) |
| fake 注册与 headless | T7 fake 清单 + §9.1 等价流水线 | §7.6.3 | §1 + §3 + §12 场景映射 S01–S13 |
10.2 MVP 明确不做 / 归后续
| 不做(MVP) | 归入 |
|---|---|
| 脚本钩子 / 脚本语言 | 脚本 / 高级 |
| 编辑 UI、属性网格、画布 | 编辑 UI |
| 与 MPF_ToolBlock 合入或迁移 | 永不作为本路径目标 |
| DAG 全局调度引擎 | 非目标(已废止) |
VisionPro .vpp / MPF 序列化兼容 | 非目标 |
| 完整产线 IO(PLC/现场总线等) | 脚本 / 高级或专文 |
| 进程外 IPC 服务化 | 演进选项(§7.2) |
| 占位工具只读打开未知类型 | 后续可选 |
同 RunSession 排队 | 后续(MVP 重入拒绝 / Busy) |
| 工具内抢占取消 | 后议(MVP 仅协作式) |
| 稳定 ABI / 符号版本承诺 | 后续(发布期;MVP 示意头≠已发布,见 T14) |
| ImageDisplay 写成 C++ UI | 关闭(仅 C# 经绑定) |
宿主形态对照与示意 C 导出(不做稳定 ABI / 不做编辑 UI / 不扩脚本)见 host-integration.md( R7);上表脚本等项仍仅占位,不因 R7 扩写。
开放风险只登记、不扩做:见 validation-and-roadmap.md「开放风险登记表」( T15)。
11. 自检清单(入口是否醒目)
- 标题下声明:本轮产品模型优先;心智借用 Tool /(原 CogToolBlock);除心智外与 VP 完全不同;C++ 自研重建、多 UI / 无 UI。
- 不接入 MPF_ToolBlock;文首写明 旧 DAG 文已废止,编排专题目径为 Tool / ToolFlow。
- 产品类型名统一为 ToolFlow(容器);不再用 ToolBlock / 工具块作本方案类型名;总称用「Tool / ToolFlow 基建」等,避免与类型名打架。
- 执行模型写明 自上而下顺序 Run,连续采集 多次 Run 非环。
- 术语自有命名,标明心智对照但不宣称 API 兼容、不以复刻 VP 为目标。
- 服务边界:C++ 核心 vs C# 显示 vs headless 共用。
- 与成像文档可引用;旧 DAG 文仅废止短文,不作对照权威。
- §7.4 公共 API 示意(创建/AddTool/Link/Run/Load
.avtf/校验失败)并标明非已发布 ABI;与 §9.4 手感一致。 - §7.5 / cpp-headers.md 头文件与模块边界;公开头与
detail/分界;成像只依赖公开句柄头。 - §9.4 示例
#include与公开头表一致(/);类型名 TypeDesc /PixelImageHandle全文统一。 - MVP 绑定倾向 PInvoke;C++/CLI 非默认;ImageDisplay 仅 C#;成像权威 C++
vyntek::imaging。 - §6.3–6.6 配方格式(
.avtf)、持久化字段分层、版本与未知类型;§6.6 嵌套 schema + 正反样例;assets[](kind:"golden_image"+path;可选role/tool_id);像素不嵌入;component_version可选且不拦 Load;不承诺 MPF/VP 文件兼容。 - §5(T3) TypeDesc / Value / PropertyBag MVP;属性路径
tool_id.prop[.sub…](无表达式);可链接表 + 数值提升白名单 + 图像精确匹配;几何/ROI/Fixture 最小字段(非第二图模型);与 cpp-headersTypeDesc同名。 - §7.4.6(T4) DiagnosticCode 表与 severity;Validate/Run/Load 返回结构化 Diagnostic 列表;对齐 §7.4 / §10.1。
- §10.1 MVP 退出条件可勾选、可测,并与「明确不做」区分。
- §3.4(T6) PixelImageHandle 交接对照表;每条可测断言;回链 §10.1;对齐 image-model/memory/§3.2(ReadOnly 发布、已交下游不得改写、链接≠DAG)。
- §7.6(T7) Tool 作者最小契约、Registry 约定、MVP fake 替身清单(FakeAcq/FakePMAlign/Passthrough/FailOnce 等);供 §9.1 / §10.1 headless。
- §6.5 / §7.4.6(T8)
schema_version(过高→LoadSchemaTooNew)与component_version(可选、不拦 Load)分轨;已知 type 缺必填属性 → ValidateMissingRequiredProperty(无静默默认)。 - §7.4.3(T9) 停止矩阵:Cancel=协作式/
Cancelled;宿主停相机≠自动 Cancel Run;显示只观察不停编排;链 session-lifecycle / camera / display。 - 输出观察集合事件(T10) 见 output-inspection.md:
EmptySnapshot/Unavailable/Evicted;失败保留上次成功;观察永不驱动 Run;对外 ToolId。 - C# 适配(T11) 见 csharp-binding.md:MVP=PInvoke;C++/CLI 非默认;opaque handle + Release/Dispose;ImageDisplay 仅 C#。
- §10.1 映射(T13) 验收勾选 ↔ 测试生命周期薄表(注册→…→fake; Arden · R13 增 OUT/ACQ/SESS/MEM/DISP + S10–S13 索引)。
- 测试契约表(R4) test-contracts.md:§10.1 / H1–H10 / Diagnostic / S01–S13 收成可勾选契约;§10.1 保留摘要并回链。
- 场景负路径与嵌套(R6) examples-scenarios.md S09a–d:嵌套+Encapsulation、SourceDisabled、InvalidBinding、IoFailure、停相机≠自动 Cancel、可选 §9.2 两流串联;契约映射已同步;不改本文 v1.9 语义与 §7.4.6 码表。
- 打包/库名(T14) 见 cpp-headers.md:
libvyntek_toolflow/libvyntek_imaging;头树/;MVP 无稳定 ABI。 - 开放风险登记(T15) 见 validation-and-roadmap.md:只登记不扩做;与 §10.2 不做清单一致。
- 宿主集成(R7) 见 host-integration.md:headless / C# 显示宿主同核;示意 C 导出非稳定 ABI;停止矩阵宿主侧分派;不改本文 v1.9 语义与 §7.4.6 码表。
- 导读与术语终检(R8) 见 overview.md 角色化路径 + glossary.md:术语终检表;Arden R4–R8 vs T15 风险 R1–R10 撞号;S07↔host-integration 薄回链;保持本文 v1.9,不改语义与码表。
- 本批横切收束(Arden · R13) §10.1 T13 阶段索引对齐 test-contracts OUT/ACQ/SESS/MEM/DISP + S10–S13;过时 S01–S09 索引已更新;不改 Diagnostic / T9 / v1.9 语义。
文档状态:设计草案 v1.9(2026-10-21,Asia/Shanghai)。T3–T9; T10 输出集合事件; T11 C#/PInvoke;T12/T13; T14 打包/库名; T15 开放风险登记。 T16→T15 迭代清单文档侧已收口。 R4 测试契约表见 test-contracts.md(§10.1 摘要回链)。 R7 宿主集成见 host-integration.md。 R8 导读/术语终检见 overview.md / glossary.md。 Arden · R13 本批横切收束:§10.1 T13 索引对齐 OUT/ACQ/SESS/MEM/DISP + S10–S13(保持 v1.9;不改本文语义与 Diagnostic 码表 / T9)。Alive 拍板:.avtf 顶层名锁定、未知工具严格失败、同 session 重入拒绝、PInvoke 默认、component_version 不拦 Load、Cancel 协作式、停相机≠自动 Cancel Run、显示只观察、空快照必发事件、MVP 无稳定 ABI、废止 DAG 文仅指向本文。实施以代码与验收为准,不以本文完成度为功能完成。
本仓实现对拍
| 规格类型 | 头 | 现状 |
|---|---|---|
ToolFlow | toolflow/toolflow.hpp | Create / AddTool / Link / Validate / MakeRunSession 已实现;.avtf Load/Save 已实现(JSON、schema 门禁、未知 type_id、IoFailure、未知字段保留;TC-SER-01…07 覆盖) |
RunSession | toolflow/run_session.hpp | Run / RequestCancel 已实现;GetOutput 做标量投影(图像句柄故空串),≠ KeepLatest |
Tool / Terminal / Link / ToolRegistry | 同目录 | 外形在;RegisterBuiltinTools 只有 passthrough 骨架(真实算法仍 stub,属 Reed 职责);fake::register_fake_tools 另有 8 个测试替身 |
Diagnostic | diagnostic.hpp | 编排码表;不是资源快照 |
| 观察 | output_inspector.hpp | T10 外形在;不驱动 Run |
| 示例 | examples/headless_run.cpp | 公开头 headless |
勾选 TC-RUN / TC-LNK / TC-SER 未迁全文、未验收。