# 2026-07-07 AXIS 按钮 LinuxCNC 真实 C++ 对标实施步骤 ## 1. 实施原则 本文件由 `19-20260707-AXIS按钮LinuxCNC-task-motion状态机制源码分析.md` 拆分而来,目标是把源码分析转成可执行的工程步骤。后续实现不能以 `compare 60/60 pass` 作为功能完成依据;`60/60 pass` 只能说明当前 compare 规则没有发现问题。真实合格标准必须是:Web 项目的按钮、状态、Task/HAL/WASM、evidence 和 UI 行为逐项符合 LinuxCNC C++ task/motion/homing 的真实实现。 权威基线: - LinuxCNC 源码:`/home/mes123456/cnc_wams/linuxcnc` - 目标项目:`/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan` - 核心 C++ 对标文件: - `linuxcnc/src/emc/task/emctaskmain.cc` - `linuxcnc/src/emc/task/emctask.cc` - `linuxcnc/src/emc/task/taskintf.cc` - `linuxcnc/src/emc/motion/command.c` - `linuxcnc/src/emc/motion/control.c` - `linuxcnc/src/emc/motion/homing.c` - `linuxcnc/src/emc/nml_intf/emc.hh` - `linuxcnc/src/emc/nml_intf/emc_nml.hh` ## 2. 实施输出物 必须形成以下输出物: | 输出物 | 目标 | |---|---| | C++ 行为映射表 | 每个按钮对应 LinuxCNC 源码入口、NML 命令、task 处理、motion 处理、状态字段 | | Web 状态字段表 | `machine.taskState/mode/interpState/...` 与 LinuxCNC `EMC_STAT` 字段一一对应 | | Task/HAL/WASM 命令 ABI | `EMC_TASK_SET_STATE`、`EMC_TASK_SET_MODE`、`EMC_JOINT_HOME`、`EMC_TASK_PLAN_RUN/PAUSE/RESUME/STEP` 的 JSON 输入输出 | | 硬门禁测试 | 不允许只靠 UI disabled,必须验证底层 task runtime 拒绝非法命令 | | 真实程序执行证据 | 用真实 G-code、真实 line/motion id、真实状态流证明 Run/Pause/Step/Home 行为 | | compare 增强规则 | compare 不只检查字段存在和误差,还检查 C++ 状态机不可变规则 | ## 3. 阶段 1:固定 LinuxCNC C++ 源码事实 ### 3.1 建立源码事实清单 实施步骤: 1. 在工作文档中建立 `按钮 -> AXIS Python -> emcmodule -> NML -> emctaskmain -> emctask/taskintf -> motion` 的链路表。 2. 每条链路必须标注具体源码文件和函数名。 3. 每条链路必须标注输入命令、先决条件、状态变更、错误路径。 必须覆盖: - 急停:`axis.py estop_clicked()`、`EMC_TASK_SET_STATE`、`emcTaskSetState(ESTOP/ESTOP_RESET)`。 - 上电/下电:`axis.py onoff_clicked()`、`emcTaskSetState(ON/OFF)`、`EMCMOT_ENABLE/DISABLE`。 - Home:`home_all_joints()`、`home_joint()`、`EMC_JOINT_HOME`、`EMCMOT_JOINT_HOME`、`homing.c`。 - Run:`task_run()`、`EMC_TASK_PLAN_RUN`、`all_homed()`、`programStartLine`、`interpState=READING`。 - Pause/Resume:`task_pause()`、`task_pauseresume()`、`EMC_TASK_PLAN_PAUSE/RESUME`、`EMCMOT_PAUSE/RESUME`。 - Step:`task_step()`、`EMC_TASK_PLAN_STEP`、`stepping`、`single_stepping`、`EMCMOT_STEP`、motion id 变化后再暂停。 完成标准: - 任一 Web 状态或测试断言都能追溯到 LinuxCNC C++ 源码。 - 没有“按前端习惯推测”的状态规则。 ### 3.2 建立状态字段对应关系 实施步骤: 1. 对照 `EMC_STAT.task`、`motion.traj`、`motion.joint[]` 建立 Web 字段映射。 2. 区分 task 层暂停和 motion 层暂停。 3. 区分 task state 命令请求和最终发布状态。 必须字段: ```js machine: { taskState: "estop" | "estop-reset" | "on", mode: "manual" | "auto" | "mdi", interpState: "idle" | "reading" | "paused" | "waiting", interpResumeState: "idle" | "reading" | "waiting", execState: "done" | "waiting-for-motion" | "waiting-for-io" | "waiting-for-motion-and-io" | "error", taskPaused: boolean, motionPaused: boolean, singleStepping: boolean, motionStepping: boolean, motionEnabled: boolean, homing: boolean, homed: boolean[], allHomed: boolean, noForceHoming: boolean, resumeInhibit: boolean, currentLine: number, readLine: number, motionLine: number } ``` 完成标准: - `taskState` 不再只从 `powerOn` 派生,而是符合 LinuxCNC “IO estop + motion enabled” 语义。 - `paused` 不再是单字段,必须拆成 `interpState/taskPaused/motionPaused`。 - `Step` 不再只用 `sampleIndex + 1` 表示,必须能表达 `singleStepping/motionStepping/motion id`。 ## 4. 阶段 2:实现 Task/HAL/WASM 真实状态机 ### 4.1 实现 NML 命令 ABI 实施文件: - `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_task_hal_wasm.cpp` - `wasm-port/runtime/sdk/src/linuxcnc-task-hal.js` - `web-rtcp-5axis-xyzbc-trt-sim-plan/app/src/runtime/linuxcnc-task-hal-runtime.js` 实施步骤: 1. 统一 JSON 命令名称为 LinuxCNC NML 名称。 2. 每条命令返回 command accepted/rejected、operator error、状态快照。 3. 非法命令必须在 runtime 层拒绝,不能只靠 UI gate。 命令要求: | 命令 | 必须实现的 C++ 语义 | |---|---| | `EMC_TASK_SET_STATE ESTOP` | abort motion/task/io/spindle,disable motion,清 pause/step,volatile home 处理 | | `EMC_TASK_SET_STATE ESTOP_RESET` | IO estop off,machine off,abort/synch | | `EMC_TASK_SET_STATE ON` | motion enable,只有 enable 成功后最终 state 才是 on | | `EMC_TASK_SET_STATE OFF` | abort、disable、清运行、清 pause/step、volatile unhome | | `EMC_TASK_SET_MODE MANUAL` | AUTO 非 idle 时拒绝或必须按 LinuxCNC abort/close/synch 语义处理 | | `EMC_TASK_SET_MODE AUTO/MDI` | motion coord mode、abort、plan synch | | `EMC_JOINT_HOME` | 只在 on/manual/free/idle/非 homing/homing-inhibit=false 时启动 | | `EMC_TASK_PLAN_RUN` | on/auto/idle/homed/program open/motion plan loaded | | `EMC_TASK_PLAN_PAUSE` | 保存 `interpResumeState`,task paused + motion paused | | `EMC_TASK_PLAN_RESUME` | 恢复 `interpResumeState`,清 single stepping | | `EMC_TASK_PLAN_STEP` | idle step 等价 run 后 pause;paused step 放行到下一个 motion id | 完成标准: - Runtime 层能在无 UI 的 Node 测试中复现所有先决条件和状态变化。 - 任一非法命令都有明确错误消息,且状态保持不被破坏。 ### 4.2 实现 Home 状态机 实施步骤: 1. 增加 per-joint `homed[]`、`homing[]`、`homeState[]`。 2. Home All 使用 `joint=-1`。 3. Home 过程至少记录 `HOME_START -> HOME_FINISHED/HOME_IDLE` 的可观测状态。 4. 正在 homing 时再次 Home 必须拒绝。 5. 下电/急停只清 LinuxCNC 语义中应清的 home 状态;若当前 Web 无 volatile home 配置,必须显式记录简化边界。 完成标准: - Home All 后所有 active joints `homed[] = true`。 - Home 期间 Run、Step、Jog、再次 Home 都被拒绝。 - Evidence 能看到 homing 瞬态,不允许直接静默从 unhomed 跳到 homed 而无事件记录。 ### 4.3 实现 Pause/Resume/Step 真实 motion 语义 实施步骤: 1. Pause 时冻结执行位置、active line、tcp pose、tool axis、feed velocity。 2. Resume 恢复 `interpResumeState`,不是固定写 `reading`。 3. Step 从 idle 开始时:先进入 run,再立即 pause。 4. Step 从 paused 开始时:只放行到下一个 `motionId/currentLine` 后再次 pause。 5. Abort、ESTOP、OFF、Resume 都必须清理 `singleStepping/motionStepping`。 完成标准: - 暂停期间 50ms 多帧采样位置完全冻结。 - Step 后 active line 或 motion id 按 LinuxCNC 语义变化,不能只看 sample index。 - Resume 后速度和执行状态恢复,且暂停锁消失。 ## 5. 阶段 3:收敛 Web 状态与 UI 门禁 实施文件: - `app/src/state/linuxcnc-task-policy.js` - `app/src/state/store.js` - `app/src/ui/axis-shell.js` - `app/src/ui/gmoccapy-shell.js` 实施步骤: 1. `linuxcnc-task-policy.js` 作为唯一按钮门禁入口。 2. UI 组件只读取 policy,不再各自写 LinuxCNC 状态判断。 3. `store.js` 的每个 action 都映射到 LinuxCNC NML 命令或明确的 UI 辅助流程。 4. Task/HAL runtime 存在时,前端可先进入预期状态,但最终必须以 status 回写覆盖。 按钮 gate 要求: | 按钮 | Web gate 必须等价的 LinuxCNC 条件 | |---|---| | ESTOP | 始终可触发 | | RESET ESTOP | 当前 taskState=estop | | POWER ON | taskState=estop-reset | | POWER OFF | taskState=on | | HOME | taskState=on、mode=manual、interpState=idle、非 homing | | RUN | taskState=on、mode=auto、interpState=idle、allHomed 或 noForceHoming、programOpen | | PAUSE | taskState=on、mode=auto、interpState=reading/waiting | | PAUSE_RESUME | taskState=on、mode=auto/mdi,paused 走 resume,非 idle 走 pause | | STEP | taskState=on、mode=auto、programOpen、已 homed 或 noForceHoming、resumeInhibit=false | 完成标准: - UI 按钮禁用状态、operator message、runtime 拒绝状态三者一致。 - UI 显示状态必须能追溯到 task/HAL status,而不是只看本地 `runState`。 ## 6. 阶段 4:重构 evidence 与 compare ### 6.1 evidence 必须采真实功能 实施步骤: 1. Native evidence 采集 LinuxCNC 真实 `task.state/mode/interpState/execState/currentLine/readLine/motionLine`。 2. Native evidence 采集 motion `enabled/paused/stepping/queue/motion id/homing/homed[]`。 3. Web evidence 输出同名字段。 4. 每个按钮输出 action 前、命令接受/拒绝、action 后、下一周期状态。 必须新增 evidence 段: ```json { "taskMotionStateTrace": [], "buttonCommandTrace": [], "homingTrace": [], "pauseFreezeTrace": [], "stepMotionIdTrace": [], "illegalCommandTrace": [], "cppParityAssertions": [] } ``` 完成标准: - 证据能说明“为什么允许/拒绝”,而不是只说明最终 pass。 - 每个 pass 都有对应 LinuxCNC C++ 规则名称。 ### 6.2 compare 必须从表面 pass 升级为硬规则 现有 `60/60 pass` 只能保留为兼容摘要。必须增加真实功能硬检查: | 新检查 | 失败条件 | |---|---| | `cppTaskStateMachineParity` | Web 状态转移不符合 `emcTaskSetState()` 或 task update 推导 | | `cppModeGateParity` | AUTO 非 idle 可切出或 MDI/AUTO gate 不符合 C++ | | `cppHomingParity` | Home 无 homing 瞬态、非法 Home 未拒绝、homed[] 错误 | | `cppRunGateParity` | 未 home 可 Run、非 idle 可 Run、无 program 可 Run | | `cppPauseResumeParity` | pause 未冻结、resume 未恢复 `interpResumeState` | | `cppStepParity` | Step 不是 motion id/line 语义,只推进 sample | | `illegalCommandParity` | UI 禁用但 runtime 接受非法命令,或 runtime 拒绝但 UI 显示允许 | | `realProgramExecutionParity` | 真实 G-code 执行 line/motion/path/status 与 native 不一致 | 完成标准: - compare 输出 `surfaceSummary` 和 `functionalSummary` 两部分。 - 只有 `functionalSummary.failCount=0` 才能称为真实通过。 - `surfaceSummary 60/60 pass` 但 `functionalSummary` 失败时,结论必须是失败。 ## 7. 阶段 5:测试实现顺序 建议顺序: 1. 先补 Task/HAL runtime 的无 UI 状态矩阵测试。 2. 再补 store/action 状态测试。 3. 再补 UI gate 测试。 4. 再补 native/Web evidence 字段。 5. 最后补 compare functional checks。 对应测试文件建议: | 测试文件 | 目标 | |---|---| | `wasm-port/tests/wasm/node/verify_task_state_matrix.mjs` | C++ task state/mode/run/pause/step/home gate | | `wasm-port/tests/wasm/node/verify_task_hal_wasm.mjs` | WASM command ABI 和 status 字段 | | `app/tests/node/verify_linuxcnc_task_policy.mjs` 或现有同类测试 | Web policy gate | | `tests/node/verify_rtcp_store.mjs` | store action 状态写入 | | `tests/node/verify_run_preconditions.mjs` | Run/Home/Power/ESTOP 前置 | | `tests/browser/xyzbc_trt_browser_smoke.html` | UI 按钮状态与实际流程 | | `tools/collect-native-xyzbc-trt-evidence.py` | native C++ 行为证据 | | `tools/collect-web-xyzbc-trt-evidence.mjs` | Web 同名证据 | | `tools/compare-xyzbc-trt-evidence.mjs` | functionalSummary 硬检查 | ## 8. 完成判定 实施完成必须同时满足: 1. C++ 源码规则已映射到文档和测试断言。 2. Task/HAL runtime 无 UI 情况下能拒绝非法命令。 3. Web UI gate、store 状态、runtime status 三者一致。 4. Native/Web evidence 包含真实状态流、非法命令、Home、Run、Pause、Step。 5. compare 输出功能级硬检查,且功能级检查通过。 6. 真实 G-code 程序执行过程中,active line、motion id、path、pose、task/motion 状态与 LinuxCNC 对齐。 明确禁止把以下结果单独作为完成依据: - `compare 60/60 pass` - 页面截图看起来正常 - UI 按钮样式 active/disabled 正常 - 单个 smoke 测试通过 - Web 本地 `runState` 看起来合理