19 KiB
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 按钮不是直接修改界面状态,而是走统一控制链:
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 调用链
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_HOMEjoint 的 homed;emcTaskPlanSynch()同步解释器。
实际 task.state 由 emctask.cc:determineState() 周期推导:
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 调用链
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:
taskState == estop -> disabled
taskState == estop-reset -> action ON
taskState == on -> action OFF
6. Home
6.1 LinuxCNC 调用链
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-jointhoming短暂状态,即使仿真为零速回零,也应经过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 调用链
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 与 taskline=0混用;- Task/HAL runtime 可用时,应从
taskHalStatus.ui.activeLine和motion.program-line回写activeLine; - 结束时必须经过
interpState=idle、runState=complete/idle、taskPaused=false。
8. 暂停/恢复
8.1 LinuxCNC 调用链
菜单 Pause:
axis.py task_pause()
-> 必须 MODE_AUTO 且 interp_state in {READING, WAITING}
-> c.auto(AUTO_PAUSE)
-> EMC_TASK_PLAN_PAUSE
工具栏 Pause/Resume:
axis.py task_pauseresume()
-> 必须 MODE_AUTO 或 MODE_MDI
-> s.paused 为真: c.auto(AUTO_RESUME)
-> 否则 interp_state != IDLE: c.auto(AUTO_PAUSE)
task 执行:
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 侧:
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 调用链
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 侧:
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 source,JS/SDK 只做适配。针对本按钮状态机,建议按以下顺序补齐:
- 在
wasm-port的 task/HAL SDK 中暴露完整 task 状态:state、mode、interpState、interpResumeState、taskPaused、singleStepping、programOpen、programStartLine。
- 暴露 motion 状态:
enabled、paused、stepping、idForStep、queueDepth、programLine、homing、homed[]、allHomed。
- 将
EMC_TASK_SET_STATE、EMC_TASK_SET_MODE、EMC_JOINT_HOME、EMC_TASK_PLAN_RUN/PAUSE/RESUME/STEP作为 JSON command 的稳定 ABI。 - 对照 LinuxCNC
emcTaskSetState()和emcTaskPlan()增加任务状态矩阵测试。 - 对照
motion/command.c增加 pause/resume/step motion id 测试。 - 对照
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 建议新增状态对象
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/step,velocity=0,状态 ESTOP |
12. 推荐实施顺序
- 先补
linuxcnc-task-policy.js的严格状态矩阵,不改 UI。 - 补
store.js的deriveTaskState()、singleStepping、homed[]、急停/下电清理。 - 补 Task/HAL status normalization,使 WASM status 能覆盖 JS 预期状态。
- 扩展 Node 测试覆盖非法状态矩阵。
- 扩展 50ms Playwright 工具,加入 Step 和 Home 过程截图/evidence。
- 更新
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。
本轮验证结果:
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 基线之上追加新任务,而不是重新发散按钮状态来源。