feat: sync axis task state parity work

This commit is contained in:
wangdequan
2026-07-07 18:45:26 -04:00
parent 16484afce6
commit 8ef67f94c2
562 changed files with 175971 additions and 248 deletions

View File

@@ -1,5 +1,81 @@
# 03-推进台账
## 2026-07-07 18:18 EDT - AXIS 主控制按钮状态链路收敛轮次
### 本轮目标
按用户要求“按 working 完成全部工作”,依据 `18-20260707-AXIS主控制按钮调用链与Web完善指南.md`,把 Web 仿真的 `解除 ESTOP``上电/下电``Home All``Run``Pause/Resume``Step` 主控制链路收敛到 LinuxCNC AXIS/task/motion 的状态模型。
### 已做事项
- 修改 `app/src/state/linuxcnc-task-policy.js`
- 新增 `deriveLinuxCncTaskState()`,统一从 `estopActive + motionEnabled/powerOn` 派生 `estop``estop-reset``on`
- policy 状态补齐 `motionEnabled``homed[]``singleStepping``motionStepping``resumeInhibit``canPowerToggle``powerAction`
- 收紧 ESTOP 下 Power、Home、Run、Step、Resume 等按钮门禁Run 必须 AUTO/IDLE/已 Home/有程序。
- 修改 `app/src/state/store.js`
- 机器状态补齐 `motionEnabled``homed[]``singleStepping``motionStepping``resumeInhibit`
- ESTOP/RESET/Power/Home/Run/Stop/Pause/Resume/Step/RunFrame 统一清理或设置 pause/step/homing/home 状态。
- Home All 增加 `homing -> homed` 瞬态;下电映射到 `estop-reset` 并清 motion/home/pause/step。
- Task/HAL status 回写补齐 `motionEnabled``homed[]``singleStepping``motionStepping`
- 修改 `app/src/runtime/linuxcnc-task-hal-runtime.js``wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_task_hal_wasm.cpp`
- Task/HAL status 输出 `motionEnabled``homed[]`
- `EMC_JOINT_HOME` 产生 homing 状态并完成后设置每轴 homed。
- `EMC_TASK_SET_MODE MANUAL` 清 pause/step 并保持解释器 idle。
- `EMC_TASK_PLAN_RUN` 增加 ON/AUTO/IDLE/非 homing/已 Home/程序已打开/运动计划已加载门禁。
- 同步测试与 evidence
- 更新 Node/browser smoke 中 ESTOP+Power、ON->OFF、Home、Run、Step、Pause/Resume 断言。
- 新增/更新 Task/HAL 状态矩阵与 SDK 测试,覆盖 ESTOP、ESTOP_RESET、MANUAL、未 Home、homing、缺程序、缺 motion plan、合法 Run。
- 修复 Web evidence 采集里的 Task/HAL 运行前置序列,使其显式 `ON -> HOME -> cycle -> AUTO -> PLAN_RUN`
- impeller Task/HAL 测试改为基于实际 G93 逆时间 feed 段验证,而不是固定旧 feed 数值。
### 验证情况
```text
source /home/mes123456/emsdk/emsdk_env.sh && wasm-port/tools/build_task_hal_wasm.sh
linuxcnc_task_hal_wasm_build=ok
node wasm-port/tests/wasm/node/verify_task_hal_wasm.mjs
node wasm-port/tests/wasm/node/verify_task_state_matrix.mjs
node wasm-port/tests/wasm/node/verify_task_hal_sdk.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_run_feedback_loop.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_run_preconditions.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_linuxcnc_task_hal_runtime.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_rtcp_store.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_impeller_feed_task_hal_run.mjs
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run build
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:browser
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare
compare_xyzbc_trt_status=pass
```
最新 evidence 摘要:
```text
web.collectedAt=2026-07-07T22:17:57.900Z
web.executionPath.sampleCount=228
web.basicSimEquivalent.ready=true
web.taskHalEquivalence.ready=true
web.axisMainUi.ready=true
web.blockers=[]
compare.comparedAt=2026-07-07T22:17:58.519Z
compare.status=pass
compare.summary.checkCount=60
compare.summary.passCount=60
compare.summary.failCount=0
compare.summary.blockers=[]
compare.requiredImprovements=[]
```
### 结论
AXIS 主控制按钮状态链路已从 Web 自定义 `runState` 主导收敛为 LinuxCNC task/motion/home 状态事实主导。当前 T-001 到 T-078 均为完成,最新 Web evidence 和 compare 通过,仍为 `60/60 pass`
## 2026-07-05 18:57 EDT - Pause 按钮 LinuxCNC AXIS 严格对标修复轮次
### 本轮目标

View File

@@ -79,6 +79,7 @@
| T-075 | 严格验收冻结与回归门禁 | 完成 | 所有 T-051 到 T-074 通过后,冻结 manifest、证据路径、截图集和 compare 摘要;后续任何修改必须重新生成并通过源码/运行双基线 compare |
| T-076 | 2026-07-04 18:12 EDT 真实执行复验与缺失功能工作计划 | 完成 | 重新执行 native LinuxCNC、Web evidence、Node smoke、browser smoke 和 compare`compare.status=pass``60/60 pass``blockers=[]`,缺失功能清单为空,后续按 `12-20260704-真实执行复验与缺失功能工作计划.md` 做变更后强制复验 |
| T-077 | 2026-07-05 用户要求完全对标复验与文档修订 | 完成 | 明确硬件相关排除边界;以 `/home/mes123456/cnc_wams/linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini` 为 native 权威基线重新执行 native/Web/compare/build/smoke`compare.status=pass``60/60 pass``blockers=[]``requiredImprovements=[]` |
| T-078 | 2026-07-07 AXIS 主控制按钮状态链路收敛 | 完成 | 按 `18-20260707-AXIS主控制按钮调用链与Web完善指南.md` 完成 ESTOP、Power、Home、Run、Pause/Resume、Step 的 LinuxCNC task/motion/home 状态收敛Task/HAL WASM 暴露 `motionEnabled``homed[]``singleStepping``motionStepping`policy 统一门禁build、Node/WASM/browser smoke、Web evidence 和 compare 均通过,最新 `compare.status=pass``60/60 pass``blockers=[]``requiredImprovements=[]` |
状态说明:
@@ -86,10 +87,12 @@
- 待前置:代码已接入,但仍依赖未完成的外部或运行环境步骤。
- 待实现:已记录验收要求,仍需修改采集或对比脚本。
当前 T-001 到 T-077 均已完成T-047/T-048/T-049/T-050 是用户指出 `working/screenshots/web-simulation-full-process-20260703T051258Z` 截图问题后的页面/截图层真实执行闭环项。
当前 T-001 到 T-078 均已完成T-047/T-048/T-049/T-050 是用户指出 `working/screenshots/web-simulation-full-process-20260703T051258Z` 截图问题后的页面/截图层真实执行闭环项。
T-051 到 T-075 是 2026-07-04 新增的严格完整对标任务:以 `/home/mes123456/cnc_wams/linuxcnc` 源树和 LinuxCNC `xyzbc-trt` 真实执行为唯一 native 权威基线,当前已完成,最新 compare 为 60/60 passblockers=[]。
T-076 是 2026-07-04 18:12 EDT 的重新真实执行复验和缺失功能分析任务:本轮未发现新增缺失功能,后续工作计划转为固定当前证据、变更后强制复验和 fail/blocker 处置流程。
T-077 是 2026-07-05 用户再次要求“除了硬件相关,在仿真的所有方面完全对标”后的复验与文档修订任务:最新 native/Web/compare/build/smoke 全部通过compare 为 60/60 passblockers=[]requiredImprovements=[]。
T-078 是 2026-07-07 按 AXIS 主控制按钮调用链完善指南完成的状态链路收敛任务Web 主控制按钮已统一到 LinuxCNC task/motion/home 状态事实,最新 Web evidence/compare/build/smoke 均通过compare 为 60/60 passblockers=[]requiredImprovements=[]。

View File

