跳到主要内容

QG 流程程序

QG 用来表达有顺序的自动化行为:调用 Object、判断条件、等待、记录状态,并处理故障和恢复。当前唯一源码格式是 .qg;编辑器的 Source 和 Flow 只是同一份程序的两种视图。

如果你第一次使用 QG,先完成QG 流程、对象调用与调试,再回来查本页。

什么时候使用 QG​

适合放进 QG 的内容:

  • 设备或业务动作的先后顺序;
  • 条件、分支、有界等待和超时;
  • 多个 Object 之间的协调;
  • App 启动、周期更新和停止时需要执行的流程;
  • 需要通过 Debug 断点观察的控制逻辑。

不适合放进 QG 的内容:

  • Page 布局和按钮外观;
  • 设备协议、寄存器解析或驱动实现;
  • 急停、安全回路或认证安全功能;
  • 项目未管理的第三方代码、动态脚本或无限后台任务。

创建并打开 QG​

  1. 在 Resources 右键 Scripts。
  2. 选择新建 QG 程序,使用清楚、稳定的文件名,例如 packaging-station.qg。
  3. 编辑器会在 graphs/ 下创建源码,并为它建立对应的 Object 实例;在 Resources → Objects 中确认 Object ID 和名称。
  4. 在 Source 编写或审阅源码;需要结构化浏览时切到 Flow。
  5. 保存后先处理当前文档的实时诊断;需要完整结果时执行 Build。

文件名(不含 .qg)会原样成为 WIT resource 名称,因此必须使用小写 kebab-case,例如 packaging-station;大写、下划线、点号和隐式改名都不接受。

每个 QG 文件开头应有一段简短 JSDoc,说明它对 App 提供的能力。描述应面向工程用途,例如“编排包装周期并处理超时恢复”,不要写实现历史或易变参数。

Source 与 Flow​

  • Source 是唯一源码,可以由人工或 AI 直接编辑。
  • Flow 从当前 Source 生成,并把数据结构、函数、条件、循环和 Object 调用投影成从上到下的纵向流程。控制流从节点顶部进入、底部流出,分支出口并排放在底部;参数和返回值的数据连接点位于两侧。App Hub 中的 QG 预览与 Editor 使用相同的节点卡片、参数字段、分支、连线和纵向布局。目前 App Hub 仅支持只读浏览,可以切换函数、选中节点、平移和缩放;修改流程或设置断点请打开 Editor。显示区域较窄时,大纲会自动收起,顶部仍可切换函数;点击大纲按钮可临时展开,选好函数后自动收起。
  • 在 Flow 中接受的修改会更新同一份 Source;不存在另一份连线文件、坐标文件或待同步草稿。
  • Source 与 Flow 使用同一组断点。源码位置变化后应重新 Build Debug,再开始新的调试会话。

如果 Flow 无法显示,先看 Source 和 Problems。上游 QG 或 Object 错误没有解决前,不要通过删除类型或猜测方法名来强行生成空流程。

程序结构​

一个 QG ObjectType 通常包含四类内容:

内容用途
declare const value propertyObject 创建时绑定的只读值属性;会出现在 QG Object 的属性配置中
顶层 let 状态当前 resource 实例自己的短期状态;重新加载实例后会重置
普通函数拆分计算和流程步骤,只在当前 QG 内使用
export function该 QG Object 对 Page、其他 QG 或在线工具公开的方法

每个 QG resource 都有固定的三个生命周期方法;源码只在需要行为时声明,省略时编译器生成空实现:

  • Init(): void:App 开始时执行一次;
  • Update(): void:运行期间由 Runtime 周期调用;
  • Dispose(): void:用户显式 Stop 时执行;执行 fault 会直接关闭当前 activation,不再运行更多 QG 代码。

不要在 QG 中创建自己的周期定时器或永久循环。把一次 Update 控制在可预测时长内,并为所有外部等待设置明确的超时和恢复路径。

数据结构与记录值​

不懂代码时,也可以在 Flow 的大纲 → 数据结构中创建 App 本地固定记录。这里展示的结构全部由当前 QG 定义,可以在右侧检查器中改名、添加或删除字段、调整字段顺序,以及选择精确的标量类型。所有修改都直接更新当前 .qg;不会生成单独的类型文件或图数据。重命名类型或字段时,编辑器会同步更新可确定的源码引用。

可用 ObjectType 的记录类型来自 Object catalog,不依赖当前是否已有该 ObjectType 的具体 Object 实例。在 Source 中显式写出 ObjectType 记录时,使用标准 TypeScript 类型导入,例如:

