Vyntek
规格机器视觉规格

示例代码场景规划(Tool / ToolFlow · v1.9)

www 工作副本(2026-10-21 Asia/Shanghai) 双轨:正文迁自 mvdocs examples-scenarios。场景是验收目录,不是已通过的测试。 实现边界:本仓可编译示例只有 examples 的 headless_run(贴近 S07 的无 UI 入口,不是 S07 全文)。tests/smoke_headers.cpp 与 Vyntek.Controls.Tests 覆盖部分骨架。勾选仍空。 关联:toolflow · test-contracts · host-integration · image-display

示例代码场景规划(Tool / ToolFlow · v1.9)

目标设计与示意骨架,非可编译工程。总体边界见设计概述;编排权威见 toolflow.md( v1.9);成像/显示契约片段见 examples.md。

定调(勿回退):Tool / ToolFlow;命名空间 vyntek::toolflow / vyntek::imaging; ToolId; DiagnosticCode。不混 MPF / 旧 DAG(dag-dataflow.md 已删除,勿链)。编排与成像核心 = C++; ImageDisplay 仅 C#。API 名与 toolflow v1.9 / cpp-headers.md / csharp-binding.md 一致。fake 替身见 toolflow §7.6;句柄交接见 §3.4;Diagnostic 见 §7.4.6;.avtf 见 §6.6。

本文是成体系场景目录:每个场景给出目的、前置、步骤骨架与可测断言,供实现期单测 / headless 验收对照。详细契约片段仍落在 examples.md 与 toolflow §9,避免把契约文撑爆。

