# 08-上游 task 源码替换分解 ## 目标 本文件把上游 `emctask.cc`、`taskintf.cc`、`emccanon.cc` 的替换路线拆成可实施步骤。目标不是把三个文件一次性塞进 WASM 构建,而是按 LinuxCNC 语义依赖顺序逐步替换当前 `linuxcnc_task_hal_wasm.cpp` 中的 JSON 自有状态机。 总原则: - 先建立 `EMC_STAT`、motion snapshot、status buffer、command buffer。 - 再迁 `emctask.cc` 的 task/interpreter/status 管理。 - 再迁 `taskintf.cc` 的 task->motion command issue 和 motion update。 - 最后迁 `emccanon.cc` 的 canon->interp_list command 生成。 - 每迁一个函数族,都必须有 source reuse、shim 边界和 WASM 测试证据。 ## 文件职责总览 | 文件 | 上游职责 | WASM 替换目标 | | --- | --- | --- | | `emctask.cc` | task mode/state、abort cleanup、interpreter plan open/read/execute/synch、`emcTaskUpdate()` | 替换当前 `TaskRuntime` 中 state/mode/interp/exec 字符串状态和 open/run/MDI 自有逻辑 | | `taskintf.cc` | task 到 motion 的接口;`emcTraj*`、`emcJoint*`、`emcSpindle*`、`emcMotionInit/Update/Abort` | 替换当前直接 `forward_motion_command()` 和 `lcmot_read_status_json()` 拼接路径 | | `emccanon.cc` | canonical API;单位/偏置/速度加速度转换;生成 `EMC_TRAJ_*`、spindle/tool/io command 到 `interp_list` | 替换当前按字符串/G-code line 生成 motion plan/linear move 的自有行为 | ## 迁移依赖链 ```text emctaskmain cycle -> emcTaskPlan()/emcTaskExecute() -> interp_list -> emcTaskIssueCommand() -> taskintf.cc emcTraj*/emcMotion*/emcJoint*/emcSpindle* -> lcmot/lchal runtime boundary -> emccanon.cc canonical functions append commands to interp_list ``` 因此迁移顺序必须是: 1. `EMC_STAT`/`interp_list`/command buffer 基础。 2. `taskintf.cc` 的 minimal motion command bridge。 3. `emctask.cc` 的 abort/state/mode/update 和 plan wrapper。 4. `emccanon.cc` 的 linear/traverse/feed/dwell/spindle/tool 子集。 5. 更完整的 interpreter file run 和 MDI 行为。 ## `emctask.cc` 分解 ### E1:状态和 abort 基础 优先函数: | 函数 | 上游行为 | WASM 实施 | | --- | --- | --- | | `emcTaskQueueTaskPlanSynchCmd()` | 向 task command queue 追加 `EMC_TASK_PLAN_SYNCH` | 需要 command queue 支持内部 command | | `emcTaskAbort()` | `emcMotionAbort()`、清 `emcTaskCommand`、清 `interp_list`、重置 interp/exec/line、queue synch | 替换当前 `EMC_TASK_ABORT` 直接设置字符串状态 | | `emcTaskSetMode()` | MANUAL/MDI/AUTO 切换并设置 traj mode、abort/synch | 替换当前 SET_MODE 自有逻辑 | | `emcTaskSetState()` | OFF/ON/ESTOP_RESET/ESTOP 驱动 motion/io/spindle/task abort | 替换当前 SET_STATE 自有逻辑 | | `determineMode()` | 由 `motion.traj.mode` 和 `mdiOrAuto` 推导 task mode | 用 snapshot 推导 status,而不是保存字符串 | | `determineState()` | 由 `io.aux.estop` 和 `motion.traj.enabled` 推导 task state | 用 snapshot 推导 status | | `emcTaskUpdate()` | 更新 mode/state/motionLine/file/active modes | 替换当前 `status_json()` 手写 task 字段 | | `emcAbortCleanup()` | abort 后清理/消息 | 初期保留 shim,后续接 LinuxCNC 行为 | 依赖 shim: - `emcMotionAbort()`、`emcTrajSetMode()`、`emcTrajEnable()`、`emcTrajDisable()`。 - `emcIoAbort()`、`emcAuxEstopOn/Off()`、coolant/spindle shim。 - `interp_list.clear()`、`emcTaskCommand`。 - `emcStatus` 全局或等价容器。 验收: - SET_STATE/SET_MODE 发送后不立即改变 task 语义;一次 task cycle 后通过 `emcTaskSetState/Mode` 路径改变。 - `emcTaskAbort()` 后 `interpState=IDLE`、`execState=DONE`、line 清零、motion abort command 已发出。 - `emcTaskUpdate()` 的 `state/mode/motionLine` 来自 `EMC_STAT.motion/io` snapshot。 ### E2:plan/interpreter wrapper 优先函数: | 函数 | 上游行为 | WASM 实施 | | --- | --- | --- | | `emcTaskPlanInit()` | 创建/初始化 interpreter、执行 startup code | 先接现有 vendored interpreter init;动态 shlib 路径作为非目标 shim | | `emcTaskPlanSetWait/IsWait/ClearWait()` | 控制 read-ahead waitFlag | 直接迁移 | | `emcTaskPlanSynch()` | `interp.synch()`,失败 abort | 用 task-held motion position/status 支撑 | | `emcTaskPlanOpen()` | 打开文件并清 motion/current/read line | 替换 `lctask_open_program()` 中自有 open 状态 | | `emcTaskPlanRead()` | `interp.read()`,必要时 reopen file | 替换 motion plan 预加载路线 | | `emcTaskPlanExecute()` | `interp.execute()`,MDI 时 `FINISH()` | 接 `emccanon.cc` 后形成 `interp_list` | | `emcTaskPlanClose/Reset/Line/Level/Command()` | interpreter 状态读写 | 直接对接 vendored interpreter | 依赖 shim: - vendored `Interp` 可编译子集已在 interpreter WASM 中存在,但 task-hal 模块需要链接或抽取共享。 - `FINISH()` 来自 `emccanon.cc`。 - `rs274ngc_startup_code`、INI lookup、file staging。 验收: - `open -> runCycles(read/execute)` 通过 LinuxCNC `Interp::open/read/execute`,不是 JS/JSON motion plan。 - `readLine/currentLine/callLevel/command` 与 upstream interpreter 返回一致。 - MDI command 通过 `emcTaskPlanExecute(command)` 进入 `interp_list`。 ## `taskintf.cc` 分解 ### TIF1:motion runtime bridge 优先函数: | 函数 | 上游行为 | WASM 实施 | | --- | --- | --- | | `emcMotionInit()` | `emcTrajInit()`、joint/axis/spindle init、position load | 调用 `lcmot_init_from_ini()` 和 HAL init;建立 config defaults | | `emcMotionAbort()` | jog abort + traj abort | 发 `EMCMOT_ABORT` 到 `lcmot` | | `emcMotionHalt()` | halt/disable/save/exit | standalone shim,不能真实硬件 | | `emcMotionUpdate()` | `usrmotReadEmcmotStatus()` 后更新 traj/joint/axis/spindle/synch IO/heartbeat | 改为 `lcmot_read_status_snapshot()` -> `EMC_MOTION_STAT` | | `emcTrajUpdate()` | 映射 emcmot status 到 `EMC_TRAJ_STAT` | 复刻字段映射到 snapshot | | `emcJointUpdate()` | 映射 joint status | 第一阶段只映射 homed/pos/ferror 必需字段 | | `emcAxisUpdate()` | 映射 axis status | 第一阶段映射 pos cmd/fb | | `emcSpindleUpdate()` | 映射 spindle status | 初期单 spindle shim,后续 orient/atspeed | 依赖 shim: - `usrmot*` 系列函数替换为 `lcmot_*` C ABI。 - `emcmot_command_t` 可复用上游 motion headers,或建立命令转换层。 - INI axis/joint/spindle 初始化先使用已有 INI/WASM runtime 或默认值。 验收: - `emcMotionUpdate(&emcStatus.motion)` 每个 task cycle 调用一次。 - status read 不调用 `emcMotionUpdate()`。 - low-level `lcmot_step_servo()` 后 task status 不变,直到 task cycle 调用 `emcMotionUpdate()`。 ### TIF2:traj command issue 优先函数: | 函数 | 上游行为 | WASM 实施 | | --- | --- | --- | | `emcTrajSetMotionId()` | 设置下一个 motion id | 映射到 `lcmot` command id 或 task pending id | | `emcTrajEnable/Disable/Abort()` | 发 ENABLE/DISABLE/ABORT | 替换当前 state ON/OFF 手写状态 | | `emcTrajPause/Step/Resume()` | 发 PAUSE/STEP/RESUME | 替换当前 pause/step/resume direct JSON | | `emcTrajLinearMove()` | 构造 `EMCMOT_SET_LINE` | 替换 `enqueue_linear_move_from_line()` | | `emcTrajCircularMove()` | 构造 `EMCMOT_SET_CIRCLE` | 后续接 arc/canon | | `emcTrajDelay()` | task controller 处理 delay | 映射到 `WAITING_FOR_DELAY` | | `emcTrajSetOffset/G5X/G92/Rotation` 相关 | motion/task offsets | 后续接 `emccanon.cc` offsets | 验收: - `EMC_TRAJ_LINEAR_MOVE` 由 `emcTrajLinearMove()` 路径进入 `lcmot`,而不是 task JSON 自行拼接。 - pause/resume/step 仍通过现有 WASM smoke。 - queueFull 时 `emcTaskExecute()` 进入 `WAITING_FOR_MOTION_QUEUE`。 ### TIF3:joint/jog/spindle/io 命令 优先函数: | 函数组 | 范围 | 首阶段处理 | | --- | --- | --- | | `emcJointHome/Unhome` | homing/unhome | 保留当前 homing smoke,但通过 taskintf shim | | `emcJogIncr/Cont/Abs/Stop` | manual jog | 先迁 `emcJogIncr()` | | `emcSpindleOn/Off/Speed/Orient` | spindle | 初期 command/event shim,后续 snapshot | | `emcMotionSetAout/Dout` | M62-M68/switchkins | 优先保留 `M428/M429/M430` switchkins 验收 | 验收: - Home/Jog/switchkins 现有 tests 通过。 - `motion.switchkins-type` 仍同步 HAL。 - spindle/tool 未实现项明确标为 runtime boundary,不假装 ready。 ## `emccanon.cc` 分解 ### C1:canon 基础状态和单位转换 优先函数/变量: | 函数/组 | 上游行为 | WASM 实施 | | --- | --- | --- | | `INIT_CANON()`、`ON_RESET()`、`FINISH()` | 初始化 canon、丢弃/flush segments | 先直接迁移或抽取 | | `get_canon()`、`CanonConfig_t canon` | canon 全局状态 | 使用 vendored类型,避免自建 motion plan | | `GET_EXTERNAL_LENGTH_UNITS()`、`GET_EXTERNAL_ANGLE_UNITS()` | 从 motion/task status 取单位 | 接 `EMC_STAT.motion.traj.linearUnits/angularUnits` | | `CANON_UPDATE_END_POINT()` | 更新 canon endpoint | 接 task/motion snapshot | | `SET_G5X_OFFSET()`、`SET_G92_OFFSET()`、`SET_XY_ROTATION()` | 偏置/旋转 | 先保留字段,后续生成 task commands | 验收: - interpreter 执行时 canon endpoint 与 motion snapshot 可同步。 - 单位换算不再由 JS/JSON motion plan 决定。 ### C2:直线/圆弧/探测 motion command 生成 优先函数: | 函数 | 上游行为 | WASM 实施 | | --- | --- | --- | | `STRAIGHT_TRAVERSE()` | 生成 `EMC_TRAJ_LINEAR_MOVE` traverse 到 `interp_list` | 第一优先级 | | `STRAIGHT_FEED()` | 通过 segment queue 生成 feed move | 第一优先级 | | `generate_fast_move()`、`generate_move()` | flush 后 append linear move | 作为 STRAIGHT 基础 | | `ARC_FEED()` | 生成 circular move/segments | 第二阶段 | | `STRAIGHT_PROBE()` | 生成 `EMC_TRAJ_PROBE` | 第二阶段 | | `RIGID_TAP()` | 生成 rigid tap | 第二阶段 | | `SET_MOTION_CONTROL_MODE()` | G61/G64 path control | 与 planner/tolerance 验证绑定 | 依赖 shim: - `interp_list`。 - `EMC_TRAJ_LINEAR_MOVE`、`EMC_TRAJ_CIRCULAR_MOVE` 等 NML command 类型。 - `StateTag`。 - velocity/acceleration helpers 的 axis limits 来源。 验收: - G0/G1 文件执行后 `interp_list` 中出现 LinuxCNC `EMC_TRAJ_LINEAR_MOVE`,再由 `emcTaskExecute()` issue。 - 不再由 `loadProgramMotionPlan(plan_json)` 提供主路径 motion segments。 - line number、motion id、axis endpoint 与 upstream interpreter/canon 输出一致。 ### C3:spindle/tool/io canonical command 优先函数: | 函数组 | 上游行为 | WASM 实施 | | --- | --- | --- | | `DWELL()` | append `EMC_TRAJ_DELAY` | 接 `WAITING_FOR_DELAY` | | `START_SPINDLE_*`、`STOP_SPINDLE_TURNING()`、`SET_SPINDLE_SPEED()` | append spindle commands | 初期 command/event shim | | `ORIENT_SPINDLE()`、`WAIT_SPINDLE_ORIENT_COMPLETE()` | spindle orient wait | 后续接 snapshot | | `SELECT_TOOL()`、`CHANGE_TOOL()`、`CHANGE_TOOL_NUMBER()` | append tool commands | 先支持 T/M6 smoke,再接 IO/tool DB | | `MOTION_OUTPUT_BIT_()`、`MOTION_OUTPUT_VALUE_()` | M62-M68 motion outputs | 优先用于 switchkins | | `WAIT()` | M66 input wait | 后续接 motion `synch_di/analog_input` | 验收: - `M428/M429/M430` 不再 special-case 字符串 MDI,而是通过 remap/canon/MOTION_OUTPUT_VALUE_ 到 motion AOUT。 - `DWELL` 进入 task delay state。 - tool/spindle runtime boundary 在 readiness 中保持 false,直到有对应验收。 ## 替换任务批次 | 批次 | 内容 | 退出条件 | | --- | --- | --- | | B1 | taskintf minimal bridge:`emcMotionInit/Update/Abort`、`emcTrajPause/Step/Resume/Abort/LinearMove` | task cycle 内 motion snapshot 和 linear move issue 通过 | | B2 | emctask state/update/abort:`emcTaskAbort/SetMode/SetState/Update` | state/mode/home/run/pause smoke 仍通过,且语义由 task cycle 触发 | | B3 | emctask plan wrapper:`PlanInit/Open/Read/Execute/Synch` | 文件和 MDI 通过 LinuxCNC interpreter/canon 生成 `interp_list` | | B4 | emccanon straight motion:`INIT_CANON/FINISH/STRAIGHT_TRAVERSE/STRAIGHT_FEED` | G0/G1 不再依赖 JSON motion plan | | B5 | task execute issue:`emcTaskExecute()` motion branches + `taskintf` issue | wait/queue/error/top-level status 对标 | | B6 | emccanon MDI/spindle/tool/io 子集 | MDI、switchkins、dwell、basic tool/spindle command 有验收 | ## 构建策略 1. 不直接修改 `/home/mes123456/cnc_wams/linuxcnc`。 2. 优先 vendored source 或可重复 copy/sync 到 `wasm-port/vendor/linuxcnc`。 3. 对 runtime-edge 依赖建立 shim: - `usrmot*` -> `lcmot_*` - NML channels -> in-process command/status buffers - POSIX process/system command -> blocked/shim - IO/tool DB/spindle hardware -> minimal status/command shim 4. 每增加一个上游源文件到 build,更新: - `tools/task-hal-source-manifest.txt` - `tools/build_task_hal_wasm.sh` - `docs/source-reuse-map.md` - `working/04-任务矩阵.md` - `working/05-验收证据.md` ## 禁止路线 - 不允许继续把 G-code line 文本在 `linuxcnc_task_hal_wasm.cpp` 中手写解析成 motion。 - 不允许让 `readStatus()` 触发 motion update 来掩盖 task cycle 缺失。 - 不允许把 `nativeTaskReady` 提前置 true。 - 不允许绕过 `interp_list` 直接把 `emccanon.cc` 输出改成 JSON。