import type { DropSettings } from "drop.workflow";

模块字符串就是准确的 ObjectType ID。若两个 ObjectType 都导出 Settings,使用 TypeScript 别名:import type { Settings as MotionSettings } from "motion.controller";。不要复制一份同结构的本地 interface,也不存在按名称猜测或维护 schema 映射的链路。Flow 的类型选择器会列出可用 ObjectType 记录,并在选择或不再使用类型时自动维护这些导入。

Object 实例不需要默认类型导入,也不进入 QG constructor。它们统一通过按当前 App 的 Object 配置精确生成的全局 objects 访问;ObjectType 内的记录仍使用命名导入。例如:

import type { MoveSettings } from "motion.controller";

declare const speed: f64;
let cycles: u64 = u64(0);

export function Run(settings: MoveSettings): void {
objects.motor.MoveTo(speed, settings);
cycles = cycles + u64(1);
}

这里 motor 是 Resources → Objects 中真实的 Object ID,speed 是每个 QG Object 实例必须绑定的值 property,cycles 是各 resource 实例隔离的 state。若 ID 不是 TypeScript 标识符,使用 objects["line.motor"].MoveTo(...)。objects 是编译期严格生成的只读调用面,不支持动态索引、枚举、解构、赋值或保存 Object handle。

在 Flow 的插入 → 记录值中,可以使用创建记录、读取字段和设置字段节点。记录字面值会在检查器中展开为逐字段输入,普通用户不需要手写 { field: value }。添加、删除、排序或改变本地字段类型时,编辑器会迁移它能明确识别的记录字面值;如果仍有无法安全迁移的表达式,修改会被拒绝并显示诊断,不会写入无效源码。

删除仍被状态、函数或节点使用的类型会被阻止。先处理使用位置,再删除类型。

使用 Object 能力​

QG 通过全局 objects 直接调用 App 中的真实 Object 实例,例如 objects.motor.MoveTo(...)。正确流程是:

  1. 在 Resources 的 Objects 中创建并配置目标 Object 实例。
  2. 从 Flow 按真实 Object ID 分组的方法列表选择能力。
  3. 按显示的参数顺序、类型和单位传值。
  4. 保存并处理 Object 或 QG 诊断。
  5. Build 后在模拟器或受控设备状态下验证真实结果。

Object 方法必须来自当前 App 已安装的能力。不要根据旧项目、截图或类似设备猜测方法名、单位、极性和允许条件。AI 也应先检查当前 Object catalog,再只展开实际要使用的 ObjectType 接口。

常用语言规则​

QG 使用便于 TypeScript 工具理解的语法,但不是通用 JavaScript 运行环境。

需要表达的内容当前写法
布尔值bool
有符号整数i8、i16、i32、i64
无符号整数u8、u16、u32、u64
浮点数f32、f64
文本和无返回值string、void
固定记录当前 QG 的顶层 TypeScript interface,或通过 import type 引入的 ObjectType 记录
一维列表T[],支持长度、索引、赋值、push 和 for...of

关键限制:

  • 不使用 number、boolean 或 null;any、unknown 只允许作为 catch 变量的可选注解;
  • 数值转换要显式,例如 u32(value);
  • QG 函数不能声明类型参数;泛型只存在于 theseus 等导入库的调用表面,由 TypeScript 从准确的参数或接收位置推导具体类型,也可以显式填写类型实参;
  • 不使用 async、await、Promise、class 或动态模块;平台能力使用普通的 import { ... } from "theseus",Object 实例通过全局 objects 访问,ObjectType 记录只使用命名 import type;
  • Object 调用按源码顺序完成,即使底层操作需要等待,源码也不添加异步标记;
  • 循环必须有清楚的退出条件;QG 不会自动给无限循环增加迭代上限。

以编辑器诊断为准。不要用类型断言、忽略注释或生成代码绕过错误。

处理调用异常​

读取和写入等 Object 调用都可以放在 try/catch 中。C# 抛出的普通异常和 Rust Object 返回的错误都能捕获;error.message 是只读错误文本。方法名和参数仍以当前 Object catalog 为准。

import { log } from "theseus";

export function Move(): void {
try {
objects.motor.MoveTo(100);
} catch (error) {
log.error(error.message);
}
}

异常会跳过 try 中剩余语句,执行 catch,然后继续后续程序。Flow 的 try / catch 节点提供两个对应的语句区域。可在 catch 中更新已有状态、记录信息,或用 throw error; 继续向外抛出,由外层处理。

