# 10-taskintf usrmot shim 设计 ## 目标 T-032 定义 `taskintf.cc` 在 WASM 中继续保留的 usrmot 语义边界,以及这些边界如何落到 standalone `lcmot_*` runtime。目标是给 T-033 之后的实现提供窄 shim 合同,而不是在本阶段直接编译完整 `linuxcnc/src/emc/motion/usrmotintf.cc`。 上游锚点: - `linuxcnc/src/emc/motion/usrmotintf.h` - `linuxcnc/src/emc/motion/usrmotintf.cc` - `linuxcnc/src/emc/task/taskintf.cc` WASM runtime 锚点: - `runtime/core/linuxcnc_wrap/linuxcnc_motion_runtime.h` - `runtime/core/linuxcnc_wrap/linuxcnc_motion_runtime.c` - `runtime/core/linuxcnc_task_subset/taskintf_wasm_subset.hh` - `runtime/core/linuxcnc_task_subset/taskintf_wasm_subset.cc` ## 不直接复用完整 `usrmotintf.cc` 完整上游 `usrmotintf.cc` 绑定 native shared memory、mutex、command ack 轮询、split read、error ring buffer、RTAPI init/exit 和 compensation file loading。WASM 当前 motion runtime 是同进程 standalone C ABI,不存在上游共享内存段,也没有 native emcmot process ack 通道。 因此 T-032 采用一个窄 usrmot shim: ```text taskintf.cc-compatible calls -> wasm usrmot shim -> lcmot_* C ABI -> standalone motion runtime ``` shim 必须保留上游函数的返回值语义,但只能承诺当前 `lcmot_*` 已有或明确新增的能力。 ## 映射总表 | usrmot 边界 | 上游行为 | WASM shim 映射 | 当前状态 | | --- | --- | --- | --- | | `usrmotIniLoad(file)` | 读取 INI 中的 motion shared memory key、timeout 等 | 只保存 `ini_path`,等待 `usrmotInit()` 调用 `lcmot_init_from_ini(ini_path, ini_text)` | 需要 shim 缓存 staged INI text | | `usrmotInit(name)` | 连接 native emcmot shared memory | 初始化 standalone motion runtime;映射到 `lcmot_init_from_ini()` | 已有底层 ABI | | `usrmotExit()` | 断开 native emcmot shared memory/RTAPI | 映射到 `lcmot_reset()`;不退出 WASM runtime | 已有底层 ABI | | `usrmotWriteEmcmotCommand(c)` | 写 `emcmot_command_t`,等待 `commandNumEcho` ack | 将 taskintf 命令转换为 `lcmot_write_command_json()` 可接受的 command JSON | 已有底层 ABI,ack 语义需降级 | | `usrmotReadEmcmotStatus(s)` | split-read 复制 `emcmot_status_t` | 映射 `lcmot_read_status_snapshot()` 到窄 `emcmot_status_t`/`EMC_MOTION_STAT` subset | 已有底层 ABI | | `usrmotReadEmcmotConfig(s)` | split-read 复制 `emcmot_config_t` | 新增 `lcmot_read_config_snapshot()`,输出 axes/joints/queue/config_num/limits/default units 等窄 config subset | T-033 已实现窄底层 ABI | | `usrmotReadEmcmotInternal(s)` | 读取 debug/internal shared memory | 第一阶段返回空 internal subset;后续可新增 `lcmot_read_internal_snapshot()` | 非 T-032 必需 | | `usrmotReadEmcmotError(e)` | 从 motion error ring 取最早错误 | 新增 `lcmot_read_error_message(char *out, int out_len)` 或等价 JSON/error buffer API | T-033 已实现窄 error message queue | | `usrmotLoadComp()` / `usrmotPrintComp()` | joint compensation 文件 IO | 暂不支持;返回 `EMCMOT_COMM_ERROR_COMMAND` 或独立 unsupported code | 后续 joint compensation 批次处理 | ## command write 上游 `usrmotWriteEmcmotCommand(emcmot_command_t *c)` 做三件事: 1. 校验 motion id。 2. 写入 shared memory command slot。 3. 轮询 `usrmotReadEmcmotStatus()`,等待 `commandNumEcho` 和 `commandStatus`。 WASM shim 的 T-033 最小实现合同: ```text usrmotWriteEmcmotCommand(c) -> validate supported command and motion id -> convert emcmot_command_t to taskintf/lcmot command JSON -> lcmot_write_command_json(json) -> return EMCMOT_COMM_OK on accepted enqueue ``` 返回值映射: | 条件 | 返回值 | | --- | --- | | command 转换成功且 `lcmot_write_command_json()` 返回 0 | `EMCMOT_COMM_OK` | | command unsupported、queue full、字段非法 | `EMCMOT_COMM_ERROR_COMMAND` | | runtime 未初始化 | `EMCMOT_COMM_ERROR_CONNECT` | | motion id 为 invalid sentinel | `EMCMOT_COMM_INVALID_MOTION_ID` | | in-process WASM runtime 无 ack 通道 | 不使用 `EMCMOT_COMM_ERROR_TIMEOUT`,除非后续引入 command ack | 现有 `taskintf_wasm_subset` 已覆盖的 command family: - `emcTrajAbort()` / `EMCMOT_ABORT` - `emcTrajPause()` / `EMCMOT_PAUSE` - `emcTrajStep()` / `EMCMOT_STEP` - `emcTrajResume()` / `EMCMOT_RESUME` - `emcTrajLinearMove()` / `EMCMOT_SET_LINE` - `emcJogIncr()` / `EMCMOT_JOG_INCR` - `emcJointHome()` / homing command envelope - `emcMotionSetAout()` / `EMCMOT_SET_AOUT` 后续 T-034 到 T-036 可以把这些 command family 从 JSON 拼接继续收缩到 `emcmot_command_t -> shim -> lcmot` 的窄转换层。 ## status read 上游 `usrmotReadEmcmotStatus(emcmot_status_t *s)` 是 split-read,只复制 shared memory,不推进 servo。WASM 语义必须保持这一点: ```text usrmotReadEmcmotStatus(s) -> lcmot_read_status_snapshot(&snapshot) -> map snapshot to status subset -> return EMCMOT_COMM_OK ``` 硬性约束: - 不调用 `lcmot_step_servo()`。 - 不消费 motion command queue。 - 不调用 `lctask_run_cycles()`。 - 不从 JSON status 反向解析。 - `status`、`in_position`、`queue_full`、`queue_count`、`motion_id`、`program_line`、`axis_cmd/fb`、`joint_cmd/fb`、`motion_error`、`on_soft_limit` 必须来自 `LcmotStatusSnapshot`。 `LcmotStatusSnapshot.status` 到 RCS 子状态的第一阶段映射: | `LcmotStatusSnapshot.status` | 语义 | | --- | --- | | `0` | motion DONE | | `1` | motion EXEC | | `2` | motion ERROR | ## config read 上游 `usrmotReadEmcmotConfig(emcmot_config_t *s)` 读取 config shared memory,并依赖 `head == tail` 的 split-read 稳定性。当前 `lcmot_*` 只有 `lcmot_init_from_ini()`,没有 config snapshot read API。 T-032 设计结论: ```text usrmotReadEmcmotConfig(s) -> lcmot_read_config_snapshot(&config) -> map config to emcmot_config_t subset ``` T-033 已新增窄实现: ```c typedef struct { int config_num; int axes; int joints; int queue_capacity; double linear_units; double angular_units; double axis_min_limit[LCMOT_MAX_AXES]; double axis_max_limit[LCMOT_MAX_AXES]; } LcmotConfigSnapshot; int lcmot_read_config_snapshot(LcmotConfigSnapshot *snapshot); ``` 如果后续替换或重命名该 API,shim 不能假装 `usrmotReadEmcmotConfig()` 已支持。允许的临时行为只有: - task 初始化阶段使用 `lcmot_init_from_ini()` 建立 runtime defaults。 - `emcMotionUpdate()` 遇到 config read 需求时返回错误,保持 readiness false。 - 文档和验证脚本继续记录 config read 是窄 runtime-edge ABI,不是 native shared-memory config promotion。 ## error read 上游 `usrmotReadEmcmotError(char *e)` 从 motion error ring 取最早错误字符串。当前 `LcmotStatusSnapshot` 只有 `motion_error` 和 `on_soft_limit` 标志,不能替代错误队列。 T-032 设计结论: ```text usrmotReadEmcmotError(e) -> lcmot_read_error_message(e, EMCMOT_ERROR_LEN) ``` T-033 已新增窄实现: ```c int lcmot_read_error_message(char *out, int out_len); ``` 行为合同: - 有错误消息时返回 0,并复制最早错误。 - 无错误消息时返回 -1,匹配上游 “no error” 分支。 - `EMCMOT_INJECT_ERROR`、soft-limit、abort cleanup 写入该 error buffer。 - status flag 仍来自 `lcmot_read_status_snapshot()`,错误文本只通过 error read 消费。 ## T-033 实施入口 1. T-033 已在 `taskintf_wasm_subset` 中新增 `lc_taskintf_subset_motion_init()`、`lc_taskintf_subset_motion_update()`、`lc_taskintf_subset_motion_abort()`。 2. T-033 已新增 `lcmot_read_config_snapshot()` 与 `lcmot_read_error_message()`。 3. T-033 已让 `lctask_init_session()`、task-cycle motion update 和 task abort path 通过 shim 访问 motion runtime。 4. 继续保持 task status read 和 motion status read 的周期隔离:外部 `lctask_read_status_json()` 不触发新的 motion update。 5. T-034+ 可继续把 traj control command family 从 envelope 迁到更完整的 usrmot command conversion。 ## 禁止路线 - 不把完整 `usrmotintf.cc` 直接加入 WASM build。 - 不用 `lcmot_read_status_json()` 反解析生成 `emcmot_status_t`。 - 不让 status read 推进 servo 或 task cycle。 - 不把 config read 映射成 HAL snapshot。 - 不把 `motion_error` boolean 当成 `usrmotReadEmcmotError()` 的错误队列。 - 不把 T-032/T-033 的 usrmot shim 解释为 `nativeTaskReady=true` 或 `nativeHalSyncReady=true`。