571 lines
15 KiB
Markdown
571 lines
15 KiB
Markdown
# 01 RUN 与 G-code 执行原理测试记录
|
||
|
||
生成时间:2026-06-22
|
||
|
||
## 1. 参考接续文件
|
||
|
||
本记录按 `textbak` 中的接续要求核对,不把 UI fixture、自增行号、JS-owned CNC 语义当作真实运行证明。
|
||
|
||
关键约束来自:
|
||
|
||
```text
|
||
textbak/text2.txt
|
||
- 数控/G-code/remap/tool/parameter/planner/kinematics/user-M 语义必须来自 LinuxCNC upstream 或 vendored LinuxCNC C/C++ source。
|
||
- UI 已优先解析 run_step,fallback 到 canonical event。
|
||
- 不引入 JS G-code parser 或 JS remap/tool/parameter semantics。
|
||
|
||
textbak/text40.txt
|
||
- LinuxCNC 可加载 kinematics module 的独立 WASM ABI 已完成。
|
||
- 下一步应转向 Web/M4/M5 simulation UI 接入 createLinuxCncKinematicsSdk() 输出,或推进 remap/planner/browser 集成验证。
|
||
|
||
textbak/text41.txt
|
||
- Web/M4/M5 当前状态曾明确记录为 RUN/STEP/JOG/HOME 仍推进 fixture axis pose。
|
||
- 目标是 RUN/STEP/JOG/HOME 消费 LinuxCNC kinematics frame contract。
|
||
- program execution 如果仍是 fixture line playback,必须继续标记为 fixture/interpreter-not-connected。
|
||
- remap/planner/browser full-process 不得因 kinematics 接入而自动 promotion。
|
||
```
|
||
|
||
当前源码已经比 `text41.txt` 的早期基线多接入了 interpreter、kinematics、task/HAL runtime,但 `RUN` 的连续执行模型仍需要按 task/HAL feedback 口径复核。
|
||
|
||
## 2. 当前 G-code 加载过程
|
||
|
||
启动入口:
|
||
|
||
```text
|
||
app/src/main.js:17-23
|
||
```
|
||
|
||
页面启动时创建 store、挂载 gmoccapy UI,并异步附加:
|
||
|
||
```text
|
||
1. LinuxCNC INI config
|
||
2. LinuxCNC kinematics runtime
|
||
3. LinuxCNC interpreter runtime
|
||
4. LinuxCNC task/HAL runtime
|
||
5. machine file staging
|
||
```
|
||
|
||
默认程序加载:
|
||
|
||
```text
|
||
app/src/main.js:103-124
|
||
```
|
||
|
||
`ensureDefaultLinuxCncProgramPreview()` 在 interpreter 与 machine files staged 后选择默认 LinuxCNC 5-axis source,然后 dispatch:
|
||
|
||
```text
|
||
LOAD_LINUXCNC_GCODE_SOURCE
|
||
```
|
||
|
||
选择 vendored G-code 后,store 的处理链是:
|
||
|
||
```text
|
||
app/src/state/store.js:744-792
|
||
```
|
||
|
||
实际步骤:
|
||
|
||
```text
|
||
1. 从 machineFileStaging.save.files 中找到 sourceRel 对应的 G-code 文本。
|
||
2. selectMachineFileProgram(...) 生成该 G-code 对应的 machine-file plan。
|
||
3. buildLoadedProgram(...) 将文本拆成 programLines,设置 activeProgram、programStartLine=1、activeLine=1。
|
||
4. 设置 programSource=linuxcnc-vendored-5axis-gcode。
|
||
5. 如果 task/HAL runtime 已加载,initializeTaskHalSession({ openProgram: true })。
|
||
6. 如果 interpreter runtime 已加载,dispatch RUN_INTERPRETER_PROGRAM。
|
||
```
|
||
|
||
本地 operator 打开文件时走同一个 `buildLoadedProgram(...)`:
|
||
|
||
```text
|
||
app/src/state/store.js:1033-1057
|
||
```
|
||
|
||
G-code 文本显示在 UI:
|
||
|
||
```text
|
||
app/src/ui/gmoccapy-shell.js:193-228
|
||
```
|
||
|
||
这里使用 `state.programLines` 逐行渲染,`state.activeLine` 对应的行加 `.active`,同时显示:
|
||
|
||
```text
|
||
Current line ${state.activeLine}
|
||
${state.activeLine} / ${programEndLine}
|
||
```
|
||
|
||
所以“能否看到 G-code 程序”和“是否逐行显示当前行”取决于 `programLines` 与 `activeLine` 是否被同一执行反馈链正确更新。
|
||
|
||
## 3. interpreter 解释与 preview 过程
|
||
|
||
`RUN_INTERPRETER_PROGRAM` 不是机床运行按钮,它是在加载程序后调用 LinuxCNC interpreter runtime,生成 canonical motion / summary / timing 基础数据:
|
||
|
||
```text
|
||
app/src/state/store.js:468-508
|
||
```
|
||
|
||
解释完成后:
|
||
|
||
```text
|
||
app/src/state/store.js:509-555
|
||
```
|
||
|
||
当前实现会:
|
||
|
||
```text
|
||
1. 保存 programExecution。
|
||
2. buildTimingForState(...) 构建 programExecutionTiming。
|
||
3. 设置 programExecutionSourceMode=execution.sourceMode。
|
||
4. 设置 programExecutionMotionIndex=0、programExecutionSampleIndex=0。
|
||
5. 根据 firstMotion 更新 activeLine、axisPose、kinsType、rtcpState。
|
||
6. 创建 firstFeedback,并更新 feed.currentVelocity。
|
||
```
|
||
|
||
需要注意:这里的 `axisPoseFromCanonicalMotion(firstMotion, ...)` 来自 canonical motion 事件,是预览/初始反馈,不等价于运行态 task/HAL 连续反馈。
|
||
|
||
## 4. timing 与速度来源
|
||
|
||
`buildTimingForState(...)` 的优先级:
|
||
|
||
```text
|
||
app/src/state/store.js:2369-2379
|
||
```
|
||
|
||
如果 `execution.plannerTiming.plannerRuntimeReady === true`,使用 plannerTiming;否则调用:
|
||
|
||
```text
|
||
buildProgramExecutionTiming(...)
|
||
```
|
||
|
||
该 timing fallback 的语义边界明确是估算:
|
||
|
||
```text
|
||
app/src/runtime/execution-timing.js:50-53
|
||
semanticBoundary: linuxcnc_canonical_motion_timing_estimate_not_planner_queue
|
||
sourceBasis: LinuxCNC canonical motion events plus INI/profile velocity limits and feed overrides
|
||
```
|
||
|
||
fallback timing 的速度计算逻辑:
|
||
|
||
```text
|
||
app/src/runtime/execution-timing.js:75-123
|
||
```
|
||
|
||
它按 canonical motion 段计算:
|
||
|
||
```text
|
||
1. rapid 使用 maxLinearVelocity * rapidOverride。
|
||
2. feed 使用 feedRate * feedOverride。
|
||
3. linearSeconds = linearDistance / linearVelocity。
|
||
4. angularSeconds = angularDistance / angularVelocity。
|
||
5. durationSeconds = max(linearSeconds, angularSeconds)。
|
||
```
|
||
|
||
这不是 LinuxCNC task/motion planner queue 的真实 run_step 推进。除非 `plannerTiming` 已 ready,否则“实际时间”和“实际进给速度”只能说是 canonical-based estimate。
|
||
|
||
## 5. RUN 按钮当前执行原理
|
||
|
||
`RUN` action 入口:
|
||
|
||
```text
|
||
app/src/state/store.js:1059-1100
|
||
```
|
||
|
||
分两条路径。
|
||
|
||
### 5.1 task/HAL runtime loaded 路径
|
||
|
||
如果 `state.taskHalRuntime?.loaded` 为 true,`RUN` 执行:
|
||
|
||
```text
|
||
app/src/state/store.js:1066-1072
|
||
```
|
||
|
||
发送命令:
|
||
|
||
```text
|
||
EMC_TASK_SET_STATE ON
|
||
EMC_TASK_SET_MODE AUTO
|
||
EMC_TASK_PLAN_RUN line = activeLine - 1
|
||
```
|
||
|
||
然后调用:
|
||
|
||
```text
|
||
runTaskHalCommandSequence(..., { taskCycles: 5 })
|
||
```
|
||
|
||
`runTaskHalCommandSequence` 的实现:
|
||
|
||
```text
|
||
app/src/state/store.js:1572-1604
|
||
```
|
||
|
||
当前真实顺序是:
|
||
|
||
```text
|
||
1. 标记 taskHalExecutionPending=true。
|
||
2. 如未初始化 task/HAL session,则 initializeTaskHalSession/openProgram。
|
||
3. 逐个 sendCommand(command)。
|
||
4. 调用 taskHalRuntime.runCycles({ taskCycles: 5 })。
|
||
5. 调用 taskHalRuntime.readStatus()。
|
||
6. dispatch TASK_HAL_STATUS_APPLIED。
|
||
```
|
||
|
||
结论:
|
||
|
||
```text
|
||
当前 RUN 是“发送 task/HAL 命令 -> 主动推进固定 cycles -> 拉取一次 status -> 应用到 UI”。
|
||
源码中没有看到 task/HAL worker 在程序运行期间主动持续 postMessage status 的订阅/推送通道。
|
||
```
|
||
|
||
### 5.2 task/HAL runtime missing fallback
|
||
|
||
如果 task/HAL runtime 未加载,`RUN` 走:
|
||
|
||
```text
|
||
app/src/state/store.js:1074-1098
|
||
```
|
||
|
||
即:
|
||
|
||
```text
|
||
nextProgramRuntimeSamplePlayback(state, 5)
|
||
```
|
||
|
||
这个 fallback 按 sampleIndex 或 motionIndex 一次跳 5 步,更新 activeLine / axisPose / feed / runtimeFeedback。
|
||
|
||
语义边界在 feedback 中标记为:
|
||
|
||
```text
|
||
linuxcnc-tp-runtime-sample
|
||
linuxcnc_tp_run_cycle_feedback_without_hardware
|
||
```
|
||
|
||
或在没有 sample 时退回:
|
||
|
||
```text
|
||
linuxcnc-canonical-motion
|
||
linuxcnc_canonical_motion_feedback_without_tp_sample
|
||
```
|
||
|
||
这条路径不能当作真实 task/HAL 运行过程。
|
||
|
||
## 6. task/HAL feedback 如何映射到 UI 状态
|
||
|
||
task/HAL runtime 包装:
|
||
|
||
```text
|
||
app/src/runtime/linuxcnc-task-hal-runtime.js:96-114
|
||
```
|
||
|
||
暴露的是:
|
||
|
||
```text
|
||
sendCommand(command)
|
||
runCycles(options)
|
||
readStatus()
|
||
```
|
||
|
||
worker client:
|
||
|
||
```text
|
||
app/src/runtime/linuxcnc-task-hal-worker-client.js:14-28
|
||
```
|
||
|
||
worker 本体:
|
||
|
||
```text
|
||
app/src/runtime/linuxcnc-task-hal-worker.js:5-17
|
||
app/src/runtime/linuxcnc-task-hal-worker.js:19-53
|
||
```
|
||
|
||
worker 当前是 request/response 模型:
|
||
|
||
```text
|
||
主线程 postMessage({ id, type, payload })
|
||
worker 执行 handleMessage(type, payload)
|
||
worker postMessage({ id, ok, result })
|
||
```
|
||
|
||
没有看到类似:
|
||
|
||
```text
|
||
subscribeStatus
|
||
onStatus
|
||
while running post feedback
|
||
```
|
||
|
||
因此,当前 task/HAL feedback 不是“运行时持续推送”,而是 UI/store 调用 `readStatus()` 得到的一次快照。
|
||
|
||
状态归一化:
|
||
|
||
```text
|
||
app/src/runtime/linuxcnc-task-hal-runtime.js:155-199
|
||
```
|
||
|
||
`normalizeTaskHalStatus()` 从 motion/HAL pins 生成:
|
||
|
||
```text
|
||
ui.taskState
|
||
ui.taskMode
|
||
ui.interpState
|
||
ui.taskCycle
|
||
ui.servoCycle
|
||
ui.motionQueueDepth
|
||
ui.activeLine
|
||
ui.switchkinsType
|
||
ui.axisPose
|
||
ui.currentVelocity
|
||
```
|
||
|
||
应用到 store:
|
||
|
||
```text
|
||
app/src/state/store.js:1776-1835
|
||
```
|
||
|
||
`applyTaskHalStatusPatch(...)` 将 status 映射为:
|
||
|
||
```text
|
||
activeLine = programStartLine + ui.activeLine - 1
|
||
axisPose = resolveTaskHalAxisPose(...)
|
||
kinsType = resolveTaskHalKinsType(...)
|
||
rtcpState = rtcpStateFromKinsType(kinsType)
|
||
runState = 根据 interpState / paused / aborted / complete 推导
|
||
feed.currentVelocity = ui.currentVelocity
|
||
programRuntimeFeedback = createTaskHalRuntimeFeedback(...)
|
||
```
|
||
|
||
runtime feedback 字段:
|
||
|
||
```text
|
||
app/src/state/store.js:1925-1948
|
||
```
|
||
|
||
包括:
|
||
|
||
```text
|
||
sourceMode=linuxcnc-task-motion-hal-wasm
|
||
sampleIndex=ui.servoCycle
|
||
motionIndex=ui.activeLine - 1
|
||
line=activeLine
|
||
timeSeconds=ui.taskCycle * 0.01
|
||
axisPose
|
||
currentVelocityMmPerMin
|
||
requestedVelocityMmPerMin
|
||
distanceToGo
|
||
queueDepth
|
||
cycle
|
||
taskCycle
|
||
```
|
||
|
||
这里的问题是:如果 `readStatus()` 不持续发生,这些字段不会按真实执行时间继续变化。
|
||
|
||
## 7. 本地快照观察
|
||
|
||
说明:本轮浏览器采集脚本在用户中断前已写出完整 JSON 快照,作为辅助证据。正式结论仍以源码追踪为主。
|
||
|
||
辅助证据文件:
|
||
|
||
```text
|
||
web-rtcp-5axis-sim-plan/working_run/run-feedback-evidence/run-feedback-local.json
|
||
web-rtcp-5axis-sim-plan/working_run/run-feedback-evidence/run-feedback-local.png
|
||
```
|
||
|
||
快照摘要:
|
||
|
||
| 快照 | runState | activeLine | servoCycle | velocity | axisPose |
|
||
| --- | --- | ---: | ---: | ---: | --- |
|
||
| loaded-default | idle | 1 | 0 | 0 | X0 Y0 Z0 A0 B0 C0 |
|
||
| after-power | idle | 1 | 10 | 0 | X0 Y0 Z0 A0 B0 C0 |
|
||
| after-home | idle | 1 | 10 | 0 | X43 Y-32.15 Z-11.306 A0 B0 C0 |
|
||
| after-auto | idle | 1 | 20 | 0 | X0 Y0 Z0 A0 B0 C0 |
|
||
| after-run-idle | running | 5 | 70 | 3600 | X0 Y0 Z5 A0 B0 C0 |
|
||
| run-plus-0.5s-no-command | running | 5 | 70 | 3600 | X0 Y0 Z5 A0 B0 C0 |
|
||
| run-plus-2.0s-no-command | running | 5 | 70 | 3600 | X0 Y0 Z5 A0 B0 C0 |
|
||
| run-plus-5.0s-no-command | running | 5 | 70 | 3600 | X0 Y0 Z5 A0 B0 C0 |
|
||
|
||
观察结论:
|
||
|
||
```text
|
||
1. 点击 RUN 后,状态从 line 1 推进到 line 5,servoCycle 从 20 推进到 70。
|
||
2. 后续 0.5s / 2.0s / 5.0s 没有再次调用命令时,activeLine、servoCycle、axisPose、velocity 均保持不变。
|
||
3. 这与源码中的 runCycles({ taskCycles: 5 }) + readStatus() 一次相符。
|
||
4. 这不是连续 task/HAL feedback 推送模型。
|
||
```
|
||
|
||
## 8. 发现的问题
|
||
|
||
### P1. RUN 没有持续消费 task/HAL feedback
|
||
|
||
期望:
|
||
|
||
```text
|
||
G-code 程序执行时,task/HAL/motion runtime 持续产生状态;
|
||
UI/store 持续消费这些状态;
|
||
当前行、轴值、速度、DTG、程序时间来自同一个 feedback source。
|
||
```
|
||
|
||
当前:
|
||
|
||
```text
|
||
RUN 只主动推进固定 taskCycles=5,然后 readStatus 一次。
|
||
没有看到 worker 主动推送 status。
|
||
没有看到 store 中存在运行期间持续 readStatus/readEvents 的执行循环。
|
||
```
|
||
|
||
影响:
|
||
|
||
```text
|
||
RUN 后页面可能停在某个 status snapshot。
|
||
用户看到 runState=running,但 activeLine、轴值、速度不会随真实时间继续推进。
|
||
```
|
||
|
||
### P1. 轴值存在状态回退/覆盖风险
|
||
|
||
快照中可见:
|
||
|
||
```text
|
||
after-home: X43 Y-32.15 Z-11.306
|
||
after-auto: X0 Y0 Z0
|
||
```
|
||
|
||
源码位置:
|
||
|
||
```text
|
||
app/src/state/store.js:1874-1901
|
||
```
|
||
|
||
`resolveTaskHalAxisPose()` 对 `ui.axisPoseFrame === "work"` 直接合并 task/HAL axisPose,因此 task/HAL status 中的 0 位姿可覆盖 HOME 后的 fixture/work pose。
|
||
|
||
这解释了“现实的轴值都不对”的一类现象:UI 显示值可能来自 task/HAL local/status 快照,而不是实际期望的机床/工件坐标连续反馈。
|
||
|
||
### P2. 程序时间和速度可能是 estimate 或单点 status
|
||
|
||
fallback timing 明确是:
|
||
|
||
```text
|
||
linuxcnc_canonical_motion_timing_estimate_not_planner_queue
|
||
```
|
||
|
||
task/HAL feedback 中的时间:
|
||
|
||
```text
|
||
timeSeconds = ui.taskCycle * 0.01
|
||
```
|
||
|
||
如果 taskCycle 不推进,程序时间也不推进。快照中 `taskCycle=0`,但 velocity 已为 3600,说明当前 runtime feedback 的时间/速度字段并不构成可靠连续执行轨迹。
|
||
|
||
### P2. “逐行执行过程”目前只显示当前 activeLine,不显示执行历史
|
||
|
||
UI 已显示:
|
||
|
||
```text
|
||
programLines
|
||
activeLine
|
||
gcode-row.active
|
||
progress
|
||
```
|
||
|
||
但没有看到 execution log / visited line history / per-feedback transcript。
|
||
|
||
因此用户能看到程序文本和当前行,但看不到完整“一行一行执行过程”的历史记录。
|
||
|
||
## 9. 正确的 RUN 执行模型建议
|
||
|
||
按你提出的标准,正确模型不应靠前端定时器自造轨迹,而应由 task/HAL/motion runtime 产出状态。
|
||
|
||
建议目标模型:
|
||
|
||
```text
|
||
1. RUN 只发送 LinuxCNC task command:
|
||
EMC_TASK_SET_STATE ON
|
||
EMC_TASK_SET_MODE AUTO
|
||
EMC_TASK_PLAN_RUN
|
||
|
||
2. task/HAL runtime 在程序执行期间持续 run task/motion/servo cycles。
|
||
|
||
3. 每个 cycle 或每批 cycle 产生 LinuxCNC-owned feedback:
|
||
activeLine
|
||
statement/source line
|
||
axisPose
|
||
jointPose
|
||
currentVelocity
|
||
requestedVelocity
|
||
distanceToGo / DTG
|
||
queueDepth
|
||
interpState
|
||
cycle / taskCycle / servoCycle
|
||
switchkinsType / RTCP state
|
||
|
||
4. worker 将 feedback 推送给 main thread,或 main thread 通过明确的 runtime pump 读取。
|
||
|
||
5. store 只消费 feedback,不自行按 motionIndex/sampleIndex 推进真实运行态。
|
||
|
||
6. UI 渲染:
|
||
- G-code 当前行高亮来自 feedback.line;
|
||
- DRO 轴值来自 feedback.axisPose 或 kinematics frame;
|
||
- 速度来自 feedback.currentVelocity;
|
||
- 程序时间来自 feedback.timeSeconds;
|
||
- 执行历史记录追加每次 feedback。
|
||
```
|
||
|
||
注意:这里的“pump”可以在 worker 内部完成,也可以由 main thread 调用 runtime pump,但语义必须是 LinuxCNC task/HAL/motion cycles 产生反馈,而不是 UI 自己按 wall clock 或固定 index 插值。
|
||
|
||
## 10. 后续测试建议
|
||
|
||
建议在 `working_run` 后续补一个严格测试脚本,断言以下行为:
|
||
|
||
```text
|
||
1. 加载一个短 G-code:
|
||
G90 G21
|
||
G0 X0 Y0 Z5
|
||
G1 X10 F600
|
||
G1 Y10 F300
|
||
M30
|
||
|
||
2. RUN 后采集至少 5 个 feedback tick。
|
||
|
||
3. 断言每个 tick 的 sourceMode 都是 linuxcnc-task-motion-hal-wasm。
|
||
|
||
4. 断言 activeLine 按 feedback 推进,而不是固定跳到 line 5 后停止。
|
||
|
||
5. 断言 servoCycle/taskCycle 单调递增。
|
||
|
||
6. 断言 axisPose 与当前 G-code motion 目标/插补位置一致。
|
||
|
||
7. 断言 currentVelocity 与当前运动段 F 值/override/task status 对齐。
|
||
|
||
8. 断言 UI `.gcode-row.active`、DRO、runtime feedback 文本和 store state 使用同一个 feedback snapshot。
|
||
|
||
9. 断言执行历史中能看到逐条 line/tick 记录。
|
||
```
|
||
|
||
## 11. 本轮验证命令
|
||
|
||
已运行:
|
||
|
||
```bash
|
||
npm --prefix web-rtcp-5axis-sim-plan/app run build
|
||
```
|
||
|
||
结果:
|
||
|
||
```text
|
||
gmoccapy_static_build=ok
|
||
```
|
||
|
||
浏览器采集脚本在用户中断前已产生完整快照 JSON,未作为正式 smoke 通过声明;本报告只把它作为辅助观察。
|
||
|
||
## 12. 总结
|
||
|
||
当前 `web-rtcp-5axis-sim-plan` 的 G-code 程序加载、文本显示、interpreter canonical motion、task/HAL 命令发送和 status 映射链路已经存在。
|
||
|
||
但当前 `RUN` 的核心行为仍是:
|
||
|
||
```text
|
||
发送命令 -> runCycles 固定 5 个 task cycles -> readStatus 一次 -> 更新 UI 一次
|
||
```
|
||
|
||
它没有形成“G-code 执行期间 task/HAL feedback 持续推送/持续消费”的运行态闭环。因此,按实际时间和进给速度连续执行、真实轴值连续更新、逐行执行过程可见,这三项目前都不能判定为满足。
|