mvdocs

# Tool / ToolFlow 基建设计

本轮产品模型(优先)。目标设计草案,尚未实施。总体边界见设计概述。

醒目声明:心智上借用 VisionPro 的 Tool /(原 CogToolBlock)容器思路,自有命名为 Tool / ToolFlow。除该心智模型外,整体设计与 VisionPro 完全不一样——架构、服务边界、实现、持久化、与 UI 关系等均为自研重建,不以复刻 VisionPro 为设计目标,不承诺任何兼容。基建以 UI 无关的 C++ 底层服务实现;上层可接 C# 编辑/显示、其它 UI,以及 纯编排运行(无 UI)。

明确不接入:现有 MPF_ToolBlock / InsToolBlock 合入方案。
旧 DAG 文已废止:原 dag-dataflow.md(「DAG 图像流转」)已废止,仅为防 404 的短文;勿再作编排对照或历史权威阅读。编排专题目径统一为 Tool / ToolFlow。

关联(成像与显示基建,非编排执行模型):图像模型 · 内存 · 显示 · 输出观察


# 1. 目标与非目标

# 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


# 3. 对象模型(C++ 服务层)

# 3.1 核心类型与职责

类型(示意) 职责
ITool 统一可 Run 单元:身份、名称、启用标志、属性袋、Run(ctx)、上次运行状态。
Tool(原子) 具体算法工具;持有参数与运行时结果属性;可选内部 Operator。
ToolGroup 有序 tools[]、组内 links[]、禁用集合、失败是否中止;Run 按列表序。可选内部基类/无终端有序组;对外主推 ToolFlow。
ToolFlow ToolGroup + inputs / outputs 终端集合;对外契约与持久化复用单元;产品主容器。
Terminal 名称、方向(In/Out)、类型描述、当前值、稳定 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 / memory);工具不得在 Run 结束后继续写已交给下游的稳定结果。细节实现可复用成像包契约,但编排不以 DAG 边模型描述。
  4. 一次 Run 的结果可见性:Run 返回后,Outputs 终端与工具输出属性可读,直到下次 Run 覆盖或工具流被重置;UI「最近结果」是观察层,不是第二次执行的隐式输入源。
  5. 线程:默认假设单会话内 Run 可重入策略由宿主约定;MVP 规定 同一 RunSession 同时只允许一次 Run(可排队或拒绝)。跨会话并行由宿主多开 Session。

# 3.3 ToolFlow:对外终端与对内列表

侧面 内容
对外 仅 Inputs / Outputs 终端集合;工具流作为 ITool 被父级 Run 时,父级只链接这些终端(或工具流的选定公开属性,若有)。
对内 有序工具列表;工具属性 ↔ 工具属性、工具属性 ↔ 本工具流终端 的链接。
编辑心智 打开工具流 = 编辑内部流水线与 I/O 页;关闭后外层只见黑盒针脚。
嵌套 子项可以是另一 ToolFlow;外层链接内层终端,不可直接绑内层私有工具属性(封装边界)。

# 4. 执行模型

# 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_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」的关系


# 5. 链接与类型系统

# 5.1 端口与端点

端点定位:

终端与属性都带 类型描述符(运行时与持久化共用)。

# 5.2 类型类别(MVP)

类别 示例 备注
图像 Gray8 / RGB24 / BGR24 / Depth16 句柄 具体格式对齐 图像模型
标量 bool, int, int64, float, double, string 允许有限数值自动提升(如 int→double)
几何 点、线、圆、矩形、仿射变换、位姿 以工具语义定义结构,不另建「几何图」
枚举 / 状态 RunStatus、OK/NG
区域 / ROI Region 工具语义,不是拓扑节点类型
坐标系 / Fixture 空间名 + 变换 工具语义(如 Fixture 工具输出空间),不是另一套图模型

# 5.3 兼容规则

规则 说明
精确匹配 同类型描述符可连。
数值提升 文档表列白名单(如 int→double);反向默认禁止。
图像格式 同格式可连;跨格式需显式转换工具,禁止静默错格式绑定。
不兼容 编辑期标红/拒绝保存为合法链接;运行期若仍存在则 Run 失败。
方向 只能 源(产出)→ 目标(消耗);禁止双向边;禁止环(在「链接依赖 + 列表序」上校验:列表序必须能解释所有链接,即源工具位于目标工具之上,工具流终端例外规则见下)。

工具流终端与序:

# 5.4 Fixture / Region(强调)

定位 → 夹具 → 测量 是工具链语义,不是第二套编排图:

  1. 定位工具输出位姿/变换;
  2. Fixture 工具在图像坐标树上附着新空间;
  3. 下游工具的 Region 落在 Fixture 空间,输入图常仍为原图像句柄。

文档与 API 用属性表达空间与 ROI,不引入「坐标系节点类型 / Region 图模型」。


# 6. 持久化与复用

# 6.1 需求(先定需求,不臆造二进制)

