Files
cnc_wams/web-rtcp-5axis-sim-plan/working_run/01-run-gcode-execution-principle-and-test.md

15 KiB
Raw Blame History

01 RUN 与 G-code 执行原理测试记录

生成时间2026-06-22

1. 参考接续文件

本记录按 textbak 中的接续要求核对,不把 UI fixture、自增行号、JS-owned CNC 语义当作真实运行证明。

关键约束来自:

textbak/text2.txt
- 数控/G-code/remap/tool/parameter/planner/kinematics/user-M 语义必须来自 LinuxCNC upstream 或 vendored LinuxCNC C/C++ source。
- UI 已优先解析 run_stepfallback 到 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 runtimeRUN 的连续执行模型仍需要按 task/HAL feedback 口径复核。

2. 当前 G-code 加载过程

启动入口:

app/src/main.js:17-23

页面启动时创建 store、挂载 gmoccapy UI并异步附加

1. LinuxCNC INI config
2. LinuxCNC kinematics runtime
3. LinuxCNC interpreter runtime
4. LinuxCNC task/HAL runtime
5. machine file staging

默认程序加载:

app/src/main.js:103-124

ensureDefaultLinuxCncProgramPreview() 在 interpreter 与 machine files staged 后选择默认 LinuxCNC 5-axis source然后 dispatch

LOAD_LINUXCNC_GCODE_SOURCE

选择 vendored G-code 后store 的处理链是:

app/src/state/store.js:744-792

实际步骤:

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(...)

app/src/state/store.js:1033-1057

G-code 文本显示在 UI

app/src/ui/gmoccapy-shell.js:193-228

这里使用 state.programLines 逐行渲染,state.activeLine 对应的行加 .active,同时显示:

Current line ${state.activeLine}
${state.activeLine} / ${programEndLine}

所以“能否看到 G-code 程序”和“是否逐行显示当前行”取决于 programLinesactiveLine 是否被同一执行反馈链正确更新。

3. interpreter 解释与 preview 过程

RUN_INTERPRETER_PROGRAM 不是机床运行按钮,它是在加载程序后调用 LinuxCNC interpreter runtime生成 canonical motion / summary / timing 基础数据:

app/src/state/store.js:468-508

解释完成后:

app/src/state/store.js:509-555

当前实现会:

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(...) 的优先级:

app/src/state/store.js:2369-2379

如果 execution.plannerTiming.plannerRuntimeReady === true,使用 plannerTiming否则调用

buildProgramExecutionTiming(...)

该 timing fallback 的语义边界明确是估算:

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 的速度计算逻辑:

app/src/runtime/execution-timing.js:75-123

它按 canonical motion 段计算:

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 入口:

app/src/state/store.js:1059-1100

分两条路径。

5.1 task/HAL runtime loaded 路径

如果 state.taskHalRuntime?.loaded 为 trueRUN 执行:

app/src/state/store.js:1066-1072

发送命令:

EMC_TASK_SET_STATE ON
EMC_TASK_SET_MODE AUTO
EMC_TASK_PLAN_RUN line = activeLine - 1

然后调用:

runTaskHalCommandSequence(..., { taskCycles: 5 })

runTaskHalCommandSequence 的实现:

app/src/state/store.js:1572-1604

当前真实顺序是:

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。

结论:

当前 RUN 是“发送 task/HAL 命令 -> 主动推进固定 cycles -> 拉取一次 status -> 应用到 UI”。
源码中没有看到 task/HAL worker 在程序运行期间主动持续 postMessage status 的订阅/推送通道。

5.2 task/HAL runtime missing fallback

如果 task/HAL runtime 未加载,RUN 走:

app/src/state/store.js:1074-1098

即:

nextProgramRuntimeSamplePlayback(state, 5)

这个 fallback 按 sampleIndex 或 motionIndex 一次跳 5 步,更新 activeLine / axisPose / feed / runtimeFeedback。

语义边界在 feedback 中标记为:

linuxcnc-tp-runtime-sample
linuxcnc_tp_run_cycle_feedback_without_hardware

或在没有 sample 时退回:

linuxcnc-canonical-motion
linuxcnc_canonical_motion_feedback_without_tp_sample

这条路径不能当作真实 task/HAL 运行过程。