@@ -1,5 +1,106 @@
# 05-验收证据
## 2026-07-07 18:18 EDT - AXIS 主控制按钮状态链路收敛证据
本轮按 `working/18-20260707-AXIS主控制按钮调用链与Web完善指南.md` 完成主控制按钮状态链路收敛,覆盖:
```text
ESTOP / RESET
Power ON / OFF
Home All
Run
Pause / Resume
Step
Task/HAL status -> Web policy/store 回写
Web evidence -> compare
```
关键实现证据:
```text
app/src/state/linuxcnc-task-policy.js
app/src/state/store.js
app/src/runtime/linuxcnc-task-hal-runtime.js
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_task_hal_wasm.cpp
tools/collect-web-xyzbc-trt-evidence.mjs
tests/node/verify_xyzbc_trt_web_app.mjs
tests/browser/xyzbc_trt_browser_smoke.html
wasm-port/tests/wasm/node/verify_task_state_matrix.mjs
```
执行命令:
```text
source /home/mes123456/emsdk/emsdk_env.sh && wasm-port/tools/build_task_hal_wasm.sh
node --check web-rtcp-5axis-xyzbc-trt-sim-plan/app/src/state/store.js
node --check web-rtcp-5axis-xyzbc-trt-sim-plan/app/src/state/linuxcnc-task-policy.js
node --check web-rtcp-5axis-xyzbc-trt-sim-plan/app/src/runtime/linuxcnc-task-hal-runtime.js
node --check web-rtcp-5axis-xyzbc-trt-sim-plan/tools/collect-web-xyzbc-trt-evidence.mjs
node wasm-port/tests/wasm/node/verify_task_hal_wasm.mjs
node wasm-port/tests/wasm/node/verify_task_state_matrix.mjs
node wasm-port/tests/wasm/node/verify_task_hal_sdk.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_run_feedback_loop.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_run_preconditions.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_linuxcnc_task_hal_runtime.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_rtcp_store.mjs
node web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_impeller_feed_task_hal_run.mjs
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run build
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:browser
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare
```
关键输出:
```text
linuxcnc_task_hal_wasm_build=ok
linuxcnc_task_runtime_smoke=ok
task_state_matrix=ok
linuxcnc_task_hal_sdk=ok
run_feedback_status_loop_smoke=ok
run_preconditions_ini_profile_smoke=ok
linuxcnc_task_hal_runtime_smoke=ok
gmoccapy_xyzab_gates_smoke=ok
rtcp_store_smoke=ok
impeller_feed_task_hal_run=ok
gmoccapy_static_build=ok
xyzbc_trt_web_app_smoke=ok
xyzbc_trt_browser_smoke=ok
web_xyzbc_trt_evidence=/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/web-xyzbc-trt-evidence.json
compare_xyzbc_trt_evidence=/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/compare-xyzbc-trt-evidence.json
compare_xyzbc_trt_status=pass
```
最新 evidence 摘要:
```text
web.status=ready-for-wasm-runtime
web.collectedAt=2026-07-07T22:17:57.900Z
web.executionPath.sampleCount=228
web.executionPath.status=ok
web.basicSimEquivalent.ready=true
web.taskHalEquivalence.ready=true
web.axisMainUi.ready=true
web.blockers=[]
compare.status=pass
compare.comparedAt=2026-07-07T22:17:58.519Z
compare.summary.checkCount=60
compare.summary.passCount=60
compare.summary.failCount=0
compare.summary.blockers=[]
compare.summary.nativeStatus=ok
compare.summary.webStatus=ready-for-wasm-runtime
compare.requiredImprovements=[]
```
结论AXIS 主控制按钮状态链路收敛完成。Web 主控制按钮不再依赖单一 `runState` 判断,而是由 LinuxCNC task/motion/home 状态事实驱动;当前 compare 仍为 `60/60 pass`
## 2026-07-05 18:57 EDT - Pause 按钮 LinuxCNC AXIS 对标修复证据
本轮按用户要求,以 `/home/mes123456/cnc_wams/linuxcnc` 源程序中的 AXIS 行为严格对标 Pause。

View File