需求 说明
ToolFlow 是复用单元 可保存为组件/配方片段,再插入其它工具流或作为 Job 根。
完整保存 子工具类型与参数、列表序、禁用集、终端定义、链接、工具流元数据(名称、版本号字段)。
稳定身份 终端与工具实例有持久 id,便于链接在重命名后仍可恢复;显示名可改。
可版本化 文件/文档含 schema_version;加载器可拒绝过高版本。
图像像素默认不进配方 配方存参数与结构;演示图/金样图可作为可选附件引用,不默认嵌入巨大像素。
可读交换格式优先 MVP 倾向 JSON/UBJSON/或等价结构化文本+附件,便于 diff 与审查;是否另定二进制加速格式后期再定。

# 6.2 非目标(持久化)


# 7. 服务边界

# 7.1 C++ 核心提供什么

能力 说明
对象模型 CRUD 创建/删除工具与终端、调序、建删链接、设禁用。
校验 类型兼容、列表序与链接一致性、环检测(按 §5.3)。
执行 RunSession::Run、取消、状态与诊断事件。
注册表 工具类型注册与工厂。
序列化 加载/保存 ToolFlow 文档(§6)。
观察钩子 Run 前后、每工具起止、属性变更通知(供 UI / 日志);不把观察者当执行边。

# 7.2 进程形态倾向

方案 优点 代价 倾向
进程内 C++ 库(libtoolflow) 延迟低、嵌入简单、headless 同链 崩溃隔离弱;语言绑定需谨慎 MVP 首选
本地服务 + IPC 隔离、多语言 UI 易挂接 图像句柄跨进程成本高 作为演进选项;若上 IPC,图像宜共享内存句柄,另文

MVP:先交付可静态/动态链接的 C++ API;C# 通过 P/Invoke 或 C++/CLI 绑定同一库;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  →  Imaging(图像句柄/内存,可 C++ 或经边界)

成像与显示基建文档(image-model / memory / display)继续作为像素与显示契约来源;不把其历史上的「DAG 编排」叙述带回本编排核心。


# 8. 与现有文档关系

文档 关系
overview.md 入口索引:Tool / ToolFlow 为编排专题;旧 DAG 文标为已废止。
image-model.md / memory-management.md 成像与内存基建;工具属性中的图像句柄应对齐其格式与寿命思想。
image-display.md / 控件文档 显示消费者;编辑器预览与 LastRun 图可订阅观察钩子。
output-inspection.md 调试观察可对照「最近输出」心智;执行不得依赖观察存储。
dag-dataflow.md 已废止(防 404 短文)。旧「DAG 图像流转」不再维护;只读图像交接语义已归成像/内存与本文图像句柄段落。
调研稿 mvdocs-visionpro-toolblock-research.md 外部心智参考;正式产品以本文为准。调研中「内核跟 DAG」的旧建议已被本轮拍板否定。

# 9. 示例

# 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() 一次 = 一工具流内自上而下跑完。

# 9.2 两工具流串联

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 嵌套工具流

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(或工具属性)取结果
#include <toolflow/toolflow.hpp>   // 示意

using namespace av::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", Type::ImageGray8);   // 可选:由宿主注入
  flow->AddOutput("DefectCount", Type::Int32);
  flow->AddOutput("OkNg",        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 可用简单阈值或后续判定工具;示意略

  flow->ValidateOrThrow();  // 类型、序、环(按链接+列表序)

  // 6) 运行会话:同一 session 同时只允许一次 Run
  RunSession session(flow);
  // 若使用 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<ImageHandle>("OutputImage"); // LastRun 观察

  return count >= 0 ? 0 : 2;
}

嵌套工具流(示意):内层先组好再 AddTool 进外层,外层只链内层终端。

auto locate = ToolFlow::Create("InnerLocate");
// … AddTool + Link …
locate->AddOutput("FixturedImage", 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 加载配方(示意):

auto flow = ToolFlow::Load("recipes/inspect_a.avtf", registry);
RunSession session(flow);
session.SetInput("InputImage", GrabOneFrame());
auto r = session.Run();
auto n = session.GetOutput<int>("DefectCount");

设计约束提醒(与上文一致):

点 说明
所有权 AddTool 后工具由 ToolFlow 拥有;不要双拥有。
顺序 列表序即执行序;Link 不得违反「源在目标之上」。
多次采集 宿主循环 session.Run(),不要在工具流里做环。
API 形态 MVP 倾向进程内 C++ 库;C# / 其它 UI 绑同一套,而不是各写执行器。

# 10. 路线

阶段 交付 退出条件(摘要)
MVP 核心类型(Tool/ToolGroup/ToolFlow/Terminal/Link);顺序 Run;终端与属性链接;校验;序列化读写;headless runner 加载并 Run 示例工具流 无 UI 跑通 §9.1;失败中止与禁用单测;文档配方可复载
编辑 UI C#(或其它)绑定:工具列表、I/O 页、连线、属性编辑、Run 与 LastRun 图 能可视化编辑 §9.2 并保存;与 headless 同一文件
嵌套与组件库 嵌套工具流、组件插入、工具注册表完善 §9.3 可编辑可跑
脚本 / 高级 工具流级脚本钩子、更细失败策略、可选 IPC 服务化、并行会话策略 单独立项;不阻塞 MVP

# MVP 明确不做


# 11. 自检清单(入口是否醒目)


文档状态:设计草案 v1.1(2026-10-21,Asia/Shanghai)。补充 §9.4 C++ 用法试想。实施以代码与验收为准,不以本文完成度为功能完成。