# 02-项目程序开发详细步骤

版本：0.1
日期：2026-06-27

## 1. 开发原则

1. 先把 ABB120 fixture、测试程序、运行器和报告数据模型稳定下来，再扩展 UI。
2. 每个任务必须有自动化测试或可复核证据。
3. 运行链路以统一 GRL/IR 为中心，品牌程序只作为输入输出格式。
4. HTML 虚拟控制器先接入现有 runtime/facade，避免在 UI 中重复实现业务逻辑。
5. 对标 ABB RobotStudio 的工程流程，不复制视觉资产和品牌资源。
6. GRL 运算功能必须贯穿 parser、AST、常量求值、语义编译和 motion/path 编译，不接受只解析不求值的半成品。

## 2. 阶段 0：基准资产整理

### A120-001：固化 ABB120 URDF fixture

步骤：

1. 将 `ABB_IRB120_3_58_URDF` 从测试 fixture 抽成可复用项目 fixture，或建立明确导出入口。
2. 保留来源记录：ROS-Industrial repository、branch、entrypoint、macro。
3. 增加 fixture 校验：base/tip、DOF、joint names、joint limits、fixed links。
4. 生成 `abb120.robot.json`，包含 URDF、load options、标准关节位和测试元数据。

验收：

1. `loadRobotFromUrdf` 能加载 ABB120。
2. `getRobotInfo` 输出 6 轴链。
3. 限位和标准关节位在范围内。

### A120-002：建立测试目录结构

建议新增：

```text
kdl-wasm/web/tests/fixtures/abb120/
  abb120.robot.ts
  programs/
    A120_00_Smoke.grl
    A120_10_JointPickPlace.grl
    A120_20_CartesianBlend.grl
    A120_30_IOWaitPulse.grl
    A120_40_ErrorDiagnostics.grl
    A120_50_OperationProcess.grl
  expected/
    abb/
    fanuc/
    kuka/
    reports/
```

验收：测试能通过稳定路径读取所有 fixture。

## 3. 阶段 1：GRL 运算表达式基础

### GRL-EXPR-001：表达式 AST 扩展

当前相关文件：

1. `kdl-wasm/web/src/grl/ast/ast.ts`
2. `kdl-wasm/web/src/grl/parser/expressionParser.ts`
3. `kdl-wasm/web/src/grl/parser/parser.ts`
4. `kdl-wasm/web/src/grl/semantic/compileData.ts`
5. `kdl-wasm/web/src/grl/semantic/compileMotion.ts`

步骤：

1. 在 AST 中新增 `UnaryExpression`、`BinaryExpression`、必要时新增 `LogicalExpression` 或统一二元节点。
2. 保留现有 `NumberLiteral`、`BooleanLiteral`、`StringLiteral`、`IdentifierExpression`、`CallExpression`、`ArrayExpression`、`ObjectExpression`、`OffsetExpression`。
3. 节点必须保留 source range，方便 diagnostic 和 source map。
4. 兼容当前 `linear(300 mm/s)`、`pose(...)`、`joint_target {...}`、`offset` 语法。

验收：

1. AST snapshot 能显示 `1 + 2 * 3` 的优先级树。
2. `-(10 mm)`、`not (a == b)`、`sin(30 deg)` source range 稳定。
3. 当前 parser 测试不回退。

### GRL-EXPR-002：表达式 parser 优先级

步骤：

1. 将 `expressionParser.ts` 从当前 primary/offset 解析扩展为递归下降或 Pratt parser。
2. 支持 `+ - * / mod`、比较、逻辑、括号、一元运算。
3. 函数调用参数内部允许完整表达式，例如 `linear(100 + 50 mm/s)`。
4. array/object 属性值允许完整表达式，例如 `joints: [(10 + 5) deg, -90 deg]`。
5. offset 轴值允许常量表达式，例如 `offset z (5 + 5) mm`，并在语义阶段求值。

验收：

