561 lines
19 KiB
Markdown
561 lines
19 KiB
Markdown
# 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/MDI,paused 时走 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 off,motion 仍 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/MDI;paused 走 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`
|
||
|