219 lines
12 KiB
Markdown
219 lines
12 KiB
Markdown
# 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 snapshot,SDK 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 cycle,status 不应被 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 不回退。
|
||
- 新增至少一个对标断言。
|