Files
cnc_wams/wasm-port/working/07-emctaskmain周期对标蓝图.md
2026-07-09 18:12:01 -04:00

219 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 不回退。
- 新增至少一个对标断言。