运行如何流动
一次运行从 开始 开始,沿接线从一个节点到下一个节点,当每个分支都完成且到达 结束 时结束。本页说明它遵循的规则;每个节点做什么见节点参考,随它流动的值见数据。
开始与结束
一个实验恰好有一个 开始 和一个 结束。
- 开始 没有输入。它立即通过,其时间线中的 行给出本次运行的种子。它的输出可以有多条接线:实验于是 以并行分支开始。
- 到达 结束 的每个分支都在那里停止。End 从第一次 到达起就显示为运行中,并在最后一个分支完成后通过一次——如果有任何步骤失败则 完全不通过。正是这次通过使运行 通过。
- 每个分支都无错误完成、但没有一个到达 End 的运行 会以
run.no_end失败。
输出与接线
节点的步骤以选择一个输出结束,运行会沿该输出的每一条接线继续。大多数节点有一个输出,输出;有些在多个之间选择:
| 节点 | 必须接线的输出 | 可以接线的输出 |
|---|---|---|
| 结束 | — | — |
| 并行分支 | 分支 1、分支 2 | — |
| 按状态分支、按值分支 | 是、否 | — |
| 所有等待节点(等待 OSC、等待 UDP、等待 MQTT、等待 HTTP 请求、等待 WebSocket) | 已匹配 | 超时 |
| 循环 | 循环体、完成 | 上限 |
| 其他所有节点 | 输出 | — |
要连接,从一个输出拖到一个节点上;拖放到空画布上,会在那里添加一个新节点。从一个已有接线的输出拖出会再添加一条接线。添加下一步、A 键以及接线上的 + 则把节点插入到现有接线中。
未完成的图是草稿:它会保存,但不会运行。工具栏中的 请补全流程图 说明缺少什么并显示该节点。参见运行前检查的内容。
并行分支
一个输出引出多条接线
当一个输出有多条接线——包括 开始 的——它们所通向的每个节点都同时运行。第一条接线延续该分支;每一条后续接线启动一个并行分支。每个分支都携带自己的变量副本和最近一次 HTTP 响应副本,因此一个分支设置或收到的内容不会被其他分支看到。
并行分支与 Join
并行分支 立即通过,并同时从 分支 1 和 分支 2 离开——与一个输出引出两条接线相同,只是画成了一个节点。
汇合分支 等待通向它的每一条接线,然后作为一个分支继续,其副本按这些接线的顺序合并:
- 它们全部的变量——两条分支都设置的同名值,实验中后列出的接线胜出;
- 按该顺序,最后一条带来 HTTP 响应的接线的响应;
- 对于其后的等待节点,它们最近一次动作中最早的那个。
接线的顺序决定一切,绝不取决于哪个分支碰巧先完成。
只 Join 并行运行的分支
Join 会统计它的接线,无论它们是如何变成并行的:来自一个 并行分支、来自一个输出的多条接线、来自不同的路径。分支的 是 和 否 之后只有一条路径运行,因此由两者共同汇入的 Join 会等待一个永远不会到来的分支:运行会以 run.join_waiting 失败,并指出有多少条接线从未被跟随。要把可选路径汇合,请把它们直接接到下一个节点。
被两条并行分支到达的非 Join 节点,会为它们各运行一次。
当步骤失败时
第一个失败使运行失败。其他分支不会开始新步骤:重复或负载提前结束,它们正在进行的任何其他步骤会运行到结束。它们期间遇到的一次失败会在时间线中报告,但不是本次运行的错误。在有 超时 接线的情况下遇到超时的步骤没有失败——参见等待。
分支
| 节点 | 何时从 是 离开 |
|---|---|
| 按状态分支 | 此路径上最近一次 HTTP 响应的状态为给定状态 |
| 按值分支 | 其比较成立——参见比较值 |
否则各自从 否 离开。状态分支在每条路径上都需要它之前有一个 HTTP 请求;按值分支需要它读取的名称在那里已知。当 是 和 否 之后的路径再次汇合时,汇合处的节点只运行一次,且在那里只已知两条路径都设置的变量(变量在哪里可见)。
重试
发送或监听的步骤可以在失败时再试:在其属性中开启 失败时重试。
| 设置 | 内容 | 范围 | 初始值 |
|---|---|---|---|
| 尝试次数 | 总共尝试次数,包括第一次 | 1–10 | 3 |
| 暂停(ms) | 第二次尝试前的暂停 | 0–60 000 ms | 500 |
| 暂停方式 | 固定:每次暂停相同;倍增:每次暂停是上一次的两倍 | — | 固定 |
- 重试适用于 HTTP 请求、TCP 消息、 MQTT 发布、OSC 消息、UDP 数据报、 WebSocket 连接、WebSocket 发送 以及所有等待节点。其他 节点拒绝它(
node.retry_unsupported),处于 负载下的 HTTP 请求不接受重试。 - 无论翻倍后是多少,单次暂停都不超过 60 s。
- 每次失败的尝试都会在时间线中显示为 重试中,带其 次数和原因。步骤随后通过,或以最后一次尝试的原因失败。
- 只有执行被重复。模板无法解析的字段会立即 失败。
- 等待回复的发送会再次发送。等待节点再次等待,像之前一样 从分支上最近一次动作起计数。
- 带有 超时 接线的等待节点不会因超时而失败,因此 不会被重试:它沿 超时 继续。
- 停止会立即结束暂停。
重复
发送的步骤可以反复发送——心跳、轮询、稳定流——而无需在图中有循环:开启 重复发送。
| 设置 | 内容 | 范围 | 初始值 |
|---|---|---|---|
| 重复 | 指定次数 或 指定时长 | — | 指定次数 |
| 次数 | 总共发送次数,包括第一次 | 2–10 000 | 10 |
| 时长(ms) | 从第一次发送起持续发送多久 | 1–300 000 ms | 10 000 |
| 间隔(ms) | 两次发送之间的暂停 | 10–60 000 ms | 1000 |
| 抖动(ms) | 每次暂停随机延长至多此值 | 0–60 000 ms | 0 |
- 重复适用于 HTTP 请求、TCP 消息、 MQTT 发布、OSC 消息、UDP 数据报 和 WebSocket 发送(其他位置为
node.repeat_unsupported)。HTTP 请求要么重复,要么负载,不能两者兼有。 - 每次发送都像单次发送那样进行:其模板会被重新读取——
{{counter}}是发送的编号,{{now}}是其时间——重试开启时 适用于每次发送。等待回复的发送等待自己那一次。 - 按时间重复时,只有当一次发送能在时间结束前开始时才会发出。
- 抖动从本次运行的种子中抽取:相同的种子给出相同的 暂停。
- 时间线最多每秒一次将进度报告为 重复中。 步骤在最后一次发送后以该次发送的结果通过;彻底失败的发送使步骤失败。
- 另一个分支的失败会结束发送;停止会立即结束暂停。
它必须能放进一次运行:发送次数及其最长暂停 ((count − 1) × (interval + jitter))合计最多 300 s(node.repeat_too_long), 按时间重复最多发送 10 000 次(node.repeat_too_many)。
循环
循环 反复运行其 循环体 输出上的步骤;其中最后一个接回循环节点。
| 设置 | 内容 | 范围 |
|---|---|---|
| 最多迭代次数 | 最多迭代次数 | 1–1000 |
| 满足条件时提前停止 | 退出条件:值、条件、预期值,与 检查值 中相同 | 可选 |
- 从外部到达时,循环节点在 循环体 上开始第 1 次迭代。
- 每次循环体返回时读取退出条件——在迭代之后,因此循环体 至少运行一次,并能设置它要测试的内容。
- 条件成立时,循环节点从 完成 离开。
- 否则开始下一次迭代,只要还有剩余。
- 迭代次数先用尽时,如果 上限 已接线,循环节点从它离开, 否则以
loop.limit使运行失败。没有条件时,循环体运行每一次迭代, 循环节点从 完成 离开。
在循环体内部,{{counter}} 是迭代的编号,因为每个节点 统计自己的执行次数。条件和 完成 或 上限 之后的步骤 可以使用循环体每次迭代都设置的内容——例如循环体提取的状态; 循环体本身只能看到到达循环节点时已知的内容。
模板 轮询直到就绪 每 0.3 s 向设备询问其状态,直到它应答 ready,最多 10 次。
循环体可以包含什么
循环体作为一个分支运行,一次迭代接一次迭代。接回循环节点的接线是实验唯一可以有的环;其他任何环都是 graph.cycle。
| 规则 | 错误 |
|---|---|
| 循环体 上的内容接回循环节点 | loop.no_return |
| 循环体中的每个输出只有一条接线 | loop.body_parallel |
| 循环体中的每个输出都继续留在循环体中或接回循环节点 | loop.body_leaves |
| 只有循环节点的 循环体 输出通向循环体 | loop.body_entered |
| 循环体中不能有 开始、结束、并行分支、汇合分支 或其他 循环 | loop.body_unsupported |
等待
等待节点在所等待的消息到达时通过: 等待 OSC、等待 UDP、等待 MQTT、 等待 HTTP 请求 和 等待 WebSocket。每个匹配什么见 节点参考;这里说明它们如何监听。
从开始就监听
运行在其第一个步骤之前就打开其等待节点所监听的内容,因此比下一个步骤更快的回复不会被错过:
| 等待 | 在第一个步骤之前打开 |
|---|---|
| OSC、UDP | 每个 监听于(IP:端口) 地址一个 UDP 套接字,由使用该地址的每个等待节点共用 |
| HTTP 请求 | 每个地址一个监听器——有本次运行的 HTTP 模拟器 时就是它,否则是一个以 204 应答的监听器 |
| MQTT | 每个代理和主题过滤器一个连接,已订阅;代理随后重放的保留消息会被忽略 |
| WebSocket | 什么都没有:它读取 WebSocket 连接 运行时打开的连接 |
因为它们最先打开,这些地址在运行前就已固定:OSC、 UDP 或 HTTP 等待节点监听字面的 IP:port,端口不能为 0,MQTT 等待节点的代理和主题只接受参数。无法打开的端口——被 占用,或不是本机的地址——会在任何流量之前、在该等待节点的字段处停止运行。运行以任何方式结束时,一切都会关闭。
哪些消息计数
等待节点考虑的是在其分支上最近一次动作开始之后到达的消息——最近一次请求、消息、发布、WebSocket 连接或 发送——或者在没有任何动作之前,运行开始之后到达的消息。请求之前的消息不计数,请求与等待之间的延时或日志不会隐藏其回复。Join 之后,合并分支中最早的那个最近动作计数。
等待节点取第一个匹配的消息并消费它:两个等待节点绝不会 匹配同一条消息。
每个套接字、订阅或连接为其等待节点最多保留 1024 条消息和 64 MiB;超过后,最旧的会被丢弃并计数。
超时
超时(ms) 为 1–120 000 ms,初始为 2000。当时间内没有任何匹配时:
- 有 超时 接线时,等待节点沿它继续;
- 没有时,步骤以
wait.timeout失败,它说明期间还到达了多少条其他 消息——错误的模式与设备沉默看起来不同——并在其详情中说明队列满时丢弃了多少条更旧的消息。
等待节点的变量——reply,HTTP 为 request——只在 已匹配 之后存在。当 检查器 正在捕获时,步骤 还会链接它匹配到的帧:参见时间线。
同一步骤上的回复
OSC 消息 或 UDP 数据报 可以等待自己的应答:开启 等待回复。
| 设置 | 内容 | 初始值 |
|---|---|---|
| 接收回复于(IP:端口) | 等待应答的地址;端口 0 表示任意空闲端口 | 0.0.0.0:0 |
| 回复地址模式(OSC)、回复载荷(UDP) | 应答必须是什么,与匹配等待节点中相同 | 任意 |
| 超时(ms) | 1–120 000 ms | 2000 |
| 回复变量 | 应答写入的变量 | reply |
接收回复于(IP:端口) 上的套接字像等待节点的一样在第一个步骤之前打开,消息从它发出:向发送方自身端口应答的设备能被听到,向固定端口应答的设备在该端口正是给定端口时能被听到。步骤在收到匹配的应答后通过,变量在其输出之后存在。没有 超时 输出:时间内没有应答会使步骤失败,重试可以再次发送。要按沉默分支,请使用单独的等待节点。
运行前检查的内容
编辑器在您编辑时检查实验;运行按钮会再检查一次。问题会指出节点,有字段时也指出字段。
| 规则 | 错误 |
|---|---|
| 实验有名称 | doc.name_required |
| 1–64 个节点,恰好一个 Start 和一个 End | doc.node_count、doc.start_end_count |
| Start 没有输入 | graph.start_input |
| 接线通向另一个存在的节点 | doc.connection_invalid |
| 同一条接线不会出现两次 | doc.connection_duplicate |
| 每个必须接线的输出都已接线 | graph.outputs_required |
| 节点在它没有的输出上没有接线 | graph.port_unexpected |
| 每个节点都能从 Start 到达 | graph.unreachable |
| 除了 Loop 接回的接线外没有环 | graph.cycle,以及循环体规则 |
| 检查或 Extract 在每条路径上其之前都有 HTTP 请求;负载下的请求不计数 | graph.needs_http |
| 每个模板都能解析,且它使用的每个名称在每条路径上都已知 | template.*、name.*——参见数据 |
| 每个字段都存在且在范围内 | node.* |
| 引用另一个节点的节点——更改损伤、模拟器停机/恢复、WebSocket 节点——引用了一个存在的节点,且 WebSocket 节点在其 connect 之后 | impair.relay_unknown、emulator.node_unknown、ws.connection_unknown、ws.connection_after |
| 本次运行的两个套接字不共用端口 | 参见故障 |
然后运行会检查它开始所需的条件:每个机密已存储,每个端口已打开。在一切成立之前,不会运行任何步骤,也不会发送任何内容。
时间限制
一次运行最多持续 300 s。届时仍在进行的会被停止并以 run.timeout 失败。从命令行和 API 可以设置更短的限制,1–300 s。
停止
运行进行期间,运行按钮是 停止。停止会立即结束运行:每个分支、重试或重复的每次暂停、每个等待和每个负载——在途的请求会被放弃。它的套接字、订阅、模拟器和中继会关闭,其 WebSocket 连接会发送关闭帧。顶栏中的 全部停止 对每个任务做同样的事。被停止的运行不保存报告;参见运行。