@@ -0,0 +1,479 @@
# 2026-07-07 AXIS 主控制按钮调用链与 Web 完善指南
## 1. 文档目标
本文在以下资料基础上整理一个可执行的 Web 项目完善指南:
- `/home/mes123456/cnc_wams/linuxcnc`
- `/home/mes123456/cnc_wams/wasm-port`
- `/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan`
- `/home/mes123456/cnc_wams/项目分析/AXIS主控制按钮调用链与WASM完善指南.md`
- `/home/mes123456/cnc_wams/项目分析/AXIS主界面按钮调用链与WASM完善建议.md`
重点覆盖 AXIS 主界面“急停、上电、Home、执行、暂停、单步执行”的调用链、先决条件、状态记录机制以及 Web 项目后续应如何按 LinuxCNC task/motion 语义继续完善。
## 2. 总体结论
LinuxCNC AXIS 按钮不是直接修改界面状态,而是走统一控制链:
```text
AXIS Tcl/Tk 按钮
-> axis.py 回调
-> linuxcnc.command() Python C++ 扩展
-> EMC_* NML 命令
-> milltask/emctaskmain.cc emcTaskPlan()
-> emcTaskIssueCommand()
-> emctask.cc task 状态动作
-> taskintf.cc 写 EMCMOT_* motion 命令
-> motion/command.c、control.c、homing.c 更新实时状态
-> emcMotionUpdate()/emcTaskUpdate() 写 emcStatus
-> linuxcnc.stat().poll()
-> AXIS Tk 变量 trace 刷新按钮可用性
```
Web 项目必须把状态模型收敛到同一组事实字段,而不是只用 `runState` 或按钮 active 样式:
| LinuxCNC 字段 | Web 当前/目标字段 | 用途 |
|---|---|---|
| `task.state` | `machine.taskState` | `estop``estop-reset``on` 的主门禁 |
| `task.mode` | `machine.mode` | `manual``auto``mdi` 的命令门禁 |
| `task.interpState` | `machine.interpState` | `idle``reading``paused``waiting` |
| `task.task_paused` | `machine.taskPaused` | task/interpreter 层暂停 |
| `motion.traj.paused` | `machine.motionPaused` | motion/trajectory 层暂停 |
| `motion.traj.single_stepping` | 目标新增/统一为 `machine.singleStepping` | 单步执行可见状态 |
| `motion.joint[n].homed` | `machine.allHomed` 和目标 `machine.homed[]` | Home/Run 门禁 |
| IO estop + motion enabled | `estopActive` + `powerOn` 派生 `taskState` | 对标 `determineState()` |
## 3. AXIS UI 层按钮门禁
AXIS 的按钮可点状态在 `linuxcnc/share/axis/tcl/axis.tcl:update_state()` 集中维护:
| 按钮 | AXIS UI 可点条件 |
|---|---|
| 急停 | 始终可点;当前 ESTOP 时执行解除急停,否则执行急停 |
| 上电/下电 | `task_state != STATE_ESTOP`;但真正上电只在 `STATE_ESTOP_RESET` 分支 |
| Home/Unhome/Zero | `task_state == STATE_ON && interp_state == INTERP_IDLE` |
| Run | `task_state == STATE_ON && interp_state == INTERP_IDLE` |
| Step | `task_state == STATE_ON && taskfile != ""` |
| 菜单 Pause | `task_state == STATE_ON && interp_state in {READING, WAITING}` |
| 菜单 Resume | `task_state == STATE_ON && interp_state == PAUSED` |
| 工具栏 Pause/Resume | `task_state == STATE_ON && interp_state != IDLE` |
| Stop | `task_state == STATE_ON && interp_state != IDLE` |
Python 回调还有二次门禁,例如 `manual_ok()` 要求 `STATE_ON`,并要求解释器空闲或 MDI 队列可接收命令。Web 项目中的 `app/src/state/linuxcnc-task-policy.js` 应继续作为唯一按钮门禁入口,避免 UI 组件各自判断。
## 4. 急停
### 4.1 LinuxCNC 调用链
```text
axis.py estop_clicked()
-> s.poll()
-> 当前 STATE_ESTOP: c.state(STATE_ESTOP_RESET)
否则: c.state(STATE_ESTOP)
-> emcmodule.cc state()
-> EMC_TASK_SET_STATE
-> emctaskmain.cc emcTaskPlan()
-> emcTaskIssueCommand()
-> emctask.cc emcTaskSetState()
```
`EMC_TASK_SET_STATE` 在 ESTOP、ESTOP_RESET、OFF、ON 多状态下都是 immediate command可在运动中触发。
### 4.2 task/motion 状态动作
`emcTaskSetState(ESTOP)` 的关键动作:
- `emcMotionAbort()` 中止 motion
- `emcSpindleAbort()` 中止主轴;
- `emcAuxEstopOn()` 置 IO 急停;
- `emcTrajDisable()` 下发 `EMCMOT_DISABLE`
- `emcTaskAbort()` 清解释器、队列、暂停、单步;
- `emcIoAbort(TASK_STATE_ESTOP)` 通知 IO
- `emcJointUnhome(-2)` 清除 `VOLATILE_HOME` joint 的 homed
- `emcTaskPlanSynch()` 同步解释器。
实际 `task.state``emctask.cc:determineState()` 周期推导:
```text
io.aux.estop == true -> ESTOP
io.aux.estop == false && motion enabled false -> ESTOP_RESET
io.aux.estop == false && motion enabled true -> ON
```
### 4.3 Web 完善要求
当前 `store.js` 已有 `ESTOP`/`RESET` 分支,但后续应补齐:
- 急停时同步清 `machine.motionPaused=false``machine.singleStepping=false``taskHalPauseLock=null``programRuntimeFeedback.currentVelocity=0`
- 急停时只清 volatile home而不是无条件清全部 Home如果 Web 暂无 `VOLATILE_HOME` 配置,需在文档和 evidence 中声明 `xyzbc-trt` 的处理策略。
- `taskState` 不应由 `powerOn` 简单替代,应通过 `deriveTaskState({ estopActive, motionEnabled })` 一处派生。
- Task/HAL runtime 可用时UI 先进入安全预期状态,但最终以 `readStatus()` 回写为准。
## 5. 上电/下电
### 5.1 LinuxCNC 调用链
```text
axis.py onoff_clicked()
-> 当前 STATE_ESTOP_RESET: c.state(STATE_ON)
否则: c.state(STATE_OFF)
-> emcmodule.cc state()
-> EMC_TASK_SET_STATE
-> emctask.cc emcTaskSetState(ON/OFF)
-> taskintf.cc emcTrajEnable()/emcTrajDisable()
-> motion/command.c EMCMOT_ENABLE/EMCMOT_DISABLE
```
上电不是“非急停即可开机”。AXIS 的 Python 分支要求当前正好是 `STATE_ESTOP_RESET`,否则按钮发送 `STATE_OFF`
### 5.2 task/motion 状态动作
`emcTaskSetState(ON)`
- `emcTrajEnable()``EMCMOT_ENABLE`
- motion 侧要求 HAL `motion.enable` 输入为真,否则报错;
- enable 在 motion 控制周期中完成,随后 `determineState()` 才推导出 `STATE_ON`
`emcTaskSetState(OFF)`
- 中止 motion、主轴、IO
- `emcTrajDisable()`
- `emcTaskAbort()`
- `emcJointUnhome(-2)`
- 同步解释器。
### 5.3 Web 完善要求
`TOGGLE_POWER` 应严格保持:
- 只有 `machine.taskState === "estop-reset"` 时执行 ON
- 当前为 `on` 时执行 OFF
- 当前为 `estop` 时按钮应不可用或返回 `power blocked: reset ESTOP first`,不要把 ESTOP 下的点击伪装成上电。
建议在 `linuxcnc-task-policy.js` 增加 `canPowerToggle``powerAction`
```text
taskState == estop -> disabled
taskState == estop-reset -> action ON
taskState == on -> action OFF
```
## 6. Home
### 6.1 LinuxCNC 调用链
```text
axis.py home_all_joints()/home_joint()
-> manual_ok()
-> ensure_mode(MODE_MANUAL)
-> go_home(-1 或 joint)
-> set_motion_teleop(0)
-> c.home(joint)
-> emcmodule.cc home()
-> EMC_JOINT_HOME
-> emctaskmain.cc emcTaskIssueCommand()
-> taskintf.cc emcJointHome()
-> motion/command.c EMCMOT_JOINT_HOME
-> homing.c do_home_joint()/do_homing()
```
motion 侧 `EMCMOT_JOINT_HOME` 要求:
- 当前 `motion_state == EMCMOT_MOTION_FREE`,即 joint/free mode
- `motion.homing-inhibit` 为 false
- 当前没有 homing 正在执行;
- motion enable 为真;
- `joint=-1` 表示 Home All。
### 6.2 状态记录方式
`homing.c` 用每关节 `H[jno]` 记录:
- `home_state``HOME_IDLE``HOME_START``HOME_SEARCH_*``HOME_FINISHED``HOME_ABORT` 等;
- `homing`:当前关节正在回零;
- `homed`:当前关节已回零;
- `homing_active`:全局回零状态。
`control.c` 周期把 `get_homing(joint)``get_homed(joint)` 写入 motion status并更新 HAL `motion.is-all-homed`
### 6.3 Web 完善要求
当前 Web 的 `HOME` 已设置 `allHomed` 和 home pose但还应补齐
- `machine.homed` 数组,长度来自 profile/INI joint 数;
- `machine.homing` 和 per-joint `homing` 短暂状态,即使仿真为零速回零,也应经过 `homing -> homed` 的状态事件;
- Home All 与单 joint Home 的命令对象应对应 `EMC_JOINT_HOME { joint: -1|n }`
- Home 执行前强制切 `manual`,且 `manualPanel` 回到 manual/joint 语义;
- 非 identity kinematics 下按坐标轴 Home 应拒绝,提示使用 joint mode
- 回零过程中禁止 Jog、Run、再次 Home。
## 7. 执行 Run
### 7.1 LinuxCNC 调用链
```text
axis.py task_run()
-> run_warn()/reload_file()
-> ensure_mode(MODE_AUTO)
-> c.auto(AUTO_RUN, program_start_line)
-> emcmodule.cc emcauto()
-> EMC_TASK_PLAN_RUN
-> emctaskmain.cc emcTaskPlan()
-> emcTaskIssueCommand()
-> all_homed() 检查
-> emcTaskPlanOpen()
-> programStartLine = run_msg->line
-> task.interpState = READING
-> task.task_paused = 0
-> readahead_reading()/emcTaskExecute()
-> canonical motion -> taskintf -> motion queue
```
task 层的关键硬门禁:
- 机器必须 `STATE_ON`
- AUTO 模式;
- 解释器必须 `IDLE` 才能正常开始新 Run
- 未全部回零且 `no_force_homing` 为 false 时拒绝:`Can't run a program when not homed`
- 程序文件需要已打开,或 `task.file` 可被 `emcTaskPlanOpen()` 打开。
### 7.2 状态记录方式
Run 后 task 写:
- `interpState = READING`
- `task_paused = 0`
- `motion.traj.single_stepping = 0`
- `stepping = 0``steppingWait = 0`
- `programStartLine = run_msg->line`
解释器完成、abort、错误或下级状态变为非 ON 时,再回到 `IDLE` 并清执行状态。
### 7.3 Web 完善要求
当前 Web 已有 `RUN_FROM_OPERATOR``RUN`、Task/HAL status loop、50ms 截图验证。后续要继续收敛:
- `RUN` 不应直接由 `runState` 决定,应以 `taskState/mode/interpState/allHomed/programOpen` 为准;
- `RUN_FROM_OPERATOR` 可以做引导序列,但最终应仍发 `SET_MODE AUTO``EMC_TASK_PLAN_RUN`
- `programStartLine` 应统一为 LinuxCNC 行号语义,避免 UI 1-based 与 task `line=0` 混用;
- Task/HAL runtime 可用时,应从 `taskHalStatus.ui.activeLine``motion.program-line` 回写 `activeLine`
- 结束时必须经过 `interpState=idle``runState=complete/idle``taskPaused=false`
## 8. 暂停/恢复
### 8.1 LinuxCNC 调用链
菜单 Pause
```text
axis.py task_pause()
-> 必须 MODE_AUTO 且 interp_state in {READING, WAITING}
-> c.auto(AUTO_PAUSE)
-> EMC_TASK_PLAN_PAUSE
```
工具栏 Pause/Resume
```text
axis.py task_pauseresume()
-> 必须 MODE_AUTO 或 MODE_MDI
-> s.paused 为真: c.auto(AUTO_RESUME)
-> 否则 interp_state != IDLE: c.auto(AUTO_PAUSE)
```
task 执行:
```text
EMC_TASK_PLAN_PAUSE
-> emcTrajPause()
-> interpResumeState = 当前 interpState
-> task.interpState = PAUSED
-> task.task_paused = 1
EMC_TASK_PLAN_RESUME
-> emcTrajResume()
-> task.interpState = interpResumeState
-> task.task_paused = 0
-> motion.traj.single_stepping = 0
-> stepping = 0
```
motion 侧:
```text
EMCMOT_PAUSE -> tpPause(); motion.paused = 1
EMCMOT_RESUME -> tpResume(); motion.paused = 0; motion.stepping = 0
```
### 8.2 Web 完善要求
当前 `PAUSE``PAUSE_RESUME``RESUME` 已基本对标。后续补齐点:
- `s.paused` 对应 motion paused不只是 `interpState=="paused"`
- `taskPaused``motionPaused` 必须一起出现在状态面板、evidence 和测试断言;
- 暂停期间 `programExecutionSampleIndex``axisPose``toolAxisVector`、Vismach pins 必须冻结;
- 恢复时使用 `interpResumeState`,不要固定回 `reading`
- `resume_inhibit` 如果 Web 暂不实现,应在策略状态中显式 `resumeInhibit=false`,并预留 gate。
## 9. 单步执行 Step
### 9.1 LinuxCNC 调用链
```text
axis.py task_step()
-> 如果不是 AUTO 或解释器非 IDLE先清 highlight 并做 run_warn()
-> ensure_mode(MODE_AUTO)
-> c.auto(AUTO_STEP)
-> emcmodule.cc emcauto()
-> EMC_TASK_PLAN_STEP
-> emctaskmain.cc emcTaskPlan()
```
task 中分状态处理:
- `AUTO + IDLE`:把 Step 转成一次 `EMC_TASK_PLAN_RUN(line=0)`,随后立即 `emcTrajPause()``interpState=PAUSED``task_paused=1`
- `AUTO + READING/WAITING`:设置 `motion.traj.single_stepping=1``stepping=1``steppingWait=0`
- `AUTO + PAUSED`:若 motion queue 中有暂停运动,发 `emcTrajStep()`;否则恢复解释器到 `interpResumeState` 以读下一步。
motion 侧:
```text
EMCMOT_STEP
-> 如果 motion.paused:
idForStep = current id
motion.stepping = 1
tpResume()
motion.paused = 1
否则报错 "can't STEP while already executing"
control.c 周期:
if stepping && idForStep != current id:
tpPause()
stepping = 0
paused = 1
```
### 9.2 Web 完善要求
当前 `STEP` 会推进一个 sample 并设 paused。后续应改成更接近 LinuxCNC 的两层语义:
- 增加 `machine.singleStepping``machine.motionStepping`
- 第一次 Step 在 IDLE 时等价于启动 Auto Run 后暂停;
- 已暂停且 motion queue 有待执行段时Step 只释放到下一个 motion id/line而不是简单固定一个 sample
- Step 后状态应为 `interpState=paused``taskPaused=true``motionPaused=true``singleStepping=true/随后 false`
- Resume 必须清 `singleStepping`
## 10. WASM-port 完善方向
`wasm-port/SKILL.md` 的原则是CNC 语义来自 LinuxCNC vendored sourceJS/SDK 只做适配。针对本按钮状态机,建议按以下顺序补齐:
1.`wasm-port` 的 task/HAL SDK 中暴露完整 task 状态:
- `state``mode``interpState``interpResumeState``taskPaused``singleStepping``programOpen``programStartLine`
2. 暴露 motion 状态:
- `enabled``paused``stepping``idForStep``queueDepth``programLine``homing``homed[]``allHomed`
3.`EMC_TASK_SET_STATE``EMC_TASK_SET_MODE``EMC_JOINT_HOME``EMC_TASK_PLAN_RUN/PAUSE/RESUME/STEP` 作为 JSON command 的稳定 ABI。
4. 对照 LinuxCNC `emcTaskSetState()``emcTaskPlan()` 增加任务状态矩阵测试。
5. 对照 `motion/command.c` 增加 pause/resume/step motion id 测试。
6. 对照 `homing.c` 增加 Home All、重复 Home、homing-inhibit、正在 homing 时拒绝再次 Home 的测试。
Web 项目不要在 JS 中重新实现解释器、G-code、planner 语义;可以做 UI 预期状态,但最终要以 Task/HAL WASM status 回写为准。
## 11. web-rtcp 项目落地清单
### 11.1 应修改/复核的核心文件
| 文件 | 后续职责 |
|---|---|
| `app/src/state/linuxcnc-task-policy.js` | 唯一按钮门禁;补 `canPowerToggle``canStepStrict``resumeInhibit``singleStepping` |
| `app/src/state/store.js` | 按 EMC 命令语义更新状态;清理只依赖 `runState` 的残留判断 |
| `app/src/runtime/linuxcnc-task-hal-runtime.js` | 标准化 Task/HAL status`enabled``homed[]``singleStepping` |
| `app/src/ui/axis-shell.js` | AXIS 按钮可用性只读 policy不散落条件 |
| `tests/node/verify_xyzbc_trt_web_app.mjs` | 补逐按钮非法状态矩阵 |
| `tests/node/verify_rtcp_store.mjs` | 补状态记录和派生状态断言 |
| `tools/verify-estop-power-home-run-pause-50ms.mjs` | 扩展 Step、Home 过程状态和 motion freeze 断言 |
| `tools/collect-web-xyzbc-trt-evidence.mjs` | evidence 输出按钮状态矩阵和状态流 |
| `tools/compare-xyzbc-trt-evidence.mjs` | 对比 native/Web 的按钮先决条件、状态转移和暂停冻结 |
### 11.2 建议新增状态对象
```js
machine: {
taskState: "estop" | "estop-reset" | "on",
mode: "manual" | "auto" | "mdi",
interpState: "idle" | "reading" | "paused" | "waiting",
interpResumeState: "idle" | "reading" | "waiting",
taskPaused: boolean,
motionPaused: boolean,
singleStepping: boolean,
motionStepping: boolean,
motionEnabled: boolean,
homing: boolean,
homed: boolean[],
allHomed: boolean,
noForceHoming: boolean,
resumeInhibit: boolean
}
```
### 11.3 验收矩阵
| 场景 | 期望 |
|---|---|
| ESTOP 下点 Power | 不上电,提示先解除急停 |
| ESTOP_RESET 下点 Power | `taskState=on``motionEnabled=true` |
| ON 下点 Power | abort、disable、`taskState=estop-reset` |
| 未上电 Home | 拒绝 |
| AUTO/READING Home | 拒绝 |
| MANUAL/ON/IDLE Home All | 进入 homing完成后 `homed[]` 全 true |
| 未 Home Run | 拒绝,除非 `noForceHoming=true` |
| ON/AUTO/IDLE Run | `interpState=reading``taskPaused=false` |
| AUTO/READING 菜单 Pause | `interpState=paused``taskPaused=true``motionPaused=true` |
| MANUAL 残留 running 点 Pause | 拒绝 |
| 工具栏 paused 点 Pause/Resume | Resume恢复到 `interpResumeState` |
| IDLE Step | 等价启动 Run 后暂停 |
| PAUSED Step | 只推进到下一个 motion id/line 后再次暂停 |
| 急停 during Run | abort清 pause/stepvelocity=0状态 ESTOP |
## 12. 推荐实施顺序
1. 先补 `linuxcnc-task-policy.js` 的严格状态矩阵,不改 UI。
2.`store.js``deriveTaskState()``singleStepping``homed[]`、急停/下电清理。
3. 补 Task/HAL status normalization使 WASM status 能覆盖 JS 预期状态。
4. 扩展 Node 测试覆盖非法状态矩阵。
5. 扩展 50ms Playwright 工具,加入 Step 和 Home 过程截图/evidence。
6. 更新 `collect-web``compare`,把按钮状态流纳入 `60/60` 之外的硬检查。
## 13. 当前判断
现有 Web 项目已经具备主干:`linuxcnc-task-policy.js``store.js`、Task/HAL runtime、50ms 截图验证和 native/Web compare。下一阶段的重点不是新增按钮而是把按钮行为的状态来源从“Web 自定义运行态”进一步收敛到 LinuxCNC 的 `task.state + task.mode + interpState + motion status + homing status`,并让所有测试和 evidence 都能证明这一点。
## 14. 2026-07-07 实施完成记录
截至 2026-07-07 18:18 EDT本指南中的 Web 完善清单已完成一轮落地:
- `linuxcnc-task-policy.js` 已作为主控制按钮唯一门禁入口,新增 `motionEnabled``homed[]``singleStepping``motionStepping``resumeInhibit``canPowerToggle``powerAction` 等状态输出。
- `store.js` 已把 `ESTOP/RESET/Power/Home/Run/Pause/Resume/Step` 收敛到 `taskState + mode + interpState + motion/home status`,并清理下电、急停、暂停、单步、回零的状态残留。
- Task/HAL WASM 已输出 `motionEnabled``homed[]`,并在 `EMC_TASK_PLAN_RUN` 中执行 ON、AUTO、IDLE、非 homing、已 Home、程序打开、motion plan 已加载的硬门禁。
- Home All 已具备 `homing -> homed` 瞬态记录Run/Step/Pause/Resume 的 task/motion 状态与 Web policy、status loop 和 evidence 采集同步。
- `collect-web-xyzbc-trt-evidence.mjs` 已改为合法 Task/HAL 运行准备序列:`ON -> HOME -> cycle -> AUTO -> PLAN_RUN`
本轮验证结果:
```text
linuxcnc_task_hal_wasm_build=ok
linuxcnc_task_runtime_smoke=ok
task_state_matrix=ok
linuxcnc_task_hal_sdk=ok
xyzbc_trt_web_app_smoke=ok
xyzbc_trt_browser_smoke=ok
gmoccapy_static_build=ok
compare_xyzbc_trt_status=pass
compare.summary.checkCount=60
compare.summary.passCount=60
compare.summary.failCount=0
compare.summary.blockers=[]
compare.requiredImprovements=[]
```
结论:本指南从“建议实施”转为“已实施并通过复验”。后续若继续扩展截图工具或 compare 检查,应在当前 T-078 基线之上追加新任务,而不是重新发散按钮状态来源。