1. `linear(100 + 50 mm/s)` 可 parse。
2. `pose(400 + 50 mm, 20 * 2 mm, sqrt(90000) mm, 0 deg, 0 deg, atan2(1, 1))` 可 parse。
3. `wait io.di[1] == true and not io.di[2] timeout 1 + 1 s` 的条件和 timeout 均可保留 source range。
4. 非法表达式输出 `GRL_EXPR_PARSE` 或现有 parse error，并定位行列。

### GRL-EXPR-003：常量求值器和单位系统

建议新增：

```text
kdl-wasm/web/src/grl/semantic/constantExpression.ts
kdl-wasm/web/tests/grl/expressionCompile.test.ts
```

步骤：

1. 实现 `evaluateConstantExpression(expression, context)`。
2. context 提供符号表、期望单位维度、默认单位和 diagnostic sink。
3. 支持 `pi`、`e`。
4. 支持 `sin/cos/tan/asin/acos/atan/atan2/sqrt/abs/pow/min/max/clamp/floor/ceil/round`。
5. 支持 `mm/mm/s/mm/s2/deg/rad/%/s/ms/kg` 归一化。
6. 做除零、函数定义域、单位不兼容、未知符号、未知函数和非常量表达式诊断。

验收：

1. `100 + 50 mm/s` 在速度上下文求值为 `0.15 m/s`。
2. `(10 + 5) deg` 在角度上下文求值为 `0.261799... rad`。
3. `clamp(20 mm, 1 mm, 10 mm)` 求值为 `0.01 m`。
4. `sqrt(-1)` 输出 `GRL_EXPR_DOMAIN`。
5. `10 mm + 2 s` 输出 `GRL_EXPR_UNIT_MISMATCH`。

### GRL-EXPR-004：数据声明和 target 编译接入

步骤：

1. 在 `compileData.ts` 中把原来只接受 `NumberLiteral` 的位置改为常量表达式求值。
2. 覆盖 `tool.tcp`、`tool.mass`、`frame.origin`、`speed`、`zone`。
3. 覆盖 `joint_target.joints`、`pose_target.pose`、`poseq`、`robot_config`。
4. 变量引用只允许引用已知 const/persistent 常量或已编译数据符号；需要静态值的位置遇到运行态变量报 `GRL_EXPR_NON_CONSTANT`。

验收：

1. `const speed v = linear(100 + 50 mm/s)` 编译为 `0.15 m/s`。
2. `const zone z = z(max(5 mm, 10 mm))` 编译为 `0.01 m`。
3. `target home = joint_target { joints: [(10 + 5) deg, -90 deg] }` 编译为弧度数组。
4. `target pick = pose_target { pose: pose(400 + 50 mm, 0 mm, sqrt(90000) mm, 0 deg, 0 deg, atan2(1, 1)) }` 编译通过。

### GRL-EXPR-005：motion/path/IO 编译接入

步骤：

1. 在 `compileMotion.ts` 中把内联 `speed`、`zone`、path defaults/source 属性改为常量表达式求值。
2. path point 的 `via`、`target`、`tool`、`frame` 若使用内联结构表达式，必须支持内部数值表达式。
3. path event `distance` 支持表达式。
4. IO/wait/pulse 中的 `timeout`、`duration` 支持表达式，归一化为秒。
5. runtime 条件中的比较/逻辑表达式至少保留结构化 AST 或稳定文本，并能在虚拟控制器 trace 中显示。

验收：

1. `point p0 movej home speed linear(100 + 50 mm/s) zone z(5 + 5 mm)` 编译到 KDL request。
2. `event at p0 distance 5 + 5 mm ...` 编译为 `0.01 m`。
3. `wait io.di[1] == true timeout 1 + 1 s` timeout 为 `2 s`。
4. `pulse io.do[1] duration 50 + 50 ms` duration 为 `0.1 s`。

### GRL-EXPR-006：三品牌后处理和报告边界

步骤：

1. 后处理器优先使用已求值 IR，不输出未折叠常量表达式。
2. ABB/FANUC/KUKA 输出速度、zone、pose、joint 值时，使用各品牌可接受的具体数值。
3. runtime 条件表达式如无法保真输出，转换报告记录 `GRL_EXPR_UNSUPPORTED_RUNTIME`。
4. roundtrip report 标注表达式已折叠，回读结果以数值等价为准。

