Vyntek
规格机器视觉规格

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 为目标。

本方案用语英文心智对照(仅概念)职责(一句话)
工具ToolVision Tool可独立 Run 的原子视觉单元;暴露属性(输入图、ROI、结果等)。
算子(可选)OperatorOperator工具内部算法实现;对外经 Tool 属性访问。MVP 可不单独暴露。
工具组(可选内部)ToolGroupCogToolGroup工具的有序集合;Run = 自上而下依次 Run 子项;组内可建链接。可作内部基类;产品对外主推 Tool + ToolFlow。
工具流ToolFlow(原 CogToolBlock 容器心智)可复用容器;额外暴露 Inputs / Outputs 终端;产品主容器类型名(勿再称 ToolBlock / 工具块)。
终端 / 针脚Terminal / PinTerminal / Pin工具流边界上命名、类型化的输入或输出端口;流外只连终端。
链接 / 绑定Link / BindingData link / DataBindings源属性或终端 → 兼容目标属性或终端;执行时传值/赋引用。
作业 / 运行会话Job / RunSessionJob / 一次 Run一次检测或一次完整 Run 的边界(含顶层工具流、输入注入、状态与结果)。
禁用DisabledDisabledTools本次 Run 跳过的工具;不参与执行,链接策略见执行模型。
嵌套NestedNested ToolGroup / ToolFlow工具流/组内再含工具流/组;内层只通过自身终端或公开属性对外可见。
脚本钩子(后续)Script HookSimple/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。
ToolFlowToolGroup + 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 文已废止,不以之为权威):

  1. 工具流拥有子工具:ToolFlow/ToolGroup 拥有 tools[] 的生命周期;移除或销毁工具流时销毁子树(除非显式「抽出」到另一所有者,MVP 不做共享所有权)。
  2. 链接不拥有数据:Link 只描述赋值关系;值落在属性或终端上。
  3. 图像值有明确句柄语义(与 image-model.md / memory-management.md 用语对齐):属性/终端持有成像基建句柄;交给下游(经链接赋值写入目标输入)前须 稳定发布为 Access=ReadOnly(典型路径:Complete / CompleteAndOpenRead / ShareImage 等,见成像专题)。已交下游的输入句柄不得再改写;工具不得在 Run 结束后继续写已交给下游的稳定结果。链接赋值只传句柄/值,不是 DAG 边调度。细节实现复用成像包契约,编排模型仍以本文顺序 Run 为准。交接路径与可测断言见 §3.4(T6)。
  4. 一次 Run 的结果可见性:Run 返回后,Outputs 终端与工具输出属性可读,直到下次 Run 覆盖或工具流被重置;UI「最近结果」是观察层,不是第二次执行的隐式输入源。
  5. 线程: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*)