View File

@@ -0,0 +1,560 @@
# 2026-07-07 AXIS 按钮 LinuxCNC task/motion 状态机制源码分析
## 1. 分析范围与结论
本文件针对 `/home/mes123456/cnc_wams/linuxcnc` 中 AXIS 主界面的“急停、上电、Home、执行、暂停、单步执行”做源码级调用链和状态机制分析并给 `/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan` 的完全对标提供状态字段依据。
核心结论:
- AXIS 主界面不是 C++,按钮回调在 `src/emc/usr_intf/axis/scripts/axis.py`,状态和命令通过 `linuxcnc` Python C++ 扩展进入 NML。
- C++ `task` 层是按钮命令的主状态机,主要在 `src/emc/task/emctaskmain.cc``src/emc/task/emctask.cc``src/emc/task/taskintf.cc`
- `motion` 层执行实时 enable、pause、step、home、homed/homing 记录,主要在 `src/emc/motion/command.c``src/emc/motion/control.c``src/emc/motion/homing.c`
- AXIS 不直接相信按钮本地状态;它周期 `stat.poll()`,从 `EMC_STAT` 读回 `task.state``task.mode``task.interpState``task.task_paused``motion`/`joint` 状态后刷新 UI。
## 2. 统一调用链
通用链路如下:
```text
AXIS Tk 按钮/快捷键
-> axis.py 回调
-> linuxcnc.command() / linuxcnc.stat()
-> src/emc/usr_intf/axis/extensions/emcmodule.cc
-> EMC_* NML 命令对象
-> milltask: src/emc/task/emctaskmain.cc emcTaskPlan()
-> emcTaskIssueCommand()
-> src/emc/task/emctask.cc 状态动作 或 src/emc/task/taskintf.cc motion 命令
-> src/emc/motion/command.c 实时 motion 命令处理
-> src/emc/motion/control.c 周期控制与状态输出
-> emcStatusBuffer->write(emcStatus)
-> AXIS stat.poll() 刷新按钮、DRO、状态栏
```
状态枚举来自 `src/emc/nml_intf/emc.hh`
- `EMC_TASK_STATE`: `ESTOP``ESTOP_RESET``OFF``ON`
- `EMC_TASK_MODE`: `MANUAL``AUTO``MDI`
- `EMC_TASK_INTERP`: `IDLE``READING``PAUSED``WAITING`
- `EMC_TASK_EXEC`: `DONE``WAITING_FOR_MOTION``WAITING_FOR_IO``WAITING_FOR_MOTION_AND_IO``ERROR` 等。
NML 命令对象来自 `src/emc/nml_intf/emc_nml.hh`
- `EMC_TASK_SET_STATE` 记录目标 task state。
- `EMC_TASK_SET_MODE` 记录目标 mode。
- `EMC_JOINT_HOME` 记录 joint 编号。
- `EMC_TASK_PLAN_RUN` 记录起始行 `line`
- `EMC_TASK_PLAN_PAUSE``EMC_TASK_PLAN_RESUME``EMC_TASK_PLAN_STEP` 是无额外参数的 task plan 命令。
## 3. 状态记录与发布机制
### 3.1 AXIS UI 读状态
AXIS 的 `stat` 成员暴露在 `emcmodule.cc`
- `task_mode``task_state``exec_state``interp_state``read_line``motion_line``current_line``file` 等字段直接映射 `EMC_STAT.task`
- `state()``mode()``home()``auto()` 分别把 Python 调用转换成 NML 命令发送。
`axis.py` 周期刷新中把读回状态写入 Tk 变量:
- `vars.task_mode <- self.stat.task_mode`
- `vars.task_state <- self.stat.task_state`
- `vars.task_paused <- self.stat.task_paused`
- `vars.interp_state <- self.stat.interp_state`
- `vars.interp_pause <- self.stat.paused`
这意味着 AXIS 按钮 active/disabled 不是按钮点击后本地硬改,而是以 task/motion 返回状态为准。
### 3.2 task 状态发布
`emctaskmain.cc` 主循环每周期:
- `emcMotionUpdate(&emcStatus->motion)` 读 motion 状态。
- `emcTaskUpdate(&emcStatus->task)` 更新 task mode/state。
- 设置 `emcStatus->task.command_type``echo_serial_number`
- 根据 task、motion、io 是否 `ERROR/DONE/EXEC` 写顶层 status。
- 最后 `emcStatusBuffer->write(emcStatus)` 发布给 GUI。
`emcTaskUpdate()` 最终由 `emctask.cc` 中的推导逻辑更新 task mode/state。task state 不是简单等于上次按钮命令,而是由 IO 急停与 motion enable 共同推导:
```text
io.aux.estop == true -> task.state = ESTOP
io.aux.estop == false && motion enabled false -> task.state = ESTOP_RESET/OFF 语义
io.aux.estop == false && motion enabled true -> task.state = ON
```
### 3.3 motion/homing 状态发布
motion 实时周期 `control.c` 执行:
- 读 homing 输入、处理 kinematics、probe、fault、operating mode。
- 在 free mode 下调用 `do_homing()`
- 输出 HAL pin 与 motion status。
Home 状态由 `homing.c``H[jno]` 结构记录:
- `home_state`: `HOME_IDLE``HOME_START`、各类 search/latch/index/final move、`HOME_FINISHED``HOME_ABORT`
- `homing`: 当前 joint 正在 homing。
- `homed`: 当前 joint 已 homed。
- `homing_active`: 全局是否仍有 homing 流程。
`control.c``get_homing(joint)``get_homed(joint)` 写入 `joint_status->homing``joint_status->homed`,供 task/GUI 读取。
## 4. 急停按钮
### 4.1 AXIS 调用链
入口:
```text
axis.py commands.estop_clicked()
-> s.poll()
-> if s.task_state == STATE_ESTOP:
c.state(STATE_ESTOP_RESET)
else:
c.state(STATE_ESTOP)
```
快捷键绑定:
```text
F1 -> commands.estop_clicked
```
C++ 扩展:
```text
emcmodule.cc state()
-> EMC_TASK_SET_STATE.state = ESTOP 或 ESTOP_RESET
-> emcSendCommand()
```
task
```text
emctaskmain.cc emcTaskPlan()
-> ESTOP/OFF/ESTOP_RESET/ON 各状态中 EMC_TASK_SET_STATE 都属于 immediate command
-> emcTaskIssueCommand()
-> emcTaskSetState()
```
### 4.2 C++ 状态动作
`emcTaskSetState(ESTOP)`
- `emcMotionAbort()` 中止 motion。
- `emcSpindleAbort()` 中止全部 spindle。
- `emcAuxEstopOn()` 置 IO 急停。
- `emcTrajDisable()` 禁用 trajectory/motion。
- `emcTaskAbort()` 清解释器/队列。
- `emcIoAbort(TASK_STATE_ESTOP)` 通知 IO。
- `emcJointUnhome(-2)` 只清 `VOLATILE_HOME` joints。
- `emcAbortCleanup()``emcTaskPlanSynch()` 清理和同步解释器。
`emcTaskSetState(ESTOP_RESET)`
- `emcAuxEstopOff()` 解除 IO 急停。
- 停 coolant、abort task/io/spindle。
- `emcTaskPlanSynch()`
- motion 仍未 enable所以发布状态应落在 `ESTOP_RESET`
### 4.3 先决条件与状态记录
- AXIS 层急停按钮始终可触发。
- ESTOP 是安全覆盖命令,运动中也可以触发。
- 点击后最终状态记录在 `task.state``io.aux.estop``motion.traj.enabled`、spindle/coolant 状态、volatile homed 标志。
- Web 对标时,`ESTOP` 必须清 `taskPaused/motionPaused/singleStepping/motionStepping`,速度归零,最终以 task/HAL status 回写为准。
## 5. 上电/下电按钮
### 5.1 AXIS 调用链
```text
axis.py commands.onoff_clicked()
-> s.poll()
-> if s.task_state == STATE_ESTOP_RESET:
c.state(STATE_ON)
可选 HOMING_PROMPT 后 commands.home_all_joints()
else:
c.state(STATE_OFF)
```
快捷键:
```text
F2 -> commands.onoff_clicked
```
后续仍走:
```text
emcmodule.cc state()
-> EMC_TASK_SET_STATE
-> emctaskmain.cc emcTaskIssueCommand()
-> emctask.cc emcTaskSetState()
```
### 5.2 C++ 状态动作
`emcTaskSetState(ON)`
- `emcTrajEnable()` 下发 motion enable。
- `emcCoolantFloodOff()`
- motion 侧 `EMCMOT_ENABLE` 要求 HAL `motion.enable` 输入为 true否则报错。
- 真正 `task.state=ON` 要等 motion enabled 后由 task update 推导。
`emcTaskSetState(OFF)`
- abort motion/spindle/io/task。
- `emcTrajDisable()`
- `emcJointUnhome(-2)` 清 volatile home。
- `emcTaskPlanSynch()`
### 5.3 先决条件与状态记录
- 上电只允许从 `STATE_ESTOP_RESET` 进入 `STATE_ON`
- 当前为 `STATE_ON` 时同一按钮执行下电。
- 当前为 `STATE_ESTOP` 时 AXIS UI 通常禁用上电;如果误触发 Python 分支也会发送 OFF而不是 ON。
- 状态记录必须包含 `task.state``motion.traj.enabled``motion.motion-enabled` HAL 输出、spindle/coolant abort 后状态。
Web 对标:
- `taskState=estop` 时 Power 应禁止,提示先解除急停。
- `taskState=estop-reset` 时 Power 发 `EMC_TASK_SET_STATE ON`
- `taskState=on` 时 Power 发 `EMC_TASK_SET_STATE OFF` 并清运行/暂停/单步。
## 6. Home 按钮
### 6.1 AXIS 调用链
Home All
```text
axis.py home_all_joints()
-> if not manual_ok(): return
-> ensure_mode(MODE_MANUAL)
-> all_homed() 判断是否二次确认
-> go_home(-1)
```
单 joint
```text
axis.py home_joint()
-> if not manual_ok(): return
-> 由 UI 选择解析 joint 编号
-> 非 identity kinematics 下坐标轴 Home 被拒绝,要求 joint mode
-> ensure_mode(MODE_MANUAL)
-> go_home(jnum)
```
`go_home()`
```text
s.poll()
if 任一 s.joint[j]["homing"] 为真: 拒绝
set_motion_teleop(0)
c.home(num)
c.wait_complete()
```
C++
```text
emcmodule.cc home()
-> EMC_JOINT_HOME.joint = -1 或 n
-> emctaskmain.cc emcTaskIssueCommand()
-> taskintf.cc emcJointHome()
-> EMCMOT_JOINT_HOME
-> motion/command.c
-> homing.c do_home_joint()/do_homing()
```
### 6.2 motion 硬条件
`motion/command.c` 处理 `EMCMOT_JOINT_HOME` 时要求:
- `motion_state == EMCMOT_MOTION_FREE`,否则报 “must be in joint mode to home”。
- `motion.homing-inhibit` 为 false。
- `get_homing_is_active()` 为 false即不能在 homing 未完成时再次 Home。
- `GET_MOTION_ENABLE_FLAG()` 为 true未 enable 时不会启动 homing。
- `joint=-1` 表示 Home All。
### 6.3 状态记录
`homing.c` 中:
- `do_home_joint(jno)` 把目标 joint 的 `home_state` 置为 `HOME_START`;负序列时会把同组 joint 一起置为 `HOME_START`
- `HOME_START` 设置 `H[jno].homing=1``H[jno].homed=0`
- `HOME_FINISHED` 设置 `H[jno].homing=0``H[jno].homed=1``home_state=HOME_IDLE`
- `base_do_homing()` 每 servo 周期推进状态机,并维护 `homing_active`
Web 对标:
- Home 前必须 `taskState=on``mode=manual``interpState=idle`、非 homing。
- 需要记录 `machine.homing``machine.homed[]``machine.allHomed`
- 回零期间禁止 Jog、Run、Step、再次 Home。
- Home All 与单 Home 必须使用 `EMC_JOINT_HOME joint=-1|n` 的语义。
## 7. 执行 Run 按钮
### 7.1 AXIS 调用链
```text
axis.py task_run()
-> run_warn()/reload_file()
-> program_start_line_last = program_start_line
-> ensure_mode(MODE_AUTO)
-> c.auto(AUTO_RUN, program_start_line)
-> program_start_line = 0
```
C++ 扩展:
```text
emcmodule.cc emcauto()
-> AUTO_RUN 带 line 参数
-> EMC_TASK_PLAN_RUN.line = line
-> emcSendCommand()
```
task
```text
emctaskmain.cc emcTaskPlan()
-> ON + AUTO + IDLE 时允许 EMC_TASK_PLAN_RUN
-> emcTaskIssueCommand()
-> all_homed() && !no_force_homing 检查
-> emcTaskPlanOpen()
-> programStartLine = run_msg->line
-> task.interpState = READING
-> task.task_paused = 0
```
### 7.2 C++ 硬条件与状态
硬条件:
- `task.state == ON`
- `task.mode == AUTO`
- 新 Run 正常要求 `interpState == IDLE`
- `all_homed()==true` 或 INI `NO_FORCE_HOMING=true`
- 程序文件已打开,或 `task.file` 可重新打开。
状态记录:
- `motion.traj.single_stepping = 0`
- `stepping = 0``steppingWait = 0`
- `programStartLine = run_msg->line`
- `task.interpState = READING`
- `task.task_paused = 0`
- 执行期间 `emcTaskExecute()``interp_list` 取命令,更新 `task.currentLine`,并通过 `emcTrajSetMotionId(currentLine)` 把当前行号传给 motion。
- `execState` 通过 pre/post conditions 在 `DONE``WAITING_FOR_MOTION``WAITING_FOR_IO``WAITING_FOR_MOTION_AND_IO` 等之间切换。
Web 对标:
- Run gate 必须以 `taskState/mode/interpState/allHomed/noForceHoming/programOpen/taskHalRuntimeReady` 为准。
- Run 后记录 `interpState=reading``taskPaused=false``singleStepping=false``motionStepping=false`
- active line 应尽量使用 task/HAL status 的 line/motion id而不是只靠前端 sample index。
## 8. 暂停与恢复按钮
### 8.1 AXIS 调用链
菜单 Pause
```text
axis.py task_pause()
-> 要求 task_mode == MODE_AUTO
-> 要求 interp_state in {INTERP_READING, INTERP_WAITING}
-> ensure_mode(MODE_AUTO)
-> c.auto(AUTO_PAUSE)
```
工具栏 Pause/Resume
```text
axis.py task_pauseresume()
-> 要求 task_mode in {MODE_AUTO, MODE_MDI}
-> if s.paused:
如果 resume_inhibit 为真则拒绝
c.auto(AUTO_RESUME)
elif s.interp_state != INTERP_IDLE:
c.auto(AUTO_PAUSE)
```
C++ 扩展:
```text
AUTO_PAUSE -> EMC_TASK_PLAN_PAUSE
AUTO_RESUME -> EMC_TASK_PLAN_RESUME
```
### 8.2 task/motion 状态动作
`EMC_TASK_PLAN_PAUSE`
- `emcTrajPause()` 下发 `EMCMOT_PAUSE`
- 若当前不是 `PAUSED`,保存 `interpResumeState = 当前 interpState`
- `task.interpState = PAUSED`
- `task.task_paused = 1`
motion `EMCMOT_PAUSE`
- `tpPause(&coord_tp)`
- `motion.paused = 1`
`EMC_TASK_PLAN_RESUME`
- `emcTrajResume()` 下发 `EMCMOT_RESUME`
- `task.interpState = interpResumeState`
- `task.task_paused = 0`
- `motion.traj.single_stepping = 0`
- `stepping = 0``steppingWait = 0`
motion `EMCMOT_RESUME`
- `motion.stepping = 0`
- `tpResume(&coord_tp)`
- `motion.paused = 0`
### 8.3 先决条件与状态记录
- 菜单 Pause 比工具栏 Pause/Resume 严格,只允许 `AUTO + READING/WAITING`
- 工具栏 Pause/Resume 允许 AUTO/MDIpaused 时走 Resume非 idle 时走 Pause。
- Resume 需要 `s.paused``resume_inhibit=false`
- 必须同时记录 task 层 `task_paused/interpState/interpResumeState` 和 motion 层 `paused`
Web 对标:
- `taskPaused``motionPaused` 都应入状态与 evidence。
- 暂停期间位置、速度、activeLine、tool pose 必须冻结。
- Resume 不应固定写 `reading`,应恢复 `interpResumeState`
## 9. 单步 Step 按钮
### 9.1 AXIS 调用链
```text
axis.py task_step()
-> 如果不是 AUTO 或 interp_state != IDLE先 run_warn()
-> ensure_mode(MODE_AUTO)
-> c.auto(AUTO_STEP)
-> emcmodule.cc emcauto()
-> EMC_TASK_PLAN_STEP
-> emctaskmain.cc emcTaskPlan()
```
### 9.2 task 分状态处理
`AUTO + IDLE`
- task 把第一次 Step 视为从头 Run构造 `taskPlanRunCmd.line = 0`
- `emcTaskIssueCommand(&taskPlanRunCmd)`
- 随即 `emcTrajPause()`
- 保存 `interpResumeState`
- `task.interpState = PAUSED`
- `task.task_paused = 1`
`AUTO + READING/WAITING`
- `motion.traj.single_stepping = 1`
- `stepping = 1`
- `steppingWait = 0`
`AUTO + PAUSED`
- 同样设置 single stepping。
- 如果 `motion.traj.paused && motion.traj.queue > 0`,调用 `emcTrajStep()`
- 否则把 `task.interpState` 恢复为 `interpResumeState` 以继续读下一步。
- `task.task_paused = 1`
### 9.3 motion 单步机制
`EMCMOT_STEP`
- 只有 `motion.paused` 时才执行。
- 保存 `idForStep = 当前 motion id`
- `motion.stepping = 1`
- `tpResume()` 暂时放行。
- `motion.paused` 保持为 1 的语义状态。
`control.c` 周期检查:
```text
if motion.stepping && idForStep != current motion id:
tpPause()
motion.stepping = 0
motion.paused = 1
```
也就是说 LinuxCNC 的 Step 是“放行到下一个 motion id/line 后再次暂停”,不是前端固定推进一个采样点。
Web 对标:
- 需要同时记录 `machine.singleStepping``machine.motionStepping`
- IDLE Step 应等价于启动 run 后暂停。
- PAUSED Step 应按 motion id/line 推进一段后再次 pause。
- Resume/Abort/ESTOP/OFF 必须清 `singleStepping``motionStepping`
## 10. 每个按钮的先决条件汇总
| 按钮 | AXIS/Python 先决条件 | C++/motion 硬条件 | 执行后状态记录 |
|---|---|---|---|
| 急停 | 始终可触发 | `EMC_TASK_SET_STATE` immediate安全覆盖 | `task.state=ESTOP`、motion disabled、spindle/coolant abort、pause/step 清理、volatile home 可清 |
| 解除急停 | 当前 `STATE_ESTOP` 时 F1 分支 | IO estop offmotion 仍 disabled | `task.state=ESTOP_RESET``interpState=IDLE` |
| 上电 | 当前 `STATE_ESTOP_RESET` | motion enable 输入必须 true | `task.state=ON``motion.enabled=true` |
| 下电 | 当前非 `STATE_ESTOP_RESET` 的 on/off 分支 | abort + disable | `task.state=ESTOP_RESET/OFF语义`、运行/暂停/step 清理 |
| Home All | `manual_ok()`,切 `MODE_MANUAL`,非 homing | free/joint mode、motion enabled、homing-inhibit false、无 homing active | `homing=true -> false``homed[]=true``allHomed=true` |
| Home Joint | 同 Home All且 joint/axis 选择合法 | 同上 | 对应 joint 的 `homed[n]` 更新 |
| Run | `ensure_mode(AUTO)`,通常由 UI 限制 ON/IDLE | ON、AUTO、IDLE、已 homed 或 no-force、程序打开 | `interpState=READING``taskPaused=false``singleStepping=false`、line/motion id 更新 |
| Pause | 菜单要求 AUTO + READING/WAITING | `EMC_TASK_PLAN_PAUSE` allowed in running states | `interpState=PAUSED``interpResumeState=READING/WAITING``taskPaused=true``motion.paused=true` |
| Pause/Resume | 工具栏要求 AUTO/MDIpaused 走 Resume非 idle 走 Pause | resume 走 `EMCMOT_RESUME` | Resume 后 `interpState=interpResumeState`、pause/step 清理 |
| Step | 切 AUTO非 idle 时先 run_warn | AUTO 分状态motion step 只在 paused 时放行 | IDLE step 启动后暂停paused step 放行到下个 motion id 后再暂停 |
## 11. 对 `web-rtcp-5axis-xyzbc-trt-sim-plan` 的对标要求
目标项目应以这些字段作为唯一状态事实源:
```js
machine: {
taskState: "estop" | "estop-reset" | "on",
mode: "manual" | "auto" | "mdi",
interpState: "idle" | "reading" | "paused" | "waiting",
interpResumeState: "idle" | "reading" | "waiting",
taskPaused: boolean,
motionPaused: boolean,
singleStepping: boolean,
motionStepping: boolean,
motionEnabled: boolean,
homing: boolean,
homed: boolean[],
allHomed: boolean,
noForceHoming: boolean,
resumeInhibit: boolean
}
```
当前项目中的 `app/src/state/linuxcnc-task-policy.js` 已经是正确的集中门禁位置;`app/src/state/store.js` 负责实际状态写入;`app/src/runtime/linuxcnc-task-hal-runtime.js` 负责把 Task/HAL/WASM status 标准化。后续不能让 UI 组件分散判断按钮可用性。
必须保持的 LinuxCNC 语义:
- `taskState` 由急停和 motion enable 派生,不能只由 `powerOn` 派生。
- `mode` 切出 AUTO 时,如果解释器非 idle 必须拒绝或先 abort不能静默切换。
- `Run``Step``MDI` 要受 `allHomed/noForceHoming` 控制。
- `Pause` 要区分 task paused 与 motion paused且冻结执行位置。
- `Step` 要向 motion id/line 语义靠拢,而不是永久使用固定 sample 步长。
- `ESTOP/OFF/ABORT/RESUME` 都要清理 pause/step 残留状态。
## 12. 源码索引
- AXIS 按钮与快捷键:`linuxcnc/src/emc/usr_intf/axis/scripts/axis.py`
- Python C++ 扩展:`linuxcnc/src/emc/usr_intf/axis/extensions/emcmodule.cc`
- NML 枚举:`linuxcnc/src/emc/nml_intf/emc.hh`
- NML 命令与 task status`linuxcnc/src/emc/nml_intf/emc_nml.hh`
- task 主状态机:`linuxcnc/src/emc/task/emctaskmain.cc`
- task mode/state 动作:`linuxcnc/src/emc/task/emctask.cc`
- task 到 motion 命令封装:`linuxcnc/src/emc/task/taskintf.cc`
- motion 命令处理:`linuxcnc/src/emc/motion/command.c`
- motion 周期控制和状态输出:`linuxcnc/src/emc/motion/control.c`
- homing 状态机:`linuxcnc/src/emc/motion/homing.c`

