Files
cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/18-20260707-AXIS主控制按钮调用链与Web完善指南.md
2026-07-07 18:45:26 -04:00

480 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 基线之上追加新任务,而不是重新发散按钮状态来源。