H1WritableView::Complete工具写完自有输出后提交;再 OpenRead→ShareImage(或等价)得到可交接句柄写权限结束;Generation 推进;内容有效后可读Complete 后同一 writer 上再 Complete/GetPixelRow → 失败(Disposed/InvalidOperation);未 Complete 就 Dispose → 内容 Invalid,不得作为下游输入[ ] H1
H2WritableView::CompleteAndOpenRead提交并在同一原子边界取得读视图,再 ShareImage 写入输出属性原子:提交与读授予之间不能插入另一写者;返回 ReadOnlyView(及后续 ReadOnly 句柄)并发/串行探测:CompleteAndOpenRead 完成前无第二写者获授;返回 view 的 version() 与提交代次一致;由该 view ShareImage() 得到 access()==ReadOnly[ ] H2
H3ReadOnlyView::ShareImage已有稳定读权限时发布独立 ReadOnly 句柄供链接/观察/显示Access=ReadOnly;同 BufferId/内容代次;不复制像素;不消费原 viewShareImage 句柄 OpenWrite → NotSupported(或等价);原 view Dispose 后,已 ShareImage 的句柄在仍持有读引用期间仍可读;字节未因 ShareImage 而复制(同 BufferId)[ ] H3
H4PixelImageHandle::Share(源已是 ReadOnly)多下游/观察者各持一份 ReadOnly 句柄;链接可赋 Share 结果仍为 ReadOnly;共用稳定内容;各句柄独立关闭Share 后两句柄皆 access()==ReadOnly;任一关闭不影响另一的 OpenRead;对 Share 结果 OpenWrite 立即拒绝[ ] H4
H5PixelImageHandle::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
H8Clone 后可写输入为 ReadOnly,算法需改像素:显式 Clone → 新 ReadWrite → OpenWrite → Complete(…) → 再 ReadOnly 发布新 BufferId;Generation 从 1;已初始化可写;格式相同(非格式转换)Clone 结果 access() 允许 OpenWrite;buffer_id() ≠ 源;源 ReadOnly 仍不可写;Clone 失败不留下半份可交接输出[ ] H8
H9ROI 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.nameflow.Inputs.InputImage
工具流输出终端flow.Outputs.nameflow.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_idTypeDesc 可带 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 → Load UnknownTypeId」分轨。
// 示意
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精确匹配(含四图像格式各自精确)
Int32Int64 / Float64见提升白名单
Int64Float64
Float32Float64
Int32Float32允许;注意精度
图像 Image*仅同格式跨格式 → 拒绝;须显式转换工具
Enum同 enum_id 的 Enumenum_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 语义链(非图模型)

  1. 定位工具输出 Pose2d / Affine2d;
  2. Fixture 工具输出 Fixture(空间名 + 变换),在图像坐标解释上附着新空间;
  3. 下游工具的 Region 落在 Fixture 空间;输入图像句柄常仍为原图像(同像素,不同空间解释)。

5.5.2 最小字段(MVP)

Type最小字段(示意)说明
Point2dx: Float64, y: Float64像素或当前空间坐标
Line2dx0,y0,x1,y1 或 point + angle(实现择一,文档与工具登记表固定)不做隐式极坐标双表示并存
Circle2dcx, cy, r
Rect2dx, y, width, height(轴对齐)ROI 常用;旋转矩形 MVP 不做或归工具私有扩展
Affine2dm00,m01,m02,m10,m11,m12(行优先 2×3)
Pose2dx, y, angle_rad与 Affine2d 可经工具转换, Link 不自动转换
Regionspace: String(可选,空=图像默认空间), `shape: Rect2d…`
Fixturespace_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 过高LoadSchemaTooNew;拒绝
缺 / 坏 component_versionLoad通过(可 Warning);与 schema 无关
type_id 未注册Load / CreateUnknownTypeId;严格失败
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_versionint是文档 schema 主版本(T8);过高 → Load SchemaTooNew(§7.4.6);与 component_version 独立
kindstring是固定 "toolflow"(MVP)
metaobject否name / created / author 等;未知子字段保留
meta.component_versionstring否可选;仅信息/展示(T8)。与 schema_version 独立; Load 不因缺失或无法解析而失败
flowobject是根 ToolFlow 本体
flow.idstring否稳定 id;缺省可由加载器生成
flow.namestring否显示名
flow.abort_on_tool_failurebool否默认 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" / 缺 flowLoad 失败(格式错误 / 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.PropEncapsulation
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});
能力示意表面校验(失败则拒绝建立或 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):

  1. Cancel Run = 协作式 + 不再启动后续 Tool(码 Cancelled);不做工具内抢占进 MVP。
  2. 宿主停相机 ≠ 自动 Cancel 已在跑的 Run(两层写清;宿主若要两端都停,须显式既停采集又 RequestCancel)。
  3. 显示只观察、不停编排(也不停相机)。

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/日志按码分支
severityError:阻止成功完成该次 Validate/Run/Load; Warning:可继续但须可见; Info:提示
message / 定位字段便于编辑器标红与 headless 日志;定位字段可空
# DiagnosticCode 表(MVP)
Code典型 severity出现于说明
TypeMismatchErrorValidate / Link / Run 拉值 / PropertyBag::Set含数值提升白名单外、图像跨格式、Enum enum_id 不同
OrderViolationErrorValidate / Link子工具间源不在目标列表之上
EncapsulationErrorValidate / Link外层绑内层私有工具属性
BusyErrorRun同 session 已有 Run 在途;拒绝第二次
UnknownTypeIdErrorLoad / CreateMVP 严格失败;无占位成功
SchemaTooNewErrorLoad过高 schema_version
IoFailureErrorLoad / Save / 资产打开路径不可读、截断、权限等
SourceDisabledError(MVP)Run 拉值源在 disabled;与 §4.3 严格策略一致
InvalidBindingErrorValidate / Load / Run端点缺失、方向错误、残链
MissingRequiredPropertyErrorValidate(亦可 Run 前强制)已知 type_id 缺必填配置属性;禁止静默默认值糊弄(T8);与 UnknownTypeId 分轨
AbortedErrorRun子工具失败且 abort_on_tool_failure;后续未跑
CancelledError 或 Warning*Run协作式取消:当前工具返回后不再启动后续;*若部分结果仍交出,可用 Warning,但 RunResult.ok==false(T9)

