# 2026-07-07 AXIS 按钮 LinuxCNC 真实 C++ 对标验收测试 ## 1. 验收原则 本文件由 `19-20260707-AXIS按钮LinuxCNC-task-motion状态机制源码分析.md` 拆分而来,定义“急停、上电、Home、执行、暂停、单步执行”的真实验收标准。 验收原则: - `compare 60/60 pass` 不是最终验收结论,只是旧 compare 摘要。 - 验收必须证明 Web 行为符合 LinuxCNC C++ task/motion/homing 真实实现。 - 每个按钮必须同时验收 UI gate、runtime gate、状态记录、非法命令拒绝、真实程序执行影响。 - 如果表面 compare 通过,但 C++ 状态机硬规则失败,结论必须是失败。 最终验收结论字段建议: ```json { "surfaceSummary": { "legacyComparePassCount": 60, "legacyCompareFailCount": 0 }, "functionalSummary": { "status": "pass", "failCount": 0, "requiredImprovements": [] } } ``` 只有 `functionalSummary.status == "pass"` 才允许写“真实通过”。 ## 2. 验收准备 ### 2.1 Native LinuxCNC 准备 命令: ```bash /home/mes123456/cnc_wams/linuxcnc/scripts/rip-environment \ python3 /home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/tools/collect-native-xyzbc-trt-evidence.py \ --run --timeout 90 ``` 必须采集: - `task.state` - `task.mode` - `task.interpState` - `task.execState` - `task.task_paused` - `task.currentLine` - `task.readLine` - `task.motionLine` - `motion.traj.enabled` - `motion.traj.paused` - `motion.traj.single_stepping` - `motion.traj.queue` - `motion.joint[].homing` - `motion.joint[].homed` - active G-code line、motion id、axis pose、tcp pose、tool axis ### 2.2 Web 准备 命令: ```bash npm --prefix /home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node npm --prefix /home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:browser npm --prefix /home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web ``` 必须采集与 native 同名字段,并额外采集: - UI button enabled/disabled - operator message - runtime command accepted/rejected - `machine.*` 状态快照 - Task/HAL status 快照 ### 2.3 Compare 准备 命令: ```bash npm --prefix /home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare ``` compare 必须输出: - 旧摘要 `surfaceSummary` - 新硬规则 `functionalSummary` - 每个 C++ 对标规则的 pass/fail 原因 ## 3. 基础状态枚举验收 ### T-STATE-001 task state 枚举一致 步骤: 1. 启动 Web runtime。 2. 依次发 `ESTOP`、`ESTOP_RESET`、`ON`、`OFF`。 3. 对照 LinuxCNC `EMC_TASK_STATE`。 期望: - `ESTOP -> estop` - `ESTOP_RESET -> estop-reset` - `ON -> on` - `OFF -> estop-reset/off 语义`,Web 显示必须与项目约定一致,但不能伪装为 on。 失败条件: - Web 只用 `powerOn` 推导 state。 - runtime status 与 UI 状态不一致。 ### T-STATE-002 task mode 枚举一致 步骤: 1. 在 machine on 状态依次切 `manual`、`auto`、`mdi`。 2. 在 `auto + reading` 时尝试切出 auto。 期望: - 正常 idle 下切换成功。 - `auto + reading` 切出 auto 必须被拒绝,或按 LinuxCNC abort/close/synch 语义处理并记录。 失败条件: - 解释器运行中静默切到 manual/mdi。 ### T-STATE-003 interp/exec 状态一致 步骤: 1. Run 前检查 `interpState=idle`、`execState=done`。 2. Run 中检查 `interpState=reading/waiting`,`execState` 可进入 waiting 状态。 3. 完成后检查 `interpState=idle`、`execState=done`。 失败条件: - 只记录 `runState`,没有 `interpState/execState`。 ## 4. 急停验收 ### T-ESTOP-001 ESTOP 可在任意状态触发 步骤: 1. machine on。 2. Home All。 3. Run 真实程序。 4. 在执行中发 ESTOP。 期望: - runtime 接受 `EMC_TASK_SET_STATE ESTOP`。 - `taskState=estop`。 - `motionEnabled=false`。 - `interpState=idle`。 - `taskPaused=false`、`motionPaused=false`。 - `singleStepping=false`、`motionStepping=false`。 - feed velocity 为 0。 - spindle/coolant 关闭。 失败条件: - ESTOP 后仍显示 running/paused/stepping。 - 位置继续推进。 - 只改 UI,不改 runtime status。 ### T-ESTOP-002 ESTOP_RESET 只解除急停但不上电 步骤: 1. 进入 ESTOP。 2. 发 RESET/ESTOP_RESET。 期望: - `taskState=estop-reset`。 - `powerOn=false`。 - `motionEnabled=false`。 - `interpState=idle`。 失败条件: - RESET 后直接上电。 ## 5. 上电/下电验收 ### T-POWER-001 ESTOP 下 Power 被拒绝 步骤: 1. 设置 `taskState=estop`。 2. 点击/发送 Power。 期望: - UI 禁用或 operator message 提示先解除急停。 - runtime 不接受 `ON`。 - 状态保持 estop。 失败条件: - ESTOP 下进入 on。 ### T-POWER-002 ESTOP_RESET 下 Power On 步骤: 1. RESET 到 `estop-reset`。 2. 点击 Power。 期望: - 发送 `EMC_TASK_SET_STATE ON`。 - `motionEnabled=true`。 - 最终 `taskState=on`。 失败条件: - 只设置 `powerOn=true`,runtime status 未 on。 ### T-POWER-003 ON 下 Power Off 步骤: 1. machine on。 2. Home All。 3. Run 或进入 idle。 4. 点击 Power Off。 期望: - 发送 `EMC_TASK_SET_STATE OFF`。 - abort/disable。 - `motionEnabled=false`。 - `interpState=idle`。 - pause/step 状态清理。 失败条件: - 下电后保留 running/paused/stepping。 ## 6. Home 验收 ### T-HOME-001 未上电 Home 被拒绝 步骤: 1. taskState=estop-reset。 2. 发 `EMC_JOINT_HOME -1` 或点击 Home All。 期望: - runtime 拒绝。 - operator message 指出 machine must be on。 - `homed[]` 不变化。 ### T-HOME-002 MANUAL/ON/IDLE Home All 步骤: 1. RESET。 2. Power On。 3. 切 Manual。 4. 发 Home All。 期望: - 出现 `homing=true` 瞬态。 - 完成后 `homing=false`。 - `homed[]` 全 true。 - `allHomed=true`。 - `homeState=homed` 或等价状态。 失败条件: - 没有 homing 状态事件。 - 只设置 `allHomed=true`,没有 per-joint `homed[]`。 ### T-HOME-003 Homing 中禁止重复 Home/Run/Step/Jog 步骤: 1. 启动 Home All。 2. 在 homing 仍 active 时发 Home、Run、Step、Jog。 期望: - 全部拒绝。 - 原 homing 流程不被破坏。 失败条件: - homing 中允许 Run 或 Step。 ## 7. Run 验收 ### T-RUN-001 未 Home Run 被拒绝 步骤: 1. machine on。 2. mode=auto。 3. 保持 `allHomed=false`。 4. 发 `EMC_TASK_PLAN_RUN`。 期望: - runtime 拒绝。 - 错误等价于 LinuxCNC `Can't run a program when not homed`。 - `interpState` 保持 idle。 失败条件: - 只因 UI 禁用,但 runtime 可直接 Run。 ### T-RUN-002 合法 Run 真实程序 步骤: 1. RESET。 2. Power On。 3. Home All。 4. 加载真实 G-code。 5. mode=auto。 6. Run。 期望: - `interpState=reading/waiting`。 - `taskPaused=false`。 - `singleStepping=false`。 - `currentLine/readLine/motionLine` 随执行推进。 - axis pose、tcp pose、tool axis 随真实程序变化。 - 完成后 `interpState=idle`。 失败条件: - 只播放前端路径,不产生 task/motion 状态流。 - line/motion id 不推进。 ### T-RUN-003 非 idle Run 被拒绝 步骤: 1. 合法 Run。 2. 在 reading 或 paused 状态再次发 Run。 期望: - reading 时拒绝。 - paused 时应提示 resume,而不是重新 Run。 失败条件: - 运行中重入 Run。 ## 8. Pause/Resume 验收 ### T-PAUSE-001 AUTO/READING Pause 步骤: 1. 合法 Run。 2. 等待进入 reading。 3. 发 Pause。 4. 连续采样至少 5 帧,每帧间隔 50ms。 期望: - `interpState=paused`。 - `interpResumeState=reading` 或 waiting。 - `taskPaused=true`。 - `motionPaused=true`。 - feed velocity 为 0。 - activeLine、axis pose、tcp pose、tool axis 在暂停采样中冻结。 失败条件: - 状态 paused 但位置继续变化。 - 只冻结 UI,不冻结 runtime feedback。 ### T-PAUSE-002 Resume 恢复 interpResumeState 步骤: 1. 在 T-PAUSE-001 暂停状态发 Resume。 期望: - `interpState` 恢复到 `interpResumeState`。 - `taskPaused=false`。 - `motionPaused=false`。 - `singleStepping=false`。 - 执行继续推进。 失败条件: - Resume 固定写 reading,丢失 waiting/mdi 语义。 ### T-PAUSE-003 非运行 Pause 被拒绝 步骤: 1. idle 状态发菜单 Pause。 期望: - 拒绝或忽略。 - 状态保持 idle。 失败条件: - idle 下进入 paused。 ## 9. Step 验收 ### T-STEP-001 IDLE Step 等价 Run 后 Pause 步骤: 1. RESET。 2. Power On。 3. Home All。 4. 加载真实程序。 5. mode=auto。 6. 在 idle 发 Step。 期望: - 启动 program run。 - 随后进入 paused。 - `taskPaused=true`。 - `motionPaused=true`。 - `singleStepping=true` 或 evidence 中记录 single stepping 事件。 - active line/motion id 到第一步后停止。 失败条件: - Step 只是前端 sample+1,没有 task/motion 状态。 ### T-STEP-002 PAUSED Step 放行到下一个 motion id 步骤: 1. 执行 T-STEP-001 或 Run 后 Pause。 2. 记录当前 `motionId/currentLine`。 3. 发 Step。 4. 等待下一次 paused。 期望: - motion id 或 currentLine 前进到下一段。 - 再次 `motionPaused=true`。 - `motionStepping` 事件出现后清理。 - 不是按固定采样点数量推进。 失败条件: - Step 后没有 line/motion id 变化。 - Step 后持续 running 不再暂停。 ### T-STEP-003 Resume/Abort/ESTOP/OFF 清 Step 步骤: 1. 进入 stepping/paused。 2. 分别执行 Resume、Abort、ESTOP、OFF。 期望: - `singleStepping=false`。 - `motionStepping=false`。 - 不残留 step lock。 失败条件: - 后续 Run 被旧 step 状态影响。 ## 10. 非法命令矩阵验收 必须在无 UI runtime 层执行: | 状态 | 命令 | 期望 | |---|---|---| | estop | ON | 拒绝,必须先 estop reset | | estop-reset | RUN | 拒绝,machine must be on | | on/manual/idle/unhomed | RUN | 拒绝,home first | | on/auto/reading | HOME | 拒绝,interpreter must be idle | | on/auto/reading | SET_MODE manual | 拒绝或按 C++ abort/synch 明确处理 | | on/auto/idle/no program | RUN | 拒绝,无程序 | | on/manual/idle | PAUSE | 拒绝 | | on/auto/idle | PAUSE | 拒绝或忽略 | | on/auto/paused/resumeInhibit | RESUME | 拒绝 | | homing active | RUN/STEP/JOG/HOME | 拒绝 | 失败条件: - UI 禁用但 runtime 接受非法命令。 - runtime 拒绝但 UI 显示允许。 - 非法命令改变状态。 ## 11. 真实程序功能验收 真实程序验收不能只看按钮状态,需要验证程序执行功能: 1. 使用 LinuxCNC `xyzbc-trt` 配置真实可运行 G-code。 2. Native 和 Web 使用同一 G-code、同一 INI/HAL/profile、同一采样周期。 3. 采集预览路径、执行路径、task 状态、motion 状态、joint home 状态、line/motion id。 4. 在执行中插入 Pause、Resume、Step、ESTOP。 5. 对比每个动作前后真实程序状态。 必须检查: - G-code line 读取顺序。 - `readLine/currentLine/motionLine` 对齐。 - motion id 单步推进。 - Pause 时路径冻结。 - Resume 后继续同一程序,不重头执行。 - ESTOP 后停止且不能继续运动。 - Home 前 Run 必须失败。 ## 12. Compare 硬通过标准 compare 输出必须包含以下硬检查: | 检查名 | 必须为 pass 的依据 | |---|---| | `cppTaskStateMachineParity` | ESTOP/RESET/ON/OFF 与 `emcTaskSetState()` 和 task update 推导一致 | | `cppModeGateParity` | `emcTaskSetMode()` 的 AUTO 非 idle 限制一致 | | `cppHomingParity` | `EMCMOT_JOINT_HOME` 和 `homing.c` 状态流一致 | | `cppRunGateParity` | `EMC_TASK_PLAN_RUN` 的 ON/AUTO/IDLE/Home/Program gate 一致 | | `cppPauseResumeParity` | `EMC_TASK_PLAN_PAUSE/RESUME` 和 `EMCMOT_PAUSE/RESUME` 一致 | | `cppStepParity` | `EMC_TASK_PLAN_STEP` 和 `EMCMOT_STEP` 的 motion id 单步一致 | | `illegalCommandParity` | runtime 和 UI gate 一致拒绝非法命令 | | `realProgramExecutionParity` | 真实程序 line/path/pose/status 一致 | 最终判定规则: ```text 如果 legacy compare 60/60 pass,但任一 cpp*Parity 或 realProgramExecutionParity fail,则整体验收 fail。 如果 smoke pass,但非法命令矩阵 fail,则整体验收 fail。 如果截图正常,但 task/motion 状态流不符合 C++,则整体验收 fail。 ``` ## 13. 验收记录模板 每轮验收必须记录: ```text 验收时间: LinuxCNC 源码 commit/版本: Web 项目 commit/工作区状态: 使用 INI: 使用 G-code: Native evidence: Web evidence: Compare evidence: surfaceSummary: functionalSummary: 失败项: 是否允许宣称真实通过: 结论: ``` 结论只能使用: - `真实通过`:所有 functional hard checks 通过。 - `表面通过但功能未通过`:legacy 60/60 通过但硬规则失败。 - `未通过`:legacy 或 functional 任一失败。 - `阻塞`:缺少 native evidence、Web evidence 或 C++ 映射。