不写 catch 或继续抛出且没有外层捕获时,运行仍会停止。停止请求和底层不可恢复故障不会被 catch 捕获。目前不支持 finally 或构造、抛出任意错误值;不用错误变量时可以写 catch { ... }。

时间、日志和持久状态​

平台能力是普通的 theseus 库,不是 QG 特殊语法:

  • clock.monotonicMilliseconds():读取单调时间;
  • clock.delay(milliseconds):执行可取消等待;
  • log.debug/info/warn/error(...):写入运行日志;
  • saved.get/set:保存需要跨运行实例保留的标量或固定记录。

数学、位运算和数组使用 TypeScript 标准表面:Math.min/max/abs、& | ^ ~ << >>、[]、索引、length 和 push。限幅由 Math.min 与 Math.max 组合;计算序列用有明确边界的循环和 push 生成。

持久状态属于当前运行目标的 App 全局名称空间,不会写回 App 工程,也不会随着 Git 自动迁移。saved.get 和 saved.set 的具体类型由准确的默认值、写入值或接收位置推导,也可以写成 saved.get<T> / saved.set<T> 明确指定;推不出唯一的 QG 标量或固定记录时会直接报错。名称是普通 string 表达式。同一具体类型的名称共用一组生成的 WIT 函数。一次成功的 saved.set 不会因为后续故障自动回滚;修改状态结构时,应规划显式迁移或使用新的名称。

示例:

import { saved } from "theseus";

export function IncrementTotal(): u64 {
let total: u64 = saved.get("totalCycles", u64(0));
total = total + u64(1);
saved.set("totalCycles", total);
return total;
}

Page 需要持久值时,应调用 QG Object 的公开方法,不直接拥有另一份存储逻辑。

调试 QG​

  1. 保存 QG 和相关 Object 修改。
  2. 在 Source 或 Flow 的目标语句上设置断点。
  3. 确认 App 已 Stopped,选择 Build Debug。
  4. 点击 Debug。缺少适用 Build 时,编辑器会询问是否 Build and continue;只有确认后才继续。
  5. 从 Page、另一个 QG 或允许的在线入口触发目标方法。
  6. 命中断点后检查当前语句、参数、状态、Console 和设备反馈,再点击 Continue。
  7. 使用 Stop 结束调试,并确认清理后的设备状态。

当前提供断点和 Continue,不提供源码单步或条件断点。Debug 不会模拟设备、跳过真实输出或自动保证流程安全。

保存、Build 与运行边界​

  • 保存只更新编辑态;Flow 和实时诊断也只反映源码。
  • Build 使用磁盘上已保存的文件,并把结果部署到当前运行目标。
  • Design 不调用 Object / Query;真实方法和设备行为需在 Start / Debug 后的运行页验证。
  • Start 和 Debug 运行已部署版本;当编辑器提示 Build 时,由用户明确确认后才会先 Build 再继续。
  • 运行中不会热替换 QG 或 Object。修改后使用 Stop → 保存 → Build → Start/Debug。

常见问题​

Flow 为空或方法缺失​

先修复 Source 和 Object 的上游错误。确认源码中的 objects.id 对应 Resources → Objects 中的真实 Object ID,再重新打开 Flow;不要手写一份接口或使用旧方法名填空。

Page 找不到 QG 方法​

确认 QG 方法使用 export function、文件已保存、对应 Object 已绑定,并重新执行 Build。Page 只会获得当前已解析 Object model 中的公开方法。

断点不命中​

确认使用了当前源码对应的 Build Debug,断点位于实际执行的方法内,并且 Debug 的目标与当前编辑器绑定目标一致。

修改后设备仍是旧行为​

保存不会替换运行实例。先 Stop,再 Build 并重新 Start/Debug;同时核对顶栏运行目标。

故障后 App 回到 Stopped​

这是统一清理行为。记录故障阶段、消息、触发动作和 Console,再修复根因。不要通过反复 Start 掩盖未查明的故障。

工程与安全检查​

在连接真实设备前,逐项确认:

  • Object 身份、地址、单位、极性和量程;
  • 启动允许条件和停止后的安全状态;
  • 每个等待的超时、取消和恢复路径;
  • Init、显式 Stop 的 Dispose,以及 fault 后重新 Run 是否符合设备要求;
  • 重试是否可能重复产生不可逆副作用;
  • 急停、安全门和硬限位是否由独立、合适的安全系统保证。

QG 负责流程编排,不替代设备驱动、后端授权或认证安全控制。

相关页面​