未列出的内部码不得替代上表语义;新增对外码须改本文。

# 返回约定
API约定
Validate / Validate(diags)填满 vector ;存在任一 Error → 校验失败(返回 false / error)
Run / RunResultRunResult 含 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/ 不对宿主暴露
UIC++ 不 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;生产默认拒绝)
Createregistry.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_acqFakeAcq无外部设备;每次 Run 产出固定或序号可配置的 Gray8 ReadOnly OutputImage(工厂 Copy/预置句柄)§9.1 流水线头;H3/H4/H6
fake_pm_alignFakePMAlign读 InputImage(ReadOnly);写 Pose(标量/几何最小字段);可选 OutputImage=Share(输入)§9.1 第二级;链接类型与序
fake_passthroughPassthroughInputImage → OutputImage,仅 Share/ShareImage,不改像素H4/H6/H7;证明链接非拷贝调度
fake_clone_writeCloneThenWrite对输入 Clone → OpenWrite 填充常数 → CompleteAndOpenRead → ShareImage 输出H2/H8
fake_fail_onceFailOnce第 1 次 Run 失败,之后成功(可配置 N)abort_on_tool_failure;H10
fake_fail_alwaysFailAlways恒失败并给出可断言诊断失败中止 / Diagnostic
fake_constantConstantScalar参数决定,输出固定 int32/boolOutputs 终端;非图像链接
fake_cancel_checkCancelCheckRun 内轮询取消令牌,合作退出§10.1 取消条(协作式)
fake_busy_probeBusyProbe(可选)故意慢 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.mdC++ 头文件与模块边界:公开/内部头、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.mdR4 可勾选测试契约表(全文):前置·动作·期望 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.mdR7 宿主集成契约:headless / C# 显示宿主同核;示意 C 导出;停止矩阵宿主侧分派;回链本节 §7 / S07 / TC-CTL。不改本文 v1.9 语义与 §7.4.6 码表。
glossary.mdR8 术语终检:核心术语 + 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 同形。

目标手感(最小闭环):

  1. 创建(或加载)一个 ToolFlow
  2. AddTool 按序放入原子 Tool(或嵌套 ToolFlow)
  3. 建 Terminal / Link(或用便捷 API 连属性)
  4. Run
  5. 从 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 验收退出条件(可勾选)
编辑 UIC#(或其它)绑定:工具列表、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.6test-contracts §1
建流终端定义、嵌套封装、列表序结构§3 / §7.4.1同上 + §3
Link / Validate链接与校验:类型/序/禁用源;缺必填属性(T8)TypeMismatch / OrderViolation / SourceDisabled / MissingRequiredProperty§2
Run执行:headless §9.1、失败中止、重入 Busy、连续两次 RunAborted / 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)集合事件 / 空快照 / 失败保留;观察不驱动 Runoutput-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 注册与 headlessT7 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-headers TypeDesc 同名。
  • §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(过高→Load SchemaTooNew)与 component_version(可选、不拦 Load)分轨;已知 type 缺必填属性 → Validate MissingRequiredProperty(无静默默认)。
  • §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 文仅指向本文。实施以代码与验收为准,不以本文完成度为功能完成。