关联: examples.md · toolflow.md §9 / §10.1 · test-contracts.md(R4 可勾选契约;R6 含 S09; Arden · R9 TC-OUT-* / S10; Arden · R10 TC-ACQ-* / TC-SESS-* / S11; Arden · R11 TC-MEM-* / S12; Arden · R12 TC-DISP-* / S13) · host-integration.md( R7 · S07 同核;两端都停;TC-CTL-05 交叉) · cpp-headers.md · sketch/README.md(R5 伪树·示意实现对照,非工程) · csharp-binding.md · glossary.md( R8;Arden · R9 / R10 / R11 / R12 撞号) · output-inspection.md( T10) · camera-acquisition.md · session-lifecycle.md · memory-management.md( Arden · R11) · image-display.md( Arden · R12;仅 C#) · validation-and-roadmap.md

场景目录

ID标题主路径文档回链
S01单工具 Run注册 → Create/AddTool → RunSession.Run → Outputstoolflow §7.4 / §9.4;cpp-headers
S02多工具 ToolFlow 顺序链列表序 Link + Validate + 自上而下 Runtoolflow §4.1 / §4.2 / §5 / §9.1
S03采集→对齐→检测→输出观察(fake)fake_acq → fake_pm_align → passthrough/constant;观察 LastRuntoolflow §7.6.3 / §9.1;output-inspection
S04.avtf Load + Validate 失败UnknownTypeId / MissingRequiredPropertytoolflow §6.5–6.6 / §7.4.5–7.4.6
S05句柄交接与交显示Complete→ReadOnly、链接赋值、H1–H10;显示段 仅 C#toolflow §3.4;test-contracts §6;examples;csharp-binding;image-display
S06Cancel / abort / Busy协作取消、abort_on_tool_failure、同 session 重入拒绝toolflow §4.3 / §7.4.3 / §7.4.6;session-lifecycle
S07headless 批跑 vs 带显示宿主无 UI 循环 Run vs 绑定层 + ImageDisplaytoolflow §7.2–7.3; host-integration.md( R7);csharp-binding;examples T12
S08Tool 作者:fake 注册 + 最小自定义 Toolregister_fake_tools + ITool 最小契约toolflow §7.6;cpp-headers
S09a嵌套 ToolFlow 合法 + Encapsulation内层终端对外;私有属性 → Encapsulationtoolflow §9.3 / §7.4.6 / §10.1
S09bSourceDisabled + InvalidBinding源禁用拉值失败;端点/方向非法主断言toolflow §4.3 / §7.4.6 / §10.1
S09cIoFailure + 停相机 ≠ 自动 Cancel截断/不可读;停止矩阵独立步骤toolflow §7.4.3 / §7.4.6;session-lifecycle
S09d§9.2 两流终端串联(可选)Locate→Defect 终端链;极薄正路径toolflow §9.2 / §4.1
S10输出观察 T10 事件闭集(fake)SnapshotReplaced / EmptySnapshot / Unavailable / Evicted;失败保留;Subscribe+replay;查看者 vs Evict;KeepLatest vs ReleaseAfterRouting;观察永不驱动 Runoutput-inspection(T10);test-contracts TC-OUT-*( Arden · R9);TC-RUN-05
S11采集接入 + 会话停流(fake · 阶段 D)ImportNative vs 回调 Copy;TryPostOwned;满池/入口预算;归还泵与 ReturnCompleted;Latest 预览;回调不堵归还;停流结束条件;外保留≠假归还;超时续清;ReturnFailed/断线可观察;两端都停须显式 Cancel+停采集camera-acquisition;session-lifecycle;test-contracts TC-ACQ-* / TC-SESS-*( Arden · R10);TC-CTL-04
S12存储 / pin / 归还(fake · 阶段 B)Rent/TakeOwnership/ImportNative 工厂接管;最后引用→Returning;禁等自身 ReturnCompleted;pin 阻写/归还;写 pin 阻 Complete;授予/取消与 Share/Dispose 原子;未 Complete→Invalid;首次根范围/ROI 中止整实体;Invalid 恢复;releaseAsync 一次与归还隔离/ReturnFailedmemory-management;test-contracts TC-MEM-*( Arden · R11);image-model(根范围薄指)
S13显示集成(仅 C# · 阶段 F)登记≠授予;Share 不接管;四回执;失败保留;四格式;ROI;卸载;预算;显示只观察image-display;TC-DISP-*(Arden · R12);≠ T15 R10

目的

验证最小闭环:Registry 创建单一 Tool → 放入 ToolFlow → RunSession::Run → 读 Outputs / 工具属性;断言成功路径与 ToolId 可定位。

前置

  • ToolRegistry 已 register_fake_tools(registry)(或至少注册 fake_constant)。
  • 无 .avtf;内存建流即可。
  • 无图像句柄要求(标量输出即可)。

步骤骨架(C++)

// 示意、非可编译;命名空间与头文件对齐 toolflow §7.4 / cpp-headers
#include <vyntek/toolflow/toolflow.hpp>
#include <vyntek/toolflow/registry.hpp>
#include <vyntek/toolflow/run_session.hpp>

using namespace vyntek::toolflow;

ToolRegistry registry;
register_fake_tools(registry);

auto flow = ToolFlow::Create("S01_Single");
auto* c = flow->AddTool(registry.Create("fake_constant", "Const"));
// 配置必填参数(示意):输出固定 int32
c->SetProperty("Value", Value::Int32(42));
flow->AddOutput("Out", TypeDesc{Type::Int32});
flow->Link(c, "Output", flow->Output("Out"));

std::vector<Diagnostic> diags;
if (!flow->Validate(diags)) { /* 期望空 Error */ return; }

RunSession session(flow.get());
RunResult r = session.Run();
// 断言见下
int out = session.GetOutput<int>("Out");

期望断言

项期望
Validate无 Severity::Error
r.oktrue
OutputsOut == 42
ToolIdc->id() 非空且流内稳定;失败时可写入 Diagnostic.tool_id
诊断成功路径 diagnostics 可空或仅 Info
契约映射TC-RUN-01 / TC-REG-04 / TC-LNK-08

对应文档章节

toolflow §3.1 / §7.4.1 / §7.4.3 / §7.4.4 / §9.4;cpp-headers(公开头);§7.6.3(fake_constant)

目的

验证列表序权威调度:多工具 AddTool → Link(源须在目标之上)→ Validate → 一次 Run 自上而下执行;链接不是 DAG 边调度。

前置

  • register_fake_tools:fake_acq、fake_passthrough、fake_constant(或等价)。
  • 可选:故意建「源在目标之下」的坏链,另测 OrderViolation(本场景主路径为合法链)。

步骤骨架(C++)

auto flow = ToolFlow::Create("S02_Chain");
auto* acq  = flow->AddTool(registry.Create("fake_acq", "Acq"));
auto* pass = flow->AddTool(registry.Create("fake_passthrough", "Pass"));
auto* flag = flow->AddTool(registry.Create("fake_constant", "Flag"));

flow->AddOutput("Image", TypeDesc{Type::ImageGray8});
flow->AddOutput("Flag",  TypeDesc{Type::Int32});

flow->Link(acq,  "OutputImage", pass, "InputImage");
flow->Link(pass, "OutputImage", flow->Output("Image"));
flow->Link(flag, "Output",      flow->Output("Flag"));
// 坏链对照(勿留在成功配方):flow->Link(pass, "OutputImage", acq, "InputImage");
// → Validate 期望 DiagnosticCode::OrderViolation

std::vector<Diagnostic> diags;
assert(flow->Validate(diags));
RunSession session(flow.get());
RunResult r = session.Run();

期望断言

项期望
执行序Acq → Pass → Flag(按 tools[]);无「边就绪即跑」
r.oktrue
H6Outputs.Image 为 Access=ReadOnly;对输入 OpenWrite 立即拒绝
坏链Validate 失败且含 OrderViolation;不留下半条边
TypeMismatch图像链到 Int32 终端 → Error
禁用源源 disabled 时下游 Run 拉值 → DiagnosticCode::SourceDisabled(MVP 严格;主场景见 S09b)
契约映射至少覆盖 test-contracts TC-RUN-02 / TC-LNK-01 / TC-LNK-03

对应文档章节

toolflow §4.1 / §4.2 / §5.4 / §7.4.2 / §9.1;§3.4 H6

目的

用 MVP fake 替身复现 §9.1 流水线心智(无需真实相机/PMAlign):fake_acq → fake_pm_align → passthrough/constant;Run 后只读观察 Outputs / LastRun(观察不驱动 Run)。

前置

  • register_fake_tools:fake_acq、fake_pm_align、fake_passthrough 或 fake_constant。
  • 无真实 SDK;可选输出观察订阅(见 output-inspection,T10)。

步骤骨架(C++)

auto flow = ToolFlow::Create("S03_InspectFake");
auto* acq   = flow->AddTool(registry.Create("fake_acq", "Acq"));
auto* align = flow->AddTool(registry.Create("fake_pm_align", "Align"));
auto* pass  = flow->AddTool(registry.Create("fake_passthrough", "OutImg"));
auto* cnt   = flow->AddTool(registry.Create("fake_constant", "Count"));

flow->AddOutput("PoseX", TypeDesc{Type::Float64});   // 示意:从 Pose 最小字段拆出
flow->AddOutput("DefectCount", TypeDesc{Type::Int32});
flow->AddOutput("Preview", TypeDesc{Type::ImageGray8});

flow->Link(acq,   "OutputImage", align, "InputImage");
flow->Link(align, "OutputImage", pass,  "InputImage");  // 或 Share 输入
flow->Link(pass,  "OutputImage", flow->Output("Preview"));
flow->Link(cnt,   "Output",      flow->Output("DefectCount"));
// Pose → 终端:按 §5.5 最小字段路径(示意)
// flow->Link(align, "Pose.X", flow->Output("PoseX"));

assert(flow->Validate(/*diags*/));
RunSession session(flow.get());
RunResult r = session.Run();

// 输出观察:只读 LastRun / Outputs;不得 session.Run 由观察回调触发
auto preview = session.GetOutput<vyntek::imaging::PixelImageHandle>("Preview");
// 可选:OutputStore 订阅 EmptySnapshot / Unavailable(T10)— 见 output-inspection

期望断言

项期望
r.oktrue
H3/H4/H6Preview / 下游输入 access()==ReadOnly;同 BufferId 语义(passthrough Share)
Pose / CountOutputs 可读;类型符合 TypeDesc
观察订阅变更不触发第二次 Run;失败保留策略见 output-inspection
无设备全程无相机 SDK 调用
契约映射TC-RUN-03 / TC-H6 / TC-REG-01 / TC-RUN-05(薄);细项 S10 / TC-OUT-*( Arden · R9)

对应文档章节

toolflow §7.6.3 / §9.1 / §3.4;output-inspection(T10);examples(成像读写片段)

目的

钉住持久化失败分轨:未知 type_id → Load UnknownTypeId(严格失败,无占位成功);已知 type 缺必填配置属性 → Validate MissingRequiredProperty(禁止静默默认糊弄)。对照 §6.5 / §6.6 反样例。

前置

  • 合法正样例 .avtf 可作对照(toolflow §6.6.2)。
  • Registry 仅含 fake/builtin;不含type_id_not_registered。
  • 另备一份「已知 fake_constant 但缺必填 Value」的配方或 Load 后删属性。

步骤骨架(C++)

#include <vyntek/toolflow/serialize.hpp>

// --- A) 未知 type_id ---
auto bad_unknown = ToolFlow::Load("fixtures/unknown_type.avtf", registry);
// fixtures 内 tools[].type_id = "type_id_not_registered"
assert(!bad_unknown);
// bad_unknown.error() 诊断列表含 DiagnosticCode::UnknownTypeId(Severity::Error)

// --- B) 已知 type、缺必填属性 ---
auto loaded = ToolFlow::Load("fixtures/missing_required.avtf", registry);
assert(loaded);  // Load 成功(type 已知;component_version 不拦)
std::vector<Diagnostic> diags;
bool ok = (*loaded)->Validate(diags);
assert(!ok);
assert(std::any_of(diags.begin(), diags.end(), [](const Diagnostic& d) {
  return d.code == DiagnosticCode::MissingRequiredProperty
      && d.severity == Severity::Error;
}));
// 不得出现「静默填默认后 Validate 成功」

反样例要点(文档级,与 §6.6.4 对齐):

失败类触发码
未知工具tools[].type_id 不在 RegistryUnknownTypeId(Load)
缺必填已知 type,缺配置属性MissingRequiredProperty(Validate)
schema 过高schema_version > 加载器SchemaTooNew(Load;本场景可作附带用例)

期望断言

项期望
ALoad 失败;≥1 条 UnknownTypeId Error;无半初始化成功流
BLoad 成功;Validate 失败;MissingRequiredProperty;与 UnknownTypeId 分轨
附带过高 schema → SchemaTooNew;component_version 差异不单独拦 Load
契约映射TC-SER-02 / TC-SER-03 / TC-LNK-07 / TC-SER-04(见 test-contracts)

对应文档章节

toolflow §6.5 / §6.6 / §7.4.5 / §7.4.6 / §10.1(Load 复载);validation-and-roadmap(T8)

目的

覆盖 §3.4 主路径:工具内 Complete / CompleteAndOpenRead + ShareImage → 链接拉值写入下游(H6)→ headless 持 ReadOnly 句柄;上屏仅经绑定层 + C# ImageDisplay(禁止 C++ UI)。

前置

  • fake:fake_acq + fake_clone_write(或最小自定义写工具)+ fake_passthrough。
  • 显示宿主:PInvoke 绑定可用;C# 侧有 ImageDisplay 实例(见 csharp-binding / image-display)。

步骤骨架(C++ · 编排与成像)

#include <vyntek/imaging/handle.hpp>
#include <vyntek/imaging/factory.hpp>
#include <vyntek/toolflow/toolflow.hpp>
#include <vyntek/toolflow/run_session.hpp>

// 流:Acq → CloneThenWrite → Passthrough → Output 终端
auto flow = ToolFlow::Create("S05_Handle");
auto* acq  = flow->AddTool(registry.Create("fake_acq", "Acq"));
auto* wr   = flow->AddTool(registry.Create("fake_clone_write", "Write"));
auto* pass = flow->AddTool(registry.Create("fake_passthrough", "Share"));
flow->AddOutput("DisplayImage", TypeDesc{Type::ImageGray8});
flow->Link(acq,  "OutputImage", wr,   "InputImage");
flow->Link(wr,   "OutputImage", pass, "InputImage");
flow->Link(pass, "OutputImage", flow->Output("DisplayImage"));

RunSession session(flow.get());
RunResult r = session.Run();
auto handle = session.GetOutput<vyntek::imaging::PixelImageHandle>("DisplayImage");
assert(handle.access() == vyntek::imaging::Access::ReadOnly);
// headless:到此为止;句柄经绑定交给 UI 进程/线程(示意)

步骤骨架(C# · 仅显示段)

// 示意、非可编译;仅 ImageDisplay;像素来自 C++ 句柄经 PInvoke 绑定
// 对齐 csharp-binding / examples.md T12 / image-display
using var bound = ImagingBindings.AdoptReadOnly(nativeHandle); // opaque + Dispose
imageDisplay.SetImage(bound);  // 或 SetImage(projection)
// 取样 / Depth 选项等见 examples.md;不得在此启动 ToolFlow.Run / Cancel 相机

期望断言

项期望
H1/H2Complete 后再写失败;CompleteAndOpenRead 得 ReadOnly;未 Complete 不得链给下游
H3/H4ShareImage / ReadOnly Share → OpenWrite 拒绝;多句柄独立关闭
H6链接赋值后目标输入 ReadOnly;列表序不变
H7/H8已交下游不可改;Clone 新 BufferId 可写后再发布
显示C# SetImage 成功;控件不承担编排 Cancel / 停相机(停止矩阵 §7.4.3)
语言无 C++ UI / Avalonia include
H9ReadOnly ROI OpenWrite 拒绝;不得把根退化为可写
H10失败/未 Complete 输出不可假成功交给下游(与 S06 abort 联测)
契约映射test-contracts TC-H1…TC-H9(主路径);显示段 TC-CTL-05

对应文档章节

toolflow §3.4(H1–H10)/ §7.4.3; test-contracts §6; examples.md(T12);csharp-binding;image-display;memory-management / image-model

目的

验证执行控制三件套:(1)协作式 RequestCancel → Cancelled,后续工具不启动;(2)abort_on_tool_failure=true + 失败工具 → Aborted,后续不 Run(H10);(3)同 RunSession 重入 → Busy(MVP 拒绝,不排队)。

前置

  • fake:fake_cancel_check、fake_fail_once / fake_fail_always、可选 fake_busy_probe。
  • abort_on_tool_failure 默认 true(§4.3)。

步骤骨架(C++)

// --- Cancel ---
auto flow_c = ToolFlow::Create("S06_Cancel");
auto* slow = flow_c->AddTool(registry.Create("fake_cancel_check", "C"));
auto* after = flow_c->AddTool(registry.Create("fake_constant", "After"));
flow_c->Link(/*…*/);  // After 在 C 之后
RunSession s1(flow_c.get());
// 另一线程/测试钩:s1.RequestCancel();
RunResult rc = s1.Run();
// 期望:rc.ok==false;diagnostics 含 Cancelled;After 未 Run(LastRun 仍为未跑/跳过)

// --- abort_on_tool_failure ---
auto flow_a = ToolFlow::Create("S06_Abort");
flow_a->set_abort_on_tool_failure(true);
auto* ok1  = flow_a->AddTool(registry.Create("fake_acq", "A"));
auto* fail = flow_a->AddTool(registry.Create("fake_fail_always", "F"));
auto* ok2  = flow_a->AddTool(registry.Create("fake_constant", "Never"));
// Link A→…;F 失败;Never 在 F 后
RunResult ra = RunSession(flow_a.get()).Run();
// 期望:Aborted;Never 未 Run;不得把 F 的 Invalid 图假成功交给下游(H10)

// --- Busy ---
auto flow_b = ToolFlow::Create("S06_Busy");
auto* probe = flow_b->AddTool(registry.Create("fake_busy_probe", "Slow"));
RunSession s2(flow_b.get());
// 线程1:s2.Run() 进行中
// 线程2:RunResult rb = s2.Run();
// 期望:rb 失败;DiagnosticCode::Busy

期望断言

项期望
CancelCancelled;当前工具合作返回后不再启动后续;r.ok==false
AbortAborted;failed_tool_id 指向失败工具;后续 LastRun 未执行;H10
Busy同 session 第二次 Run → Busy Error;首次 Run 不受损
停止矩阵Cancel ≠ 停相机;停相机 ≠ 自动 Cancel(§7.4.3;独立步骤见 S09c)
契约映射TC-CTL-01 / TC-CTL-02 / TC-CTL-03 / TC-H10

对应文档章节

toolflow §4.3 / §7.4.3 / §7.4.6 / §3.4 H10 / §10.1;session-lifecycle;validation-and-roadmap(T9)

目的

对照两种宿主形态:纯编排(循环 Run、断言 Outputs、无 UI)与 显示宿主(同 C++ 核心 + PInvoke + 仅 C# ImageDisplay)。证明不存在第二执行引擎,也不写 C++ UI。

前置

  • 同一 .avtf 或内存流(建议 S03 fake 流水线)。
  • headless:测试进程 / CLI runner。
  • 显示:C# 应用引用绑定包。

步骤骨架(C++ · headless 批跑)

auto loaded = ToolFlow::Load("recipes/inspect_fake.avtf", registry);
assert(loaded);  // 未知 type / SchemaTooNew 等见 S04
std::unique_ptr<ToolFlow> flow = std::move(*loaded);
RunSession session(flow.get());  // 与 §7.4.5 一致:session 不抢 flow 所有权
for (int i = 0; i < N; ++i) {
  // 可选:注入帧 / 依赖 fake_acq 自产
  RunResult r = session.Run();
  assert(r.ok);
  auto n = session.GetOutput<int>("DefectCount");
  // 记录 n;无 SetImage / 无窗口
}

步骤骨架(C# · 显示宿主,编排仍可经绑定调 C++)

// 示意薄 PInvoke 入口(对齐 csharp-binding);编排仍在 C++,非第二套 ToolFlow
ToolFlowNative.RunOnce(sessionHandle); // opaque session handle;不在托管侧重写执行器
using var img = ToolFlowNative.GetOutputImage(sessionHandle, "Preview");
imageDisplay.SetImage(img);
// Cancel / 停相机按停止矩阵分派到编排/采集 API,不进 ImageDisplay

期望断言

项期望
headlessN 次 Run 稳定;无 UI 依赖;Diagnostic 可收集到日志
显示宿主同一配方结果一致;SetImage 只读快照;控件卸载不 Abort 编排
边界无第二套 ToolFlow;无 C++ ImageDisplay
会话批跑结束按 session-lifecycle 排空/释放;观察引用不阻塞归还契约
契约映射TC-RUN-04 / TC-SER-01 / TC-CTL-05

对应文档章节

toolflow §7.2 / §7.3 / §9.4; host-integration.md( R7 同核 / 停止分派);csharp-binding; examples.md T12;session-lifecycle;image-display; test-contracts.md TC-RUN-04 / TC-SER-01 / TC-CTL-05

目的

落实 §7.6:测试包 register_fake_tools;作者实现最小 ITool(type_id、属性袋、Run、失败/取消、成像纪律);与 Create/Load 共用同一 Registry;重复 type_id 拒绝。

前置

  • 公开头:registry.hpp / tool.hpp(示意);成像公开句柄头。
  • 自定义工具 不要默认链进生产 register_builtin_tools。

步骤骨架(C++)

#include <vyntek/toolflow/registry.hpp>
#include <vyntek/toolflow/tool.hpp>
#include <vyntek/imaging/handle.hpp>

// 最小自定义 Tool(示意)
class ScaleGrayTool final : public ITool {
 public:
  std::string_view type_id() const override { return "demo_scale_gray"; }
  ToolRunStatus Run(RunContext& ctx) override {
    auto in = GetProperty("InputImage");  // ReadOnly 句柄
    // Clone → OpenWrite → 缩放填充 → CompleteAndOpenRead → ShareImage → SetProperty OutputImage
    // 定期 ctx.cancel_requested();失败勿交出未 Complete 句柄
    return ToolRunStatus::Ok;
  }
  // id/name/enabled/properties/Get/Set … 按 §7.6.1
};

void register_demo_tools(ToolRegistry& r) {
  r.Register("demo_scale_gray", [](std::string_view name) {
    return std::make_unique<ScaleGrayTool>(name);
  });
  // 重复 Register 同 type_id → 拒绝
}

// 测试入口
ToolRegistry registry;
register_fake_tools(registry);   // §7.6.3 清单
register_demo_tools(registry);
auto* t = registry.Create("demo_scale_gray", "S");
assert(t);
auto missing = registry.Create("no_such_type", "X");
// 期望失败:UnknownTypeId

期望断言

项期望
注册fake + demo 可 Create;未知 → UnknownTypeId
共用.avtf 含 demo_scale_gray 时 Load 走同一工厂;禁止反序列化旁路
成像纪律输入只读;输出稳定发布;H1/H2/H8 可单测
失败未 Complete 输出不可被下游拉成成功 ReadOnly(H10)
重复注册同 type_id 第二次 Register 失败(生产默认)
契约映射TC-REG-01…TC-REG-06;成像 TC-H2 / TC-H8 / TC-H10

对应文档章节

toolflow §7.6 / §7.4.1 / §3.4;cpp-headers; examples.md(工具读写像素片段)

目的

补齐 §9.3 / §10.1「嵌套仅经终端对外」:内层先组好再 AddTool 进外层;外层只链内层 Outputs/Inputs 终端;企图绑内层私有工具属性 → Encapsulation。证明嵌套 Run = 子流完整顺序表,对外仍是一个 ITool::Run。

前置

  • register_fake_tools:fake_acq、fake_passthrough、fake_constant(或等价)。
  • 无 .avtf 亦可;内存建内层/外层即可(.avtf 嵌套 schema 见 §6.6,本场景主路径为内存)。

步骤骨架(C++)

// 示意、非可编译;对齐 toolflow §9.3 / §9.4 嵌套示意 / §7.4.6 Encapsulation
auto inner = ToolFlow::Create("InnerLocate");
auto* acq  = inner->AddTool(registry.Create("fake_acq", "Acq"));
auto* pass = inner->AddTool(registry.Create("fake_passthrough", "Share"));
inner->AddOutput("FixturedImage", TypeDesc{Type::ImageGray8});
inner->Link(acq, "OutputImage", pass, "InputImage");
inner->Link(pass, "OutputImage", inner->Output("FixturedImage"));
assert(inner->Validate(/*diags*/));

auto outer = ToolFlow::Create("OuterMeasure");
auto* locate = outer->AddTool(std::move(inner));  // 嵌套;所有权转入 outer
auto* flag  = outer->AddTool(registry.Create("fake_constant", "WidthFlag"));
outer->AddOutput("Preview", TypeDesc{Type::ImageGray8});
outer->AddOutput("Flag", TypeDesc{Type::Int32});
// 合法:外层只链内层终端
outer->Link(locate->Output("FixturedImage"), outer->Output("Preview"));
// 或:outer->Link(outer->Tool("InnerLocate")->Output("FixturedImage"), …);
flag->SetProperty("Value", Value::Int32(1));
outer->Link(flag, "Output", outer->Output("Flag"));

std::vector<Diagnostic> diags;
assert(outer->Validate(diags));
RunResult r = RunSession(outer.get()).Run();
// 期望:r.ok;Preview / Flag 可读;内层私有 Acq 属性对外不可见

// --- Encapsulation 负例(勿留在成功配方)---
// outer->Link(…, /* 指向 */ "InnerLocate.Acq.OutputImage", …);
// 或 Validate 时发现外层 link 写内层私有路径
// → DiagnosticCode::Encapsulation(Severity::Error);不留下半条边

期望断言

项期望
合法嵌套Validate 无 Error;r.ok==true;外层 Outputs 可读
封装外层仅经内层终端链接;内层私有工具属性不可被外层 Link
Encapsulation绑内层私有路径 → DiagnosticCode::Encapsulation;无残链
嵌套 Run子流按自身列表序完整执行;对外一次 ITool::Run
契约映射TC-RUN-06 / TC-LNK-04 / TC-REG-05

对应文档章节

toolflow §4.3(嵌套 Run)/ §9.3 / §9.4 嵌套示意 / §6.6 / §7.4.6 / §10.1(终端封装)

目的

将已进 §10.1 / Diagnostic 但 S01–S08 未独立成文的两项升格为清晰主断言:(1)源工具 disabled → 下游 Run 拉值 SourceDisabled(MVP 严格);(2)端点缺失 / 方向非法 → InvalidBinding(从 S04 附带升格,不与 UnknownTypeId / MissingRequiredProperty 混轨)。

前置

  • fake:fake_acq + fake_passthrough(或 fake_constant)。
  • 可内存建流;InvalidBinding 亦可经残缺 .avtf links 触发(§6.6.4)。

步骤骨架(C++)

// --- A) SourceDisabled ---
auto flow_d = ToolFlow::Create("S09b_Disabled");
auto* acq  = flow_d->AddTool(registry.Create("fake_acq", "Acq"));
auto* pass = flow_d->AddTool(registry.Create("fake_passthrough", "Pass"));
flow_d->AddOutput("Image", TypeDesc{Type::ImageGray8});
flow_d->Link(acq, "OutputImage", pass, "InputImage");
flow_d->Link(pass, "OutputImage", flow_d->Output("Image"));
acq->set_enabled(false);  // 或 flow 级 disabled 集合;语义对齐 §4.3
assert(flow_d->Validate(/*diags*/));  // 结构合法;禁用本身不必然 Validate 失败
RunResult rd = RunSession(flow_d.get()).Run();
// 期望:rd.ok==false;diagnostics 含 DiagnosticCode::SourceDisabled;
// Pass 不得假成功交出 Image;下游不得读到「假装有效」的帧

// --- B) InvalidBinding(主断言)---
auto flow_b = ToolFlow::Create("S09b_BadBind");
auto* c = flow_b->AddTool(registry.Create("fake_constant", "C"));
flow_b->AddOutput("Out", TypeDesc{Type::Int32});
// 端点缺失:属性名不存在 / 方向非法(输出←输出、输入→输入)/ 残缺 link
// flow_b->Link(c, "NoSuchProp", flow_b->Output("Out"));
// 或 Load("fixtures/invalid_binding.avtf") 后 Validate
std::vector<Diagnostic> diags;
bool ok = flow_b->Validate(diags);  // 或 Link 立即失败
assert(!ok);
assert(std::any_of(diags.begin(), diags.end(), [](const Diagnostic& d) {
  return d.code == DiagnosticCode::InvalidBinding
      && d.severity == Severity::Error;
}));
// 不留下半条边;与 TypeMismatch / OrderViolation / Encapsulation 分码

期望断言

项期望
SourceDisabledRun 拉值失败;码 SourceDisabled;下游无假成功 Outputs
禁用目标以其为目标的链接本轮不写入(§4.3);本场景主断言在「源禁用」
InvalidBinding端点缺失/方向非法/残链 → InvalidBinding Error;无残链
分轨不与 UnknownTypeId(Load)/ MissingRequiredProperty(Validate 缺配置)/ TypeMismatch / Encapsulation 混用同一码
契约映射TC-LNK-06 / TC-LNK-05

对应文档章节

toolflow §4.3 / §7.4.2 / §7.4.6 / §6.6.4 / §10.1(禁用源);S02 负例指针升格至此

目的

(1)文件不可读 / 截断 → Load/Save IoFailure(补齐 Diagnostic 表有码、场景正文缺席)。(2)停止矩阵独立步骤:宿主停相机 不自动 RequestCancel 在途 Run(可并入本场景;与 S06 Cancel/Busy 分测,避免只写在断言附注里)。

前置

  • 夹具:截断/权限错误的 .avtf 路径;或 Save 到不可写路径(示意)。
  • 停相机:采集入口可用 fake/桩(无真实相机 SDK 证据要求);Run 在途可用 fake_busy_probe / fake_cancel_check。

步骤骨架(C++)

// --- A) IoFailure ---
auto truncated = ToolFlow::Load("fixtures/truncated.avtf", registry);
assert(!truncated);
// truncated.error() 诊断含 DiagnosticCode::IoFailure(Severity::Error)
// 对照:未知 type → UnknownTypeId;过高 schema → SchemaTooNew;本项为 IO/截断

// auto save_fail = flow->Save("/root/no_write/out.avtf"); // 示意
// 期望:失败;IoFailure

// --- B) 停相机 ≠ 自动 Cancel(停止矩阵独立步骤)---
auto flow = ToolFlow::Create("S09c_StopCam");
auto* slow = flow->AddTool(registry.Create("fake_busy_probe", "Slow"));
// … 合法最小链 …
RunSession session(flow.get());
// 线程1:RunResult r = session.Run();  // 在途
// 宿主:StopCamera() / CloseAcquisition();  // 采集层;见 camera-acquisition / session-lifecycle
// 期望:session **未**被自动 RequestCancel;r 可跑至工具边界正常结束(或宿主再显式 Cancel)
// 诊断:不得仅因停相机而出现 Cancelled(除非宿主显式既停采集又 RequestCancel)

期望断言

项期望
IoFailure截断/不可读 Load 或不可写 Save → IoFailure;无半初始化成功流
分轨IoFailure ≠ SchemaTooNew ≠ UnknownTypeId
停相机不自动 RequestCancel;在途 Run 继续至边界或自行 Cancel
显式双停宿主若要两端都停,须显式停采集 + RequestCancel(文档断言,非隐式)
与显示显示关闭仍不停编排(TC-CTL-05;本场景主钉停相机条)
契约映射TC-SER-07 / TC-CTL-04

对应文档章节

toolflow §7.4.3 停止矩阵 / §7.4.6 / §10.1(T9);session-lifecycle;camera-acquisition(薄);S06 附注升格

目的

可选补齐 §9.2 心智:外层 Job/流内两个子 ToolFlow(Locate → Defect)仅经 终端 串联;列表序先 Locate 再 Defect;非嵌套私有属性、非 DAG。与 S09a(真嵌套封装)对照:本场景是「两流并列 + 终端链接」。

前置

  • fake 足以冒充 Locate/Defect 对外终端(如内部分别用 fake_acq+passthrough / fake_constant)。
  • 可全部内存组装;不要求编辑 UI。

步骤骨架(C++)

// 示意:两子流先各自 Validate,再放入外层列表并链终端(§9.2)
auto locate = ToolFlow::Create("Locate");
// … AddTool fake_acq → passthrough;AddOutput FixturedImage / Pose(示意)…
assert(locate->Validate(/*…*/));

auto defect = ToolFlow::Create("Defect");
defect->AddInput("Image", TypeDesc{Type::ImageGray8});
// … 内部 fake 检测;AddOutput OkNg / DefectCount …
assert(defect->Validate(/*…*/));

auto job = ToolFlow::Create("Job");
auto* loc = job->AddTool(std::move(locate));
auto* def = job->AddTool(std::move(defect));
job->Link(loc->Output("FixturedImage"), def->Input("Image"));
// 可选:Pose 终端同理
job->AddOutput("DefectCount", TypeDesc{Type::Int32});
// job->Link(def->Output("DefectCount"), job->Output("DefectCount"));

assert(job->Validate(/*…*/));
RunResult r = RunSession(job.get()).Run();
// 期望:执行序 Locate → Defect;r.ok;终端赋值生效;无环边

期望断言

项期望
序外层列表:Locate 在 Defect 之上;先 Locate.Run 再 Defect.Run
终端只链子流 Inputs/Outputs;不绑子流内部私有工具属性(否则走 S09a Encapsulation)
r.oktrue;Outputs 符合链接
非 DAG链接不是边就绪调度
契约映射TC-RUN-07 / TC-LNK-01 / TC-REG-05

对应文档章节

toolflow §9.2 / §4.1 / §10.1;与 S09a(§9.3 嵌套)对照

目的

将 output-inspection.md T10 已拍板集合事件与观察不变量收成可跑骨架(fake tools 即可):SnapshotReplaced / EmptySnapshot(禁静默)/ Unavailable / Evicted;失败保留上次成功;Subscribe + replay;查看者寿命 vs Evict;KeepLatest vs ReleaseAfterRouting;观察永不驱动 Run(对齐 TC-RUN-05 / TC-OUT-09)。不改事件闭集与 toolflow v1.9 语义。

前置

  • register_fake_tools:至少 fake_constant / fake_passthrough / fake_fail_once(或等价空输出 / 失败替身)。
  • 会话可配置 KeepLatest 与 ReleaseAfterRouting(示意开关;非 UI)。
  • 观察面示意 API 见 output-inspection(SubscribeOutputs / TryOpenLatest);非已发布 ABI;无观察 UI 专文。

步骤骨架(C++)

// 示意、非可编译;对齐 output-inspection T10 / test-contracts TC-OUT-*(Arden · R9)
using namespace vyntek::toolflow;

ToolRegistry registry;
register_fake_tools(registry);

// --- A: KeepLatest ---
auto flow = ToolFlow::Create("S10_OutObserve");
auto* c = flow->AddTool(registry.Create("fake_constant", "Const"));
c->SetProperty("Value", Value::Int32(1));
flow->AddOutput("Out", TypeDesc{Type::Int32});
flow->Link(c, "Output", flow->Output("Out"));
// session_opts.retain = KeepLatest;  // 示意

assert(flow->Validate(/*…*/));
RunSession session(flow.get());

std::vector<OutputSetEventKind> seen;
auto sub = session.SubscribeOutputs(
    [&](const OutputSetEvent& e) {
        seen.push_back(e.kind);
        // 禁止:在回调内 session.Run() / 重新采集 / 隐式链接赋值
    },
    /*replay_latest=*/true);

RunResult r1 = session.Run();           // 非空成功 → 期望 SnapshotReplaced
// 配置空成功替身(示意):c->SetProperty("EmitEmpty", true);
RunResult r_empty = session.Run();      // 空成功 → 必须 EmptySnapshot(禁静默)

// 失败保留:先成功再 fail_once
auto* f = flow->AddTool(registry.Create("fake_fail_once", "Fail"));
// … 使本轮失败 …
RunResult r_fail = session.Run();       // 不替换上次成功快照;可与失败状态并存

// Unavailable:模拟保留/配额失败(观察侧注入,示意)
// expect Unavailable;业务路由仍可 ok,但不得把旧集合冒充本轮新结果

// Evict + 查看者寿命
auto opened = session.TryOpenLatest(/*tool/port 示意*/);
session.EvictRetained(/*…*/);           // 或预算回收 → Evicted
// opened 仍可用至自行关闭;TryOpenLatest 对新身份不可用

sub.reset();  // 断开只释放订阅,不清空运行时最近输出(KeepLatest)

// --- B: ReleaseAfterRouting ---
// session_opts.retain = ReleaseAfterRouting;
// Run 后路由完成即不保留;再 Subscribe 无法补取已回收历史

期望断言

项期望
SnapshotReplaced非空成功 → 发该事件;当前集合身份可打开
EmptySnapshot空成功 → 必须发事件;禁止静默;不得把上次非空标为当前
Unavailable保留失败 → 该事件;不冒充本轮新结果
Evicted淘汰 → 该事件;其后 TryOpen 不可用
失败保留工具失败不替换上次成功快照(TC-OUT-05 / TC-RUN-05)
Subscribe+replayreplay_latest 重放当前保留(含空);之后不漏更新
查看者 vs Evict已打开查看者不被强制关;新 Open 不可用
KeepLatest vs ReleaseAfterRouting前者可重放;后者不冒充可恢复已回收历史
永不驱动 Run观察回调/查询不隐式 Run(TC-OUT-09)
契约映射TC-OUT-01…TC-OUT-09;交叉 TC-RUN-05

对应文档章节

output-inspection( T10); test-contracts.md §7 TC-OUT-*( Arden · R9);toolflow §7.4.3 / §10.1 非编排驱动;S03 薄指针

目的

将 camera-acquisition.md / session-lifecycle.md 已拍板散文收成可跑骨架( fake SDK / 假入口即可):持帧 ImportNative vs 回调内 Copy;TryPostOwned 接管/拒绝/抛错;满池与入口预算;归还泵与 ReturnCompleted;Latest 预览;回调不堵 SDK 归还;停流状态机结束条件;外保留 ≠ 假称已归还;超时未排空但底层续清;ReturnFailed / 断线可观察;两端都停须显式 Cancel + 停采集。不改 Diagnostic / T9 停止矩阵语义与 toolflow v1.9。

前置

  • fake 采集适配:可切换「持帧稳定」与「回调临时指针」两种模式;假池容量与入口预算可注入满载。
  • 假归还泵:可延迟确认、注入 ReturnFailed、模拟断线。
  • 可选:fake_acq 仅作 ToolFlow 侧对照(本场景主断言在采集/会话层,不扩编排语义)。
  • 示意 API 非已发布 ABI;无真实供应商驱动要求。

步骤骨架(C++)

// 示意、非可编译;对齐 camera-acquisition / session-lifecycle / test-contracts TC-ACQ-* · TC-SESS-*(Arden · R10)
using namespace vyntek::imaging;

// --- A: 持帧 ImportNative ---
FakeSdkSession sdk(/*hold_frame=*/true);
auto img = adapter.ImportNativeReadOnly(sdk.CurrentFrame());  // TC-ACQ-01
bool ok = input.TryPostOwned(std::move(img));                 // TC-ACQ-03
assert(ok);
// Share 不重复 AddRef SDK;关闭最后句柄 → releaseAsync → 泵上 ReturnCompleted(TC-ACQ-05)

// --- B: 回调内 Copy ---
FakeSdkSession sdk2(/*hold_frame=*/false);
sdk2.OnFrame([](void* p, size_t n) {
  // 先取池+入口预算(TC-ACQ-04);满则拒绝,禁止先拷再发现无容量
  auto owned = PixelImage::CopyFrom(p, n);
  if (!input.TryPostOwned(std::move(owned))) { /* 关闭未接管 */ }
  // 按协议立即归还临时 SDK 缓冲(TC-ACQ-02)
  // 禁止在回调内等 UI / 算法 / 同线程 ReturnCompleted(TC-ACQ-07)
});

// --- C: Latest 预览 ---
for (int i = 0; i < 10; ++i) { /* 连续 Post */ }
// Latest 槽只保留最新;被替换项立即关闭;禁止无限 Post 带图 dispatcher(TC-ACQ-06)

// --- D: 停流结束条件 ---
session.BeginStop(/*drain=*/true);  // Quiescing → … → Stopped(TC-SESS-01)
// 无新回调、队列空、会话拥有引用已关、SDK 归还完成

// --- E: 外保留 ---
auto exported = host.ExportToExternalWindow(latest);  // 会话外保留
auto r = session.BeginStop(/*destroy_sdk=*/true);
// expect:报告仍有外部保留;不得假称设备已归还(TC-SESS-02)

// --- F: 超时续清 ---
auto r_to = session.BeginStop(/*timeout=*/50ms);
// 返回未排空/故障摘要;底层清理仍继续(TC-SESS-03)

// --- G: ReturnFailed / 断线 ---
sdk.InjectReturnFailed();  // 或 InjectDisconnect()
// 可观察故障;不自动再 Release;泵继续其他项;停止结果汇总(TC-SESS-04)

// --- H: 两端都停 ---
// 宿主要相机与 Run 一起停:必须显式
acquisition.StopAccepting();          // 停采集
run_session.RequestCancel();          // 显式 Cancel(TC-SESS-05 / TC-CTL-04)
// 禁止假设一次 SDK.Stop = 三层全停

期望断言

项期望
ImportNative持帧路径稳定 ReadOnly;Share 不重复 AddRef;一次 Release(TC-ACQ-01)
回调 Copy临时指针路径必须 Copy;回调后不持原地址(TC-ACQ-02)
TryPostOwned成功接管 / 拒绝关闭 / 抛错未接管(TC-ACQ-03)
满池预算先预算再拷;满则拒绝可观察(TC-ACQ-04)
归还泵ReturnCompleted 仅 SDK 确认后;失败不吞、不自动重试(TC-ACQ-05)
Latest有界最新槽;替换即关;无无限带图 Post(TC-ACQ-06)
回调不堵回调不等 UI/算法/同线程归还(TC-ACQ-07)
停流结束状态机到 Stopped 的联合条件满足(TC-SESS-01)
外保留不得假称已归还(TC-SESS-02)
超时续清超时返回未排空;底层继续(TC-SESS-03)
故障可观察ReturnFailed/断线汇总;泵不卡死(TC-SESS-04)
两端都停显式 Cancel + 停采集(TC-SESS-05)
契约映射TC-ACQ-01…07;TC-SESS-01…05;交叉 TC-CTL-04

对应文档章节

camera-acquisition.md; session-lifecycle.md; test-contracts.md §8–§9( Arden · R10); host-integration.md §4「两端都停」;toolflow §7.4.3( T9,不改语义);S09c 薄指针

目的

将 memory-management.md 已拍板散文收成可跑骨架( fake buffer 即可):工厂接管点;最后引用与 Returning;禁止持最后引用等自身 ReturnCompleted;pin 延寿与写 pin 阻 Complete;读 pin 永不提升为可写;OpenRead/OpenWrite 取消与 Share/Dispose 原子裁定;未 Complete→Invalid;首次根范围与 ROI 中止整实体;Invalid 仅无期望版本完整恢复;releaseAsync 恰好一次、一项卡住不堵其他、ReturnFailed 不自动重试不回池。不改 Diagnostic / T9 停止矩阵与 toolflow v1.9。

前置

  • fake MemoryPool / fake native release 回调;可注入抛错、延迟归还、ReturnFailed。
  • 可构造多 handle、排队读/写申请、读/写 pin。
  • 类型化首次根范围断言可薄指 image-model.md;本场景主断言在存储协调层。
  • 示意 API 非已发布 ABI;无真实供应商驱动要求。

步骤骨架(C++)

// 示意、非可编译;对齐 memory-management / test-contracts TC-MEM-*(Arden · R11)
using namespace vyntek::imaging;

// --- A: 工厂接管 ---
auto rented = ImageBuffer::Rent(4096);           // TC-MEM-01 成功接管
// TakeOwnership 抛错路径:owner 仍由调用方释放(TC-MEM-02)
// ImportNative 抛错路径:不调 release(TC-MEM-03)

// --- B: 最后引用 ---
auto h1 = rented.Share();
auto h2 = rented.Share();
h1.Dispose();  // 仍 Active(TC-MEM-04)
// 关闭全部 handle + 访问 + 排队申请后才 Returning
// 禁止:持最后引用边 AwaitReturnCompleted(TC-MEM-05)

// --- C: pin ---
auto reader = rented.OpenRead().value();
auto pin = reader.Pin();
reader.Dispose();  // pin 仍阻写/归还;pin.access()==ReadOnly(TC-MEM-06)
pin.Dispose();

auto writer = rented.OpenWrite().value();
auto wpin = writer.Pin();
// writer.Complete() → InvalidOperation(写 pin 未放)(TC-MEM-07)
wpin.Dispose();
writer.Complete();

// --- D: 授予/取消与 Share/Dispose ---
// OpenRead/OpenWrite + CancelToken;发起 handle Dispose 不撤已入队申请(TC-MEM-08)
// Share 与 Dispose 竞争:成功子对象独立有效(TC-MEM-09)

// --- E: 初始化 / Invalid ---
{
  auto w = ImageBuffer::Rent(4096).OpenWrite().value();
  w.Dispose();  // 未 Complete → Invalid(TC-MEM-10)
}
// 类型化首次仅写 ROI → 拒绝;ROI 中止 → 整实体 Invalid(TC-MEM-11)
// Invalid 后带 expectedVersion 的 OpenWrite 拒绝;无期望版本完整写可恢复(TC-MEM-12)

// --- F: 归还隔离 ---
// 两实体并行归还;其一 releaseAsync 卡住/失败
// expect:恰好一次 release;不串行堵另一项;ReturnFailed 不自动重试、不回池(TC-MEM-13)

期望断言

项期望
Rent成功接管;失败归还内部;ReadOnly+Uninitialized 拒绝(TC-MEM-01)
TakeOwnership成功才接管;抛错不释放调用方 owner(TC-MEM-02)
ImportNative成功才接管;抛错不调 release(TC-MEM-03)
最后引用全 handle+访问+排队结束才 Returning(TC-MEM-04)
等待环禁持最后引用等自身 ReturnCompleted(TC-MEM-05)
读 pin父 reader Dispose 后仍阻写/归还;Access 永 ReadOnly(TC-MEM-06)
写 pin未放则 Complete 拒绝;父先 Dispose→Invalid 但独占至末 pin(TC-MEM-07)
取消/排队cancel 可撤等待;发起 handle Dispose 不撤已入队(TC-MEM-08)
Share/Dispose原子裁定;成功子对象独立有效(TC-MEM-09)
未 CompleteDispose→Invalid;不重发旧代次(TC-MEM-10)
根范围/ROI首次须根范围;ROI 中止整实体 Invalid(TC-MEM-11)
恢复无期望版本完整写可恢复;带 expected 拒绝 Invalid(TC-MEM-12)
归还隔离release 一次;卡住不堵其他;ReturnFailed 不重试不回池(TC-MEM-13)
契约映射TC-MEM-01…13

对应文档章节

memory-management.md; image-model.md(首次根范围 / padding 薄指); test-contracts.md §10( Arden · R11);S05 句柄交接薄指针(H1–H10 不替代本表)

目的

将 image-display.md 已拍板显示契约收成可勾选路径:登记≠授予、Share 不接管、Retain/Snapshot/Detach、四回执互不等价、失败保留旧图、四格式取样、ROI/负步长、卸载/重挂/context、预算失败不丢旧图、显示只观察。仅 C# 显示宿主;像素经绑定自 C++ vyntek::imaging。勿写成 C++ UI;≠ T15 开放风险 R10(C++ UI·已关闭)。

前置

  • 绑定层可将 C++ PixelImageHandle 投影为 IPixelImage(及 Gray8/Rgb24/Bgr24/Depth16)。
  • UI 线程可用;C# ImageDisplay 实例已创建(见 csharp-binding / examples 显示段)。
  • fake / 测试图像可提供四格式、ROI、负步长与可写/只读两态(无需真机相机)。

步骤骨架(C# · 仅显示段)

// 示意、非可编译;对齐 image-display / test-contracts TC-DISP-*(Arden · R12)
// 像素已由绑定层从 C++ 句柄投影为 IPixelImage

var update = viewer.SetImage(image);           // TC-DISP-01:登记+Share;未承诺已 OpenRead
// 调用方随后可关闭自有 handle;控件不接管(TC-DISP-02)

var retain = viewer.SetImage(rwImage, ImageInputRetention.RetainInput);
// 原地写须等读者退出(TC-DISP-03)

var snap = viewer.SetImage(rwImage, ImageInputRetention.Snapshot);
await snap.Completion; await snap.InputReleased;  // 释放后原图可写;屏幕可仍旧图(TC-DISP-04)
var detach = await retain.DetachInputAsync();     // Released 或 CpuBudgetExceeded 不毁当前(TC-DISP-05)

// Completion / InputReleased / ImageRendered / ReturnCompleted 分别断言,互不等价(TC-DISP-06)
// 注入 VersionMismatch / UnsupportedImage → Failed,旧画面保留(TC-DISP-07)

viewer.SetImage(gray);  pixel.TryGetGray8(...);   // 仅相符格式成功(TC-DISP-08)
viewer.SetImage(rgb);   pixel.TryGetRgb24(...);   // 无隐式转换
viewer.SetImage(bgr);   pixel.TryGetBgr24(...);
viewer.SetImage(depth); pixel.TryGetDepth16(...);

viewer.TryGetPixel(viewPos, out var p);           // ROI/负步长→逻辑坐标(TC-DISP-09)

// 卸载 → 取消候选;重挂载仍有效 Source = 新请求(TC-DISP-10)
// 预算不足 → Failed + 保留旧图(TC-DISP-11)
// Dispose / ClearImageAsync → 不停 Run、不停相机(TC-DISP-12 / TC-CTL-05)

期望断言

项期望
登记≠授予SetImage 返回即已 Share;Installed/OpenRead 在 Completion 路径(TC-DISP-01)
Share 不接管控件不 Dispose 调用方 handle;多控件同图合法(TC-DISP-02)
RetainInput持稳定读;原地写等待退出;属性不改当前存储(TC-DISP-03)
SnapshotInputReleased 后不阻原图写;Source 不置空(TC-DISP-04)
DetachReleased 保持来源身份;预算不足不毁当前(TC-DISP-05)
四回执Completion ≠ InputReleased ≠ ImageRendered ≠ ReturnCompleted(TC-DISP-06)
失败保留Failed/VersionMismatch 保留旧画面;无信息混叠(TC-DISP-07)
四格式TryGet* 无隐式转换;Depth 无效码/米制规则正确(TC-DISP-08)
ROI/负步长逻辑像素取样;禁止 viewPos 当缓冲偏移(TC-DISP-09)
卸载/重挂取消候选;重挂=新请求;context 可恢复(TC-DISP-10)
预算失败不丢旧图;释放本次引用;无自动重试队列(TC-DISP-11)
只观察卸图/Dispose 不停编排/不停相机(TC-DISP-12 / TC-CTL-05)
契约映射TC-DISP-01…12;交叉 TC-CTL-05
边界仅 C#;无 C++ UI;≠ T15 R10;不扩 T9

对应文档章节

image-display.md; examples.md 显示段; test-contracts.md §11( Arden · R12); host-integration.md TC-CTL-05;S05/S07 显示段薄指针(不替代本表)

使用说明(给实现 / 验收)

  1. 示意非可编译:骨架钉住 API 形状与断言;落地时以公开头与单测为准。树形碎片对照见 sketch/README.md(R5,非验收正文)。
  2. 主路径覆盖:S01–S08 为八条主路径; S09a–d(R6) 补齐嵌套/Encapsulation、SourceDisabled、InvalidBinding、IoFailure、停相机≠自动 Cancel,以及可选 §9.2 两流串联; S10(Arden · R9) 对齐 T10 输出观察可勾选; S11(Arden · R10) 对齐阶段 D 采集/会话可勾选; S12(Arden · R11) 对齐阶段 B 存储/pin/归还可勾选; S13(Arden · R12) 对齐阶段 F 显示集成可勾选(仅 C#;≠ T15 R10)——不改 v1.9 语义与 Diagnostic 码表 / T9 停止矩阵 / T10 事件闭集。
  3. 验收勾选:全文契约见 test-contracts.md(R4 + R6 + Arden · R9 + Arden · R10 + Arden · R11 + Arden · R12 映射,TC-* / TC-OUT-* / TC-ACQ-* / TC-SESS-* / TC-MEM-* / TC-DISP-*);与 toolflow §10.1 / T13 生命周期对照( Arden · R13 索引已对齐 OUT/ACQ/SESS/MEM/DISP + S10–S13);场景骨架映射见契约表 §12;薄指针见 validation-and-roadmap.md。不以本文代替契约表勾选正文。
  4. 禁止:第二执行引擎;C++ UI;MPF/旧 DAG 链接;把 ImageDisplay 示例改成 C++;不写覆盖率 KPI;不扩观察 UI / 脚本 / IPC / ABI。

文档状态:场景规划草案(2026-10-21,Asia/Shanghai),对齐 toolflow v1.9 / R4 契约表 / R6 S09a–d / R7 S07↔host-integration / R8 导读索引 / Arden · R9 S10↔TC-OUT- / Arden · R10 S11↔TC-ACQ-·TC-SESS- / Arden · R11 S12↔TC-MEM-* / Arden · R12 S13↔TC-DISP-(显示仅 C#;≠ T15 R10)/ Arden · R13 §10.1 T13 索引对齐(本批横切收束)。实施以代码与契约表 / §10.1 勾选为准。

本仓对拍

场景本仓
S07 无 UI 入口examples/headless_run.cpp 只走到 passthrough Run + RequestCancel
S10–S13规格与薄摘在本站;Controls.Tests / smoke 是外形,未验收
S01–S06 / S08 / S09无成套示例工程

On this page