View File

@@ -0,0 +1,294 @@
# 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/spindledisable motion清 pause/stepvolatile home 处理 |
| `EMC_TASK_SET_STATE ESTOP_RESET` | IO estop offmachine offabort/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 后 pausepaused 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/mdipaused 走 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` 看起来合理

View File

@@ -0,0 +1,576 @@
# 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++ 映射。

View File

@@ -15,9 +15,15 @@
- `09-设计任务书与技术方案整合.md`:从 DOCX 设计任务书整合出的 Markdown 版本,保留任务书、技术方案、程序逻辑分析、状态联锁和图片引用。
- `11-LinuxCNC源码与真实执行严格对标任务.md`:以 `/home/mes123456/cnc_wams/linuxcnc` 为唯一 native 权威源整理源码、配置、HAL、Vismach、AXIS、PyVCP、解释器、task/HAL、真实运行采样和 Web 复现的新增严格对标任务。
- `12-20260704-真实执行复验与缺失功能工作计划.md`:记录 2026-07-04 18:12 EDT 重新真实执行 native/Web/compare 后的缺失功能分析和后续工作计划。
- `18-20260707-AXIS主控制按钮调用链与Web完善指南.md`:在两份 `项目分析/AXIS...` 文档和 LinuxCNC 源码复核基础上整理急停、上电、Home、Run、Pause/Resume、Step 的 task/motion 调用链、状态机制、Web/WASM 完善清单和验收矩阵。
- `19-20260707-AXIS按钮LinuxCNC-task-motion状态机制源码分析.md`:从 LinuxCNC C++ task/motion/homing 源码角度整理 AXIS 按钮真实调用链、状态记录和 Web 对标字段。
- `20-20260707-AXIS按钮LinuxCNC真实C++对标实施步骤.md`:由 19 号源码分析拆分出的详细实施步骤,要求以 LinuxCNC C++ 真实状态机为准,不以 `compare 60/60 pass` 作为完成依据。
- `21-20260707-AXIS按钮LinuxCNC真实C++对标验收测试.md`:由 19 号源码分析拆分出的验收测试文档,定义功能级硬检查、非法命令矩阵、真实程序执行验收和 `functionalSummary` 通过标准。
当前新增关注点:
- 2026-07-07 18:35 EDT 已将 19 号源码分析拆分为 20 号实施步骤和 21 号验收测试。后续结论必须区分 `surfaceSummary``functionalSummary``compare 60/60 pass` 只代表表面规则通过,真实通过必须证明 Web 行为符合 LinuxCNC C++ `emctaskmain.cc``emctask.cc``taskintf.cc``motion/command.c``control.c``homing.c` 的状态机和真实程序执行结果。
- 2026-07-07 18:18 EDT 已完成 AXIS 主控制按钮状态链路收敛:`ESTOP/RESET/Power/Home/Run/Pause/Resume/Step` 统一以 LinuxCNC `task.state + task.mode + interpState + motion/home status` 为事实来源Web policy、store、Task/HAL WASM、evidence 采集和 smoke/compare 已同步,最新 compare 为 `60/60 pass``blockers=[]``requiredImprovements=[]`
- 2026-07-06 00:22 EDT 新任务:完全对标 `/home/mes123456/cnc_wams/linuxcnc``xyzbc-trt` 的 AXIS/task 状态联锁,重整 Web 仿真系统 `解除 ESTOP``上电``Home All``Run``暂停/继续` 功能逻辑;要求修复当前混乱状态流,并通过源码对照、自动化 smoke、browser 验证和 evidence/compare 严格复验。
- 2026-07-05 18:57 EDT 已按 LinuxCNC AXIS 源码修复 Pause工具栏按钮恢复为 `.toolbar.program_pause -> task_pauseresume` 语义,运行中点击暂停、暂停中点击继续;菜单 Pause/Resume 仍为独立命令。Node smoke、browser smoke、build、native/Web/compare 均通过,最新 compare 为 `60/60 pass`
- 2026-07-05 18:18 EDT 已按 `working/12-20260704-真实执行复验与缺失功能工作计划.md` 的 P-002 规则再次执行全量复验native LinuxCNC、Web evidence、compare、build、Node smoke、browser smoke 均通过;最新 `compare-xyzbc-trt-evidence.json``60/60 pass``blockers=[]``requiredImprovements=[]`

View File

@@ -1,7 +1,7 @@
{
"apiName": "xyzbc-trt-native-web-evidence-comparison",
"status": "pass",
"comparedAt": "2026-07-06T06:35:08.413Z",
"comparedAt": "2026-07-07T22:17:58.519Z",
"nativePath": "/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/native-xyzbc-trt-evidence.json",
"webPath": "/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/web-xyzbc-trt-evidence.json",
"summary": {
@@ -548,7 +548,7 @@
"execution": {
"completed": true,
"sampleCount": 228,
"taskHalEventCount": 28
"taskHalEventCount": 30
},
"semanticBoundary": "web_basic_sim_joint_home_spindle_manualtoolchange_feedback"
},
@@ -559,7 +559,7 @@
"semanticBoundary": "linuxcnc_task_motion_hal_wasm_simulation_runtime",
"sessionProgramPath": "/work/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt/demos/xyzbc_switchkins.ngc",
"completed": true,
"eventCount": 28
"eventCount": 30
},
"taskPolicy": {
"taskState": "estop-reset",
@@ -600,7 +600,7 @@
"execution": {
"completed": true,
"sampleCount": 228,
"taskHalEventCount": 28
"taskHalEventCount": 30
},
"semanticBoundary": "web_basic_sim_joint_home_spindle_manualtoolchange_feedback"
},

View File

@@ -1,7 +1,7 @@
{
"apiName": "xyzbc-trt-web-opfs-wasm-evidence",
"status": "ready-for-wasm-runtime",
"collectedAt": "2026-07-06T06:35:07.906Z",
"collectedAt": "2026-07-07T22:17:57.900Z",
"profile": {
"id": "xyzbc-trt",
"machineName": "sim-xyzbc-trt-kins (switchkins)",
@@ -102296,7 +102296,7 @@
"semanticBoundary": "linuxcnc_task_motion_hal_wasm_simulation_runtime",
"sessionProgramPath": "/work/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt/demos/xyzbc_switchkins.ngc",
"completed": true,
"eventCount": 28
"eventCount": 30
}
},
"semanticExecutionPath": {
@@ -227512,7 +227512,7 @@
"semanticBoundary": "linuxcnc_task_motion_hal_wasm_simulation_runtime",
"sessionProgramPath": "/work/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt/demos/xyzbc_switchkins.ngc",
"completed": true,
"eventCount": 28
"eventCount": 30
},
"taskPolicy": {
"taskState": "estop-reset",
@@ -227553,7 +227553,7 @@
"execution": {
"completed": true,
"sampleCount": 228,
"taskHalEventCount": 28
"taskHalEventCount": 30
},
"semanticBoundary": "web_basic_sim_joint_home_spindle_manualtoolchange_feedback"
},
@@ -227584,7 +227584,7 @@
"execution": {
"completed": true,
"sampleCount": 228,
"taskHalEventCount": 28
"taskHalEventCount": 30
},
"semanticBoundary": "web_basic_sim_joint_home_spindle_manualtoolchange_feedback"
},
@@ -229807,29 +229807,29 @@
"rel": "wasm-port/build/wasm/tp/linuxcnc_tp.js",
"absolutePath": "/home/mes123456/cnc_wams/wasm-port/build/wasm/tp/linuxcnc_tp.js",
"bytes": 10542,
"mtime": "2026-07-02T12:24:39.710Z",
"mtime": "2026-07-07T14:02:52.601Z",
"sha256": "94a7d66466b354094d06f589d4c8bb47c9c7155dce972801470ebae4c3952185"
},
{
"rel": "wasm-port/build/wasm/tp/linuxcnc_tp.wasm",
"absolutePath": "/home/mes123456/cnc_wams/wasm-port/build/wasm/tp/linuxcnc_tp.wasm",
"bytes": 178883,
"mtime": "2026-07-02T12:24:39.510Z",
"sha256": "192edfd1028ee769025a89ed1dfcf32dff5ca4322e2b363f1cf0e6a90e4d21ff"
"bytes": 181159,
"mtime": "2026-07-07T14:02:52.381Z",
"sha256": "059de6c59668d9eef41dc307c3439ecf3793bf23da6f2f7cedadc7f9e1fbbf6b"
},
{
"rel": "wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js",
"absolutePath": "/home/mes123456/cnc_wams/wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js",
"bytes": 19878,
"mtime": "2026-07-02T12:24:43.462Z",
"mtime": "2026-07-07T22:01:17.840Z",
"sha256": "b5f5caa1a898acea8280dfba6dae7b723d775e54e9d769bdb4d21ac6ac2ac358"
},
{
"rel": "wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm",
"absolutePath": "/home/mes123456/cnc_wams/wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm",
"bytes": 206516,
"mtime": "2026-07-02T12:24:43.254Z",
"sha256": "f07802e9264509fdcbdc7e29bac25922b20ce77bdb285770c000fa7d3f5b9996"
"bytes": 215042,
"mtime": "2026-07-07T22:01:17.596Z",
"sha256": "b456ffb64e5a32bc4a425343b5539dea9cf064b1804c4c6b53dea3661ea37fa8"
}
],
"buildCommands": [
@@ -230621,7 +230621,7 @@
"execution": {
"completed": true,
"sampleCount": 228,
"taskHalEventCount": 28
"taskHalEventCount": 30
}
},
"semanticBoundary": "web_task_hal_full_state"
@@ -231666,29 +231666,29 @@
"rel": "wasm-port/build/wasm/tp/linuxcnc_tp.js",
"absolutePath": "/home/mes123456/cnc_wams/wasm-port/build/wasm/tp/linuxcnc_tp.js",
"bytes": 10542,
"mtime": "2026-07-02T12:24:39.710Z",
"mtime": "2026-07-07T14:02:52.601Z",
"sha256": "94a7d66466b354094d06f589d4c8bb47c9c7155dce972801470ebae4c3952185"
},
{
"rel": "wasm-port/build/wasm/tp/linuxcnc_tp.wasm",
"absolutePath": "/home/mes123456/cnc_wams/wasm-port/build/wasm/tp/linuxcnc_tp.wasm",
"bytes": 178883,
"mtime": "2026-07-02T12:24:39.510Z",
"sha256": "192edfd1028ee769025a89ed1dfcf32dff5ca4322e2b363f1cf0e6a90e4d21ff"
"bytes": 181159,
"mtime": "2026-07-07T14:02:52.381Z",
"sha256": "059de6c59668d9eef41dc307c3439ecf3793bf23da6f2f7cedadc7f9e1fbbf6b"
},
{
"rel": "wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js",
"absolutePath": "/home/mes123456/cnc_wams/wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js",
"bytes": 19878,
"mtime": "2026-07-02T12:24:43.462Z",
"mtime": "2026-07-07T22:01:17.840Z",
"sha256": "b5f5caa1a898acea8280dfba6dae7b723d775e54e9d769bdb4d21ac6ac2ac358"
},
{
"rel": "wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm",
"absolutePath": "/home/mes123456/cnc_wams/wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm",
"bytes": 206516,
"mtime": "2026-07-02T12:24:43.254Z",
"sha256": "f07802e9264509fdcbdc7e29bac25922b20ce77bdb285770c000fa7d3f5b9996"
"bytes": 215042,
"mtime": "2026-07-07T22:01:17.596Z",
"sha256": "b456ffb64e5a32bc4a425343b5539dea9cf064b1804c4c6b53dea3661ea37fa8"
}
],
"missing": [],

Some files were not shown because too many files have changed in this diff Show More