验收：

1. `linear(100 + 50 mm/s)` 在 ABB/FANUC/KUKA 输出中体现为具体速度。
2. `z(clamp(...))` 输出具体 zone/CNT/C_DIS 近似，并在 report 记录近似。
3. 后处理 report 不允许静默丢失 runtime 逻辑表达式。

## 4. 阶段 2：通用机器人测试程序

### A120-010：编写 GRL 程序集

步骤：

1. 按 `01-项目功能内容.md` 第 3 章创建 6 个 GRL 程序。
2. 每个程序声明 `module`、`speed`、`zone`、`tool/frame` 需求和 source metadata。
3. 关节目标使用 fixture 标准关节位。
4. pose 目标由 FK 自动生成，避免手写不可达姿态误判。
5. 故障程序单独放入 error suite，不进入 happy path 汇总。

验收：

1. 所有 happy path 程序可 parse。
2. error suite 输出稳定 diagnostic。
3. source map 覆盖每个 motion、wait、io、operation。

### A120-011：建立程序 golden snapshot

步骤：

1. 保存 AST snapshot。
2. 保存 Semantic IR snapshot。
3. 保存 KDL bridge request snapshot。
4. 保存 source map snapshot。

验收：同一输入重复生成一致；非预期 diff 会导致测试失败。

## 5. 阶段 3：运行器和 KDL 全面测试

### A120-030：ABB120 KDL 基线测试

步骤：

1. 加载 ABB120 URDF。
2. 执行 `fk(home)`，校验 TCP 位置和姿态。
3. 执行 `fkAllLinks(home)`，校验 link 顺序。
4. 执行 `jacobian(pick)`，校验矩阵维度和有限值。
5. 执行 `checkJointLimits`，覆盖合法和非法关节。
6. 执行 `checkSingularity`，输出 manipulability 和 conditionNumber。

验收：测试报告包含 robot info、FK、link poses、Jacobian、limits、singularity。

### A120-031：轨迹规划测试

步骤：

1. 对 `A120_10_JointPickPlace` 执行 `planPath`。
2. 对 `A120_20_CartesianBlend` 执行 `planMoveL/planMoveC`。
3. 对故障路径执行 `validatePath`。
4. 输出轨迹点数、duration、max velocity、max acceleration、blend diagnostics。

验收：happy path `ok=true`；error path 有稳定 `code/severity/sourceMap`。

### A120-032：虚拟控制器运行测试

步骤：

1. `compileSemanticProgram` 得到 IR。
2. `VirtualController.load` 加载程序。
3. 分别执行 run、pause、step、stop、reset。
4. Motion Queue 绑定 KDL planner。
5. IO runtime 注入 DI script。
6. 记录 trace、program counter、scope、alarm、wait、motion activeIndex。

验收：控制器状态机合法；trace 可回放；wait 卡住和 timeout 可诊断。

### A120-033：job runner

步骤：

1. 新增 `runAbb120Suite` 或 CLI 脚本。
2. 输入：suite id、程序列表、robot fixture、输出目录。
3. 输出：`job_id`、`job.json`、`trace.json`、`trajectory.json`、`diagnostics.json`。
4. job id 格式：`A120-JOB-YYYYMMDD-HHMMSS-<short-hash>`。

验收：CI 可运行并生成稳定证据目录。

## 6. 阶段 4：跨品牌转换和回读

### A120-050：三品牌后处理 golden

步骤：

1. 对每个 happy path GRL 编译 IR。
2. 调用 `postProcessAllBrands`。
3. 生成 ABB `.mod`、FANUC `.ls`、KUKA `.src/.dat`。
4. 保存 golden 文件。
5. 生成 post report。

验收：