6. task/HAL feedback 如何映射到 UI 状态

task/HAL runtime 包装:

app/src/runtime/linuxcnc-task-hal-runtime.js:96-114

暴露的是:

sendCommand(command)
runCycles(options)
readStatus()

worker client

app/src/runtime/linuxcnc-task-hal-worker-client.js:14-28

worker 本体:

app/src/runtime/linuxcnc-task-hal-worker.js:5-17
app/src/runtime/linuxcnc-task-hal-worker.js:19-53

worker 当前是 request/response 模型:

主线程 postMessage({ id, type, payload })
worker 执行 handleMessage(type, payload)
worker postMessage({ id, ok, result })

没有看到类似:

subscribeStatus
onStatus
while running post feedback

因此,当前 task/HAL feedback 不是“运行时持续推送”,而是 UI/store 调用 readStatus() 得到的一次快照。

状态归一化:

app/src/runtime/linuxcnc-task-hal-runtime.js:155-199

normalizeTaskHalStatus() 从 motion/HAL pins 生成:

ui.taskState
ui.taskMode
ui.interpState
ui.taskCycle
ui.servoCycle
ui.motionQueueDepth
ui.activeLine
ui.switchkinsType
ui.axisPose
ui.currentVelocity

应用到 store

app/src/state/store.js:1776-1835

applyTaskHalStatusPatch(...) 将 status 映射为:

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 字段:

app/src/state/store.js:1925-1948

包括:

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 快照,作为辅助证据。正式结论仍以源码追踪为主。

辅助证据文件:

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

观察结论:

1. 点击 RUN 后,状态从 line 1 推进到 line 5servoCycle 从 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

期望:

G-code 程序执行时task/HAL/motion runtime 持续产生状态;
UI/store 持续消费这些状态;
当前行、轴值、速度、DTG、程序时间来自同一个 feedback source。

当前:

RUN 只主动推进固定 taskCycles=5然后 readStatus 一次。
没有看到 worker 主动推送 status。
没有看到 store 中存在运行期间持续 readStatus/readEvents 的执行循环。

影响:

RUN 后页面可能停在某个 status snapshot。
用户看到 runState=running但 activeLine、轴值、速度不会随真实时间继续推进。

P1. 轴值存在状态回退/覆盖风险

快照中可见:

after-home: X43 Y-32.15 Z-11.306
after-auto: X0 Y0 Z0

源码位置:

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 明确是:

linuxcnc_canonical_motion_timing_estimate_not_planner_queue

task/HAL feedback 中的时间:

timeSeconds = ui.taskCycle * 0.01

如果 taskCycle 不推进,程序时间也不推进。快照中 taskCycle=0,但 velocity 已为 3600说明当前 runtime feedback 的时间/速度字段并不构成可靠连续执行轨迹。

P2. “逐行执行过程”目前只显示当前 activeLine不显示执行历史

UI 已显示:

programLines
activeLine
gcode-row.active
progress

但没有看到 execution log / visited line history / per-feedback transcript。

因此用户能看到程序文本和当前行,但看不到完整“一行一行执行过程”的历史记录。

9. 正确的 RUN 执行模型建议

按你提出的标准,正确模型不应靠前端定时器自造轨迹,而应由 task/HAL/motion runtime 产出状态。

建议目标模型:

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 后续补一个严格测试脚本,断言以下行为:

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. 本轮验证命令

已运行:

npm --prefix web-rtcp-5axis-sim-plan/app run build

结果:

gmoccapy_static_build=ok

浏览器采集脚本在用户中断前已产生完整快照 JSON未作为正式 smoke 通过声明;本报告只把它作为辅助观察。

12. 总结

当前 web-rtcp-5axis-sim-plan 的 G-code 程序加载、文本显示、interpreter canonical motion、task/HAL 命令发送和 status 映射链路已经存在。

但当前 RUN 的核心行为仍是:

发送命令 -> runCycles 固定 5 个 task cycles -> readStatus 一次 -> 更新 UI 一次

它没有形成“G-code 执行期间 task/HAL feedback 持续推送/持续消费”的运行态闭环。因此,按实际时间和进给速度连续执行、真实轴值连续更新、逐行执行过程可见,这三项目前都不能判定为满足。