chore: close wasm status contract work

This commit is contained in:
wangdequan
2026-07-08 09:20:47 -04:00
parent 97732ceb0b
commit e69333972c
69 changed files with 20435 additions and 495 deletions

View File

@@ -0,0 +1,218 @@
# 07-emctaskmain 周期对标蓝图
## 对标目标
本蓝图把 LinuxCNC `emctaskmain.cc` 的 task 主循环拆成 `wasm-port` 可逐步实现的周期阶段。后续实现不得只在 JSON status 层拼接 motion而必须让 task 周期本身读取并消费 motion snapshot。
## 上游周期源代码锚点
| 上游位置 | 行为 | WASM 对标要求 |
| --- | --- | --- |
| `emctask_startup()` motion 初始化 | `emcMotionInit()` 后立即 `emcMotionUpdate(&emcStatus->motion)`,失败则启动失败 | `lctask_init_session()` 必须初始化 motion runtime并建立第一份 task 持有的 motion snapshot |
| 主循环 `task_beat++` | task heartbeat 每周期递增 | `lctask_run_cycles()` 每个 task cycle 增加 task heartbeat/cycle |
| `check_ini_hal_items()` | 周期检查 INI/HAL 项 | standalone 可先记录为 shim 阶段,后续接 HAL/INI 检查 |
| `emcCommandBuffer->read()` | 读取新 task command成功后清 `taskPlanError/taskExecuteError` | JSON C ABI command 应先进入 command buffer/slot再由 task cycle 消费 |
| `emcTaskPlan()` | 处理 task state/mode/interpreter/read-ahead/MDI 队列 | 当前 `lctask_send_command_json()` 的状态变化要迁到 plan 阶段 |
| `emcTaskExecute()` | 根据 `execState` issue command 或等待 motion/io | 当前直接 forward motion command 的逻辑要迁到 execute 阶段 |
| `emcMotionUpdate(&emcStatus->motion)` | 每周期读取 motion 状态 | 每个 `lctask_run_cycles()` 周期必须执行结构化 `lcmot_read_snapshot()` 等价调用 |
| volatile home sync | motion enabled -> disabled 时 unhome volatile joints | standalone 先保留字段/事件,后续接 joints/homing |
| estop subordinate sync | io estop 时 disable traj、abort task/io/spindle、synch plan | 需要 task 周期内根据 `emcStatus.io``emcStatus.motion` 统一处理 |
| motion/io error sync | soft-limit、motion error、io hard fault 驱动 abort cleanup | 必须新增 motion error 注入测试 |
| `emcTaskUpdate(&emcStatus->task)` | 刷新 task status | WASM status snapshot 要从 `emcStatus.task` 等价结构导出 |
| top-level status aggregation | 根据 task/motion/io 状态设置 `emcStatus.status``task.status` | JSON status 中的 task top-level status 必须由聚合结果生成 |
| `emcStatusBuffer->write(emcStatus)` | 写出完整状态 | WASM 中写入 last status snapshotSDK read 只读该 snapshot |
## WASM 周期骨架
目标伪代码:
```cpp
int lctask_run_cycles(long task_period_ns, long servo_period_ns, int task_cycles)
{
for each task cycle:
task_heartbeat++;
task_cycle_begin();
check_ini_hal_items_shim();
command_read_result = wasm_task_command_buffer_read();
if (command_read_result.new_command) {
taskPlanError = false;
taskExecuteError = false;
}
if (wasm_emcTaskPlan() != 0) {
taskPlanError = true;
}
if (wasm_emcTaskExecute() != 0) {
taskExecuteError = true;
}
wasm_emcMotionUpdate(&emcStatus.motion);
wasm_sync_subordinate_states();
wasm_emcTaskUpdate(&emcStatus.task);
wasm_update_top_level_status();
wasm_status_buffer_write(emcStatus);
lcmot_step_servo(servo_period_ns, servo_per_task);
}
```
说明:
- 上游 motion 是独立 realtime 子系统task 读的是 motion 已产生的状态。
- WASM standalone 需要 deterministic servo step。若先 step servo 再 motion update 才能模拟“读取最新子系统状态”,必须在 `06-决策记录.md` 明确差异,并用测试固定语义。
- 无论 servo step 放在周期前还是周期后,`emcTaskExecute()` 的判断必须读取 task 持有的上一份/本份 motion snapshot不允许临时解析 status JSON。
## `EMC_STAT` 字段映射优先级
第一优先级字段用于让 task 主循环能按 LinuxCNC 结构运行:
| LinuxCNC 字段 | 上游定义 | 当前/目标 WASM 来源 | 用途 |
| --- | --- | --- | --- |
| `EMC_STAT.task.state` | `EMC_TASK_STAT.state` | 当前 `TaskRuntime.state` -> `emcStatus.task.state` | ESTOP/OFF/ON gate |
| `EMC_STAT.task.mode` | `EMC_TASK_STAT.mode` | 当前 `TaskRuntime.mode` -> `emcStatus.task.mode` | MANUAL/MDI/AUTO gate |
| `EMC_STAT.task.execState` | `EMC_TASK_STAT.execState` | 当前 `exec_state` -> enum | execute 状态机 |
| `EMC_STAT.task.interpState` | `EMC_TASK_STAT.interpState` | 当前 `interp_state` -> enum | read-ahead/pause/run |
| `EMC_STAT.task.currentLine` | `EMC_TASK_STAT.currentLine` | motion plan/interp list line | motion id、UI line |
| `EMC_STAT.task.readLine` | `EMC_TASK_STAT.readLine` | interpreter read line | read-ahead 对标 |
| `EMC_STAT.task.motionLine` | `EMC_TASK_STAT.motionLine` | motion snapshot `programLine/id` | 当前 motion line |
| `EMC_STAT.task.task_paused` | `EMC_TASK_STAT.task_paused` | pause/step state | pause/resume/step |
| `EMC_STAT.motion.status` | base `RCS_STAT_MSG.status` | `lcmot` snapshot status | wait/error/status aggregation |
| `EMC_STAT.motion.traj.enabled` | `EMC_TRAJ_STAT.enabled` | state ON/off + motion runtime | estop/disable edge |
| `EMC_STAT.motion.traj.inpos` | `EMC_TRAJ_STAT.inpos` | `lcmot.in_position` | wait-for-motion |
| `EMC_STAT.motion.traj.queue` | `EMC_TRAJ_STAT.queue` | `lcmot.queue_count` | queue/status aggregation |
| `EMC_STAT.motion.traj.activeQueue` | `EMC_TRAJ_STAT.activeQueue` | active motion depth | queue/status aggregation |
| `EMC_STAT.motion.traj.queueFull` | `EMC_TRAJ_STAT.queueFull` | queue capacity check | `WAITING_FOR_MOTION_QUEUE` |
| `EMC_STAT.motion.traj.id` | `EMC_TRAJ_STAT.id` | `lcmot.motion_id` | stepping/current line |
| `EMC_STAT.motion.traj.paused` | `EMC_TRAJ_STAT.paused` | `lcmot.paused` | pause/step |
| `EMC_STAT.motion.traj.single_stepping` | `EMC_TRAJ_STAT.single_stepping` | `lcmot.stepping` | step |
| `EMC_STAT.motion.traj.actualPosition` | `EMC_TRAJ_STAT.actualPosition` | `lcmot.axis_fb` mapped pose | interpreter sync/start line |
| `EMC_STAT.motion.traj.position` | `EMC_TRAJ_STAT.position` | `lcmot.axis_cmd` mapped pose | UI/canon endpoint |
| `EMC_STAT.motion.on_soft_limit` | `EMC_MOTION_STAT.on_soft_limit` | future injection/runtime flag | soft-limit path |
| `EMC_STAT.io.status` | `EMC_IO_STAT.status` | initial shim `DONE`/future IO runtime | wait/error aggregation |
| `EMC_STAT.io.aux.estop` | `EMC_IO_STAT.aux.estop` | task state/IO shim | estop subordinate sync |
第二优先级字段在 program execution 更深入时补齐:
- `activeGCodes``activeMCodes``activeSettings`
- `g5x_offset``g92_offset``rotation_xy``toolOffset`
- `synch_di``synch_do``analog_input``analog_output`
- spindle orient state/fault
- toolchanger fault/reason
## command read 对标
当前 `lctask_send_command_json()` 直接修改 task state 或 forward motion。目标结构
1. `lctask_send_command_json()` 只把 JSON 解析成 task command envelope写入 WASM command buffer。
2. `lctask_run_cycles()` 内的 command read 阶段消费一条 command。
3. command read 成功后清 `taskPlanError``taskExecuteError`
4. command 的语义处理转入 `wasm_emcTaskPlan()``wasm_emcTaskIssueCommand()`
验收:
- 调用 `sendCommand()` 后不调用 `runCycles()`task state 不发生 LinuxCNC 语义变化。
- 调用一次 `runCycles()`command 被计划/执行,状态变化出现。
## plan 对标
第一阶段 plan 只迁移现有已支持命令:
- `EMC_TASK_SET_STATE`
- `EMC_TASK_SET_MODE`
- `EMC_TASK_PLAN_RUN`
- `EMC_TASK_PLAN_PAUSE`
- `EMC_TASK_PLAN_RESUME`
- `EMC_TASK_PLAN_STEP`
- `EMC_TASK_ABORT`
- `EMC_TASK_PLAN_EXECUTE`
- `EMC_JOINT_HOME`
- `EMC_JOG_INCR`
要求:
- plan 只决定 task/interp/exec 状态和待执行命令,不直接 step motion。
- AUTO run 必须通过 state/mode/home/program/motion-plan gates。
- MDI/JOG/HOME 只生成后续 execute 要 issue 的 command。
## execute 对标
第一阶段 execute 对标 `emcTaskExecute()` 的 motion 相关分支:
- `DONE`:从 interp/MDI/jog/home pending command 取命令,检查 `motion.traj.queueFull`issue command 或进入 wait。
- `WAITING_FOR_MOTION_QUEUE`:直到 `motion.traj.queueFull == false` 才继续。
- `WAITING_FOR_MOTION``motion.status == ERROR` 进入 `ERROR``motion.status == DONE` 进入 `DONE`
- `WAITING_FOR_MOTION_AND_IO`motion/io 都 DONE 才完成,任一 ERROR 则 ERROR。
- `ERROR`:执行 abort cleanup清 pending command/interp list重置 interp/exec state。
暂缓:
- `WAITING_FOR_SYSTEM_CMD`:外部进程 runtime boundary。
- spindle orient 深度行为:先保留字段和测试入口,后续接 spindle snapshot。
- toolchanger fault 深度行为:先由 IO shim 固定 DONE。
## motion update 对标
必须新增结构化边界,建议名称:
```c
typedef struct LcmotStatusSnapshot { ... } LcmotStatusSnapshot;
int lcmot_read_status_snapshot(LcmotStatusSnapshot *out);
```
要求:
- `lcmot_read_status_snapshot()` 不推进 servo不消费队列只复制当前 motion runtime 状态。
- `wasm_emcMotionUpdate(&emcStatus.motion)` 只把 `LcmotStatusSnapshot` 映射到 `EMC_MOTION_STAT` 等价结构。
- `lctask_read_status_json()` 只导出最后一次 `wasm_status_buffer_write()` 的结果。
验收:
- 先写入 motion command不跑 task cyclestatus 不应被 task semantic 更新。
- 跑 task cycle 后task-held `motion.traj.queue/queueFull/id/inpos/position` 更新。
- 直接调用 low-level `lcmot_step_servo()` 后,除非再跑 task cycle否则 task status snapshot 仍保持上一周期。
## subordinate sync 对标
第一阶段需要覆盖:
1. `motion.traj.enabled` 从 true 到 false 时,清 homed 或记录 volatile-home TODO。
2. `io.aux.estop == true` 且 motion enabled 时,执行 traj disable、task abort、io abort、spindle abort shim、plan synch。
3. `motion.status == ERROR && motion.on_soft_limit` 时,进入 soft-limit reporting path。
4. `motion.status == ERROR` 或 hard IO error 时,执行 abort cleanup。
5. 根据 task/motion/io 三方状态聚合 top-level `RCS_STATUS::ERROR/DONE/EXEC`
## status write 对标
WASM 需要引入 task status buffer 等价物:
- `TaskRuntime.last_status_snapshot`
-`StandaloneEmcStatus emcStatus`
- `lctask_read_status_json()` 从该 snapshot 序列化
要求:
- status read 不改变 task/motion/io 状态。
- status read 不直接调用 `lcmot_read_status_json()` 改变观察口径。
- debug/event log 可以单独存在,但不能替代 status buffer。
status JSON 只是 status buffer 的 host/browser 序列化格式。后续新增 LinuxCNC 对标字段时,字段应先进入 `StandaloneEmcStatus` 或后续 `EMC_STAT` 等价容器,再导出到 `emcStatus` JSON 对象;旧 `task``motionStatus` 字段只作为兼容视图。具体字段映射和 SJ 批次见 `12-status-json-LinuxCNC对标方案.md`
## 替换上游源码路线
| 阶段 | 引入源 | 目标 |
| --- | --- | --- |
| R1 | `emc_nml.hh` 必需 enum/status 子集或 vendored include | 用 LinuxCNC 状态类型替换字符串状态 |
| R2 | `emctaskmain.cc` 周期骨架等价函数 | 建立 task main loop 结构 |
| R3 | `taskintf.cc` motion issuing 可编译子集 | 用上游 task interface 发 motion command |
| R4 | `emctask.cc` state/mode/task update 可编译子集 | 减少自有 task state logic |
| R5 | `emccanon.cc` canonical motion command 子集 | interpreter/canon 到 motion 命令更接近上游 |
每一阶段必须满足:
- source reuse map 更新。
- build 脚本增量编译。
- Node WASM smoke 不回退。
- 新增至少一个对标断言。