1. ABB 输出包含 `MoveJ/MoveL/MoveC/SetDO/WaitUntil/PulseDO`。
2. FANUC 输出包含 `J/L/C/DO/WAIT/PULSE`。
3. KUKA 输出包含 `PTP/LIN/CIRC/$OUT/WAIT FOR`。
4. unsupported/approximation 不允许静默丢失。

### A120-051：品牌导入回读

步骤：

1. 将后处理输出重新导入。
2. 对比 target 数量、motion 顺序、operation 结构。
3. 对不可逆语义生成 diff report。
4. 输出 `roundtrip.json`。

验收：happy path 结构等价；近似项全部进入 report。

## 7. 阶段 5：离线编程全面测试

### A120-070：OLP 项目生成

步骤：

1. 根据 ABB120 fixture 创建 Station。
2. 添加 Robot、Tool、Frame、Target、Path、Operation、Program。
3. 保存到 workspace。
4. 导出 bundle，再导入恢复。

验收：恢复后项目对象等价，manifest 完整。

### A120-071：验证报告

步骤：

1. 生成 reachability report。
2. 生成 cycle time report。
3. 生成 IO/Wait report。
4. 生成 collision report。
5. 生成 post/import report。
6. 汇总为 HTML report，预留 PDF/print。

验收：每个 report 有 `report_id`、status、summary、sections、diagnostics、source map。

### A120-072：客户交付包

步骤：

1. 打包 GRL 源程序。
2. 打包 ABB/FANUC/KUKA 输出程序。
3. 打包 IO map、校准数据、trace、trajectory、report。
4. 输出 bundle manifest。

验收：交付包可解包并追溯每个输出来源。

## 8. 阶段 6：HTML 虚拟控制器

### A120-090：页面骨架

建议新增：

```text
kdl-wasm/web/app/virtual-controller.html
kdl-wasm/web/app/virtual-controller.css
kdl-wasm/web/app/virtual-controller.js
kdl-wasm/web/scripts/verify-virtual-controller.mjs
```

步骤：

1. 页面直接进入 ABB120 工作台。
2. 顶部命令栏、左侧对象树、中央 station viewport、右侧示教器、底部诊断面板。
3. 接入 WorkbenchFacade 和 VirtualController snapshot。
4. 无数据时显示空工程状态；加载 fixture 后显示对象树和程序。

验收：浏览器直接打开 HTML 即可看到 ABB120 项目工作台；`npm run verify:virtual-controller` 能生成桌面和移动截图证据。

### A120-091：运行控制

步骤：

1. 实现 Load、Run、Pause、Step、Stop、Reset。
2. 显示状态灯、mode、motors、program pointer、cycle time。
3. 显示 joint/TCP 值。
4. Motion Queue 与时间轴同步。

验收：按钮触发 runtime 状态变化，页面数据同步。

### A120-092：编辑器和 source map

步骤：

1. GRL/ABB/FANUC/KUKA 多标签只读查看。
2. 当前执行行高亮。
3. 点击诊断跳转源码。
4. 点击 path point 高亮 viewport 和 source。

验收：source map 双向定位可用。

### A120-093：IO、Wait、报警和报告

步骤：

1. IO 面板显示 DI/DO 当前值和最近事件。
2. wait 面板显示表达式、elapsed、remaining timeout、子表达式值。
3. alarm 面板显示 severity、code、source。
4. report 面板打开 HTML report。

验收：wait 卡住时可以定位并手动置位 DI 继续运行。

### A120-094：视觉验证

步骤：

1. 使用 Playwright 打开 HTML 页面。
2. 截图桌面和移动视口。
3. 如果有 canvas/3D，做非空像素检查。
4. 保存截图到证据目录。

验收：页面无重叠、核心文字不溢出、主要面板可见。

## 9. 阶段 7：全量验收

### A120-900：CI 全量命令

命令：

```bash
cd /home/meswork/kdl_work
npm run typecheck
npm test
cmake --build kdl-wasm/build-wasm -j16
```

新增后还应包含：

```bash
npm run test -- abb120
npm run test -- post import workbench controller runtime reports
```

验收：所有测试通过，`05-验收证据.md` 记录 job/report/screenshot 路径。
