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

19 KiB
Raw Blame History

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 estopestop-reseton 的主门禁
task.mode machine.mode manualautomdi 的命令门禁
task.interpState machine.interpState idlereadingpausedwaiting
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_HOME joint 的 homed
  • emcTaskPlanSynch() 同步解释器。

实际 task.stateemctask.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=falsemachine.singleStepping=falsetaskHalPauseLock=nullprogramRuntimeFeedback.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 增加 canPowerTogglepowerAction

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_stateHOME_IDLEHOME_STARTHOME_SEARCH_*HOME_FINISHEDHOME_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 调用链

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 = 0steppingWait = 0
  • programStartLine = run_msg->line

解释器完成、abort、错误或下级状态变为非 ON 时,再回到 IDLE 并清执行状态。

7.3 Web 完善要求

当前 Web 已有 RUN_FROM_OPERATORRUN、Task/HAL status loop、50ms 截图验证。后续要继续收敛:

  • RUN 不应直接由 runState 决定,应以 taskState/mode/interpState/allHomed/programOpen 为准;
  • RUN_FROM_OPERATOR 可以做引导序列,但最终应仍发 SET_MODE AUTOEMC_TASK_PLAN_RUN
  • programStartLine 应统一为 LinuxCNC 行号语义,避免 UI 1-based 与 task line=0 混用;
  • Task/HAL runtime 可用时,应从 taskHalStatus.ui.activeLinemotion.program-line 回写 activeLine
  • 结束时必须经过 interpState=idlerunState=complete/idletaskPaused=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 完善要求

当前 PAUSEPAUSE_RESUMERESUME 已基本对标。后续补齐点:

  • s.paused 对应 motion paused不只是 interpState=="paused"
  • taskPausedmotionPaused 必须一起出现在状态面板、evidence 和测试断言;
  • 暂停期间 programExecutionSampleIndexaxisPosetoolAxisVector、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=PAUSEDtask_paused=1
  • AUTO + READING/WAITING:设置 motion.traj.single_stepping=1stepping=1steppingWait=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.singleSteppingmachine.motionStepping
  • 第一次 Step 在 IDLE 时等价于启动 Auto Run 后暂停;
  • 已暂停且 motion queue 有待执行段时Step 只释放到下一个 motion id/line而不是简单固定一个 sample
  • Step 后状态应为 interpState=pausedtaskPaused=truemotionPaused=truesingleStepping=true/随后 false
  • Resume 必须清 singleStepping

10. WASM-port 完善方向

wasm-port/SKILL.md 的原则是CNC 语义来自 LinuxCNC vendored sourceJS/SDK 只做适配。针对本按钮状态机,建议按以下顺序补齐:

  1. wasm-port 的 task/HAL SDK 中暴露完整 task 状态:
    • statemodeinterpStateinterpResumeStatetaskPausedsingleSteppingprogramOpenprogramStartLine
  2. 暴露 motion 状态:
    • enabledpausedsteppingidForStepqueueDepthprogramLinehominghomed[]allHomed
  3. EMC_TASK_SET_STATEEMC_TASK_SET_MODEEMC_JOINT_HOMEEMC_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 唯一按钮门禁;补 canPowerTogglecanStepStrictresumeInhibitsingleStepping
app/src/state/store.js 按 EMC 命令语义更新状态;清理只依赖 runState 的残留判断
app/src/runtime/linuxcnc-task-hal-runtime.js 标准化 Task/HAL statusenabledhomed[]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=onmotionEnabled=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=readingtaskPaused=false
AUTO/READING 菜单 Pause interpState=pausedtaskPaused=truemotionPaused=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.jsderiveTaskState()singleSteppinghomed[]、急停/下电清理。
  3. 补 Task/HAL status normalization使 WASM status 能覆盖 JS 预期状态。
  4. 扩展 Node 测试覆盖非法状态矩阵。
  5. 扩展 50ms Playwright 工具,加入 Step 和 Home 过程截图/evidence。
  6. 更新 collect-webcompare,把按钮状态流纳入 60/60 之外的硬检查。

13. 当前判断

现有 Web 项目已经具备主干:linuxcnc-task-policy.jsstore.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 已作为主控制按钮唯一门禁入口,新增 motionEnabledhomed[]singleSteppingmotionSteppingresumeInhibitcanPowerTogglepowerAction 等状态输出。
  • store.js 已把 ESTOP/RESET/Power/Home/Run/Pause/Resume/Step 收敛到 taskState + mode + interpState + motion/home status,并清理下电、急停、暂停、单步、回零的状态残留。
  • Task/HAL WASM 已输出 motionEnabledhomed[],并在 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 基线之上追加新任务,而不是重新发散按钮状态来源。