本仓实现对拍

规格类型头现状
ToolFlowtoolflow/toolflow.hppCreate / AddTool / Link / Validate / MakeRunSession 已实现;.avtf Load/Save 已实现(JSON、schema 门禁、未知 type_id、IoFailure、未知字段保留;TC-SER-01…07 覆盖)
RunSessiontoolflow/run_session.hppRun / RequestCancel 已实现;GetOutput 做标量投影(图像句柄故空串),≠ KeepLatest
Tool / Terminal / Link / ToolRegistry同目录外形在;RegisterBuiltinTools 只有 passthrough 骨架(真实算法仍 stub,属 Reed 职责);fake::register_fake_tools 另有 8 个测试替身
Diagnosticdiagnostic.hpp编排码表;不是资源快照
观察output_inspector.hppT10 外形在;不驱动 Run
示例examples/headless_run.cpp公开头 headless

勾选 TC-RUN / TC-LNK / TC-SER 未迁全文、未验收。

On this page

Tool / ToolFlow 基建设计1.1 目标1.2 非目标1.3 设计立场(一句话)2. 术语3.1 核心类型与职责3.2 所有权与生命周期原则3.3 ToolFlow:对外终端与对内列表3.4 PixelImageHandle 交接对照表(T6 · P0)4.1 主模型:自上而下顺序 Run4.2 链接何时推 / 拉4.3 失败、禁用、嵌套、连续采集4.4 与「流程图 UI」的关系5. 链接与类型系统5.1 端口与属性路径5.2 TypeDesc MVP 子集5.2.1 Type 枚举(MVP 闭集)5.3 Value / PropertyBag5.4.1 可链接类型表(MVP)5.4.2 数值提升白名单(正向;反向禁止)5.4.3 图像格式:精确匹配5.4.4 方向、序与其它5.5 几何 / ROI / Fixture 最小字段5.5.1 语义链(非图模型)5.5.2 最小字段(MVP)6.1 需求(先定需求,不臆造二进制)6.2 非目标(持久化)6.3 文档 / 配方格式目标6.4 必须持久化 vs 运行时态6.5 版本字段、向前兼容、未知工具与缺属性(Alive 拍板 · T8)6.6 .avtf 嵌套 schema(Alive 拍板 · T5)6.6.1 顶层与 flow 嵌套字段6.6.2 正样例(最小合法)6.6.3 assets[](像素不嵌入)6.6.4 反样例(须拒绝或校验失败)7.1 C++ 核心提供什么7.2 进程形态倾向7.3 上层分工7.4 公共 API 表面(示意)7.4.1 创建、注册、AddTool、终端7.4.2 Link 建立与校验7.4.3 Run、RunSession、取消与失败中止# 停止矩阵(RunSession / 采集 / 显示 · Alive 拍板 · T9)7.4.4 只读查询(上次状态与 Outputs)7.4.5 加载 .avtf 与查询(示意)7.4.6 Diagnostic(Alive 拍板 · T4)# 结构# DiagnosticCode 表(MVP)# 返回约定7.5 头文件与模块边界7.6 Tool 作者最小契约与 MVP 测试替身(T7 · P0)7.6.1 作者必须实现什么7.6.2 Registry 注册约定7.6.3 MVP fake 替身清单(headless §9.1 / §10.1)8. 与现有文档关系9.0 场景规划索引9.1 单工具流水线9.2 两工具流串联9.3 嵌套工具流9.4 C++ 用法试想(设计示意,非已定 API)10. 路线10.1 MVP 验收退出条件(可勾选 / 可测)对象模型与注册链接与校验执行图像句柄交接(T6 · 对齐 §3.4 / §3.2)序列化非目标门禁(MVP 不得误交付)§10.1 ↔ 测试生命周期映射(T13 · 薄表 · Arden · R13 索引对齐)10.2 MVP 明确不做 / 归后续11. 自检清单(入口是否醒目)本仓实现对拍