26 KiB
05 RUN 功能程序实现详细实施步骤
生成时间:2026-06-22
1. 实施目标
本文件把 working_run 下 01-04 文档中的原则、前置条件、源码对照和测试结论,整理成可直接执行的程序实现步骤。
目标不是做一个前端 G-code 播放器,而是实现这个链路:
LinuxCNC INI contract
-> profile 与 INI 一致
-> kinematics WASM 与 INI [KINS] 一致
-> machine files 按同一个 INI stage
-> task/HAL runtime 按同一个 machine context 初始化
-> selected G-code 通过 task plan open 打开
-> RUN 发送 LinuxCNC task command
-> taskHalStatusLoop 持续 runCycles/readStatus
-> UI 只消费 task/HAL/motion feedback
2. 实施边界
必须保持的边界:
1. 不在 UI/store 自行解析 G-code 来推进 activeLine。
2. 不在 UI/store 自造 axisPose、velocity、DTG。
3. 不把 canonical timing estimate 当成真实 task/HAL runtime。
4. 不在真实 RUN 前置失败时退回 fixture playback。
5. RUN、PAUSE、RESUME、STOP 必须映射为 LinuxCNC task command 语义。
6. activeLine、DRO、RTCP、velocity、runtime feedback 必须来自同一个 task/HAL/motion status snapshot。
允许保留的 fallback:
fixture-line-playback 只能作为开发演示 fallback。
一旦 taskHalRuntime.loaded === true,真实 RUN 失败必须明确 blocked,不能 fallback 冒充 LinuxCNC runtime。
3. 主要代码落点
需要实现或调整的文件:
app/src/runtime/linuxcnc-ini-runtime.js
app/src/profiles/index.js
app/src/profiles/xyzac-trt.js
app/src/profiles/xyzbc-trt.js
app/src/runtime/linuxcnc-kinematics-runtime.js
app/src/runtime/linuxcnc-machine-file-staging.js
app/src/runtime/linuxcnc-task-hal-runtime.js
app/src/runtime/linuxcnc-task-hal-worker-client.js
app/src/runtime/linuxcnc-task-hal-worker.js
app/src/state/store.js
app/src/state/linuxcnc-task-policy.js
app/src/runtime/full-execution-boundary.js
app/src/ui/gmoccapy-shell.js
app/package.json
需要新增或扩展的测试:
tests/node/verify_run_preconditions.mjs
tests/node/verify_machine_file_staging.mjs
tests/node/verify_linuxcnc_ini_runtime.mjs
tests/node/verify_linuxcnc_kinematics_runtime.mjs
tests/node/verify_linuxcnc_task_hal_runtime.mjs
tests/node/verify_run_feedback_loop.mjs
tests/node/verify_rtcp_store.mjs
qa/web-rtcp-5axis-site-test/capture-toolpath-preview-cases.mjs
4. 阶段 0:建立当前状态基线
先确认当前代码和文档一致,避免在错误前提上继续实现。
执行:
node web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_machine_file_staging.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_task_hal_runtime.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_run_feedback_loop.mjs
npm --prefix web-rtcp-5axis-sim-plan/app run build
基线记录:
1. 哪些测试已通过。
2. 哪些测试失败,失败是否来自外部 linuxcnc source 缺文件、runtime 缺功能,还是 RUN 逻辑本身。
3. 当前 store 中是否已有 validateRunPreconditions、initializeTaskHalSession、runValidatedTaskHalProgramRun、taskHalStatusLoop。
通过标准:
已知 smoke 能稳定复现。
失败项有明确归因。
不在没有基线的情况下改 RUN 主流程。
5. 阶段 1:实现 INI definition contract
目标:让 RUN 依赖结构化 INI readiness,而不是散落字符串和 profile 默认值。
5.1 扩展 INI parser
文件:
app/src/runtime/linuxcnc-ini-runtime.js
实现内容:
1. parseLinuxCncIni(text, options) 必须解析这些 section:
[EMC]
[DISPLAY]
[RS274NGC]
[KINS]
[HAL]
[HALUI]
[TRAJ]
[EMCMOT]
[TASK]
[EMCIO]
[AXIS_*]
[JOINT_*]
2. 返回结构必须包含:
machineName
profileId
kinematics.name
kinematicsModuleId
kinematicsParameters.sparm
kinematicsParameters.joints
traj.coordinates
traj.linearUnits
traj.angularUnits
rs274ngc.halPinVars
rs274ngc.remaps
rs274ngc.subroutinePath
rs274ngc.parameterFile
hal.halui
hal.halFiles
hal.postguiHalFiles
hal.halcmd
hal.switchkinsSignal
halui.mdiCommands
emcmot.module
emcmot.servoPeriodNs
task.module
task.cycleTimeSeconds
emcio.toolTable
axisLimits
jointConfig
validation.ready
validation.errors
3. validateIniConfig() 必须校验:
required sections 存在
COORDINATES 是 XYZAC 或 XYZBC
KINEMATICS 与 profile 一致
JOINTS = 5
JOINT_0..JOINT_4 完整
AXIS_* 与 COORDINATES 一致
M428/M429/M430 remap 完整
HAL_PIN_VARS = 1
motion.analog-out-03 => motion.switchkins-type HALCMD 存在
SERVO_PERIOD 存在
CYCLE_TIME 存在
TOOL_TABLE 存在
注意:
[DISPLAY] GEOMETRY 和 JOG_AXES 只能用于 UI 显示,不能覆盖 [TRAJ] COORDINATES。
[KINS] sparm=identityfirst 决定 switchkins type 0 是 identity。
xyzac-trt 和 xyzbc-trt 的 axis/joint contract 不能混用。
5.2 应用 INI 到 profile
文件:
app/src/runtime/linuxcnc-ini-runtime.js
app/src/profiles/index.js
app/src/profiles/xyzac-trt.js
app/src/profiles/xyzbc-trt.js
实现内容:
1. applyIniConfigToProfile(profile, iniConfig) 只接受 validation.ready=true 的 INI。
2. profile.machineName 来自 [EMC] MACHINE。
3. profile.traj 来自 [TRAJ]。
4. profile.kinematicsModuleId 来自 [KINS] KINEMATICS 推导结果。
5. profile.axisLimits 来自 [AXIS_*]。
6. profile.jointConfig 来自 [JOINT_*]。
7. profile.rs274ngc 来自 [RS274NGC]。
8. profile.hal 来自 [HAL]。
9. profile.emcmot/task/emcio 保存到 profile 或 linuxCncIniConfig,供 runtime/session 使用。
阻断行为:
INI validation 不 ready 时:
state.iniConfigReadiness.ready=false
operatorMessage=LinuxCNC INI contract invalid
RUN gate 必须返回 run blocked: LinuxCNC INI contract invalid
5.3 测试
扩展:
tests/node/verify_linuxcnc_ini_runtime.mjs
tests/node/verify_run_preconditions.mjs
断言:
1. xyzac-trt.ini 解析出 XYZAC、xyzac-trt-kins、JOINTS=5、AXIS_X/Y/Z/A/C。
2. xyzbc-trt.ini 解析出 XYZBC、xyzbc-trt-kins、JOINTS=5、AXIS_X/Y/Z/B/C。
3. M428/M429/M430 remap 完整。
4. HAL switchkins signal 完整。
5. 删除任一必填 section 后 validation.ready=false。
6. 删除 HAL_PIN_VARS 后 RUN 被阻断。
7. xyzac 使用 xyzbc tool table 或 parameter file 时 validation.ready=false。
6. 阶段 2:绑定 kinematics runtime
目标:运动学 runtime 必须来自 INI [KINS],不能只靠当前 UI profile 猜测。
文件:
app/src/runtime/linuxcnc-kinematics-runtime.js
wasm-port/runtime/sdk/src/linuxcnc-kinematics.js
app/src/state/store.js
实现内容:
1. load kinematics runtime 时只接受 iniConfig.kinematicsModuleId。
2. state.kinematicsRuntimeReadiness.moduleId 必须等于 profile.kinematicsModuleId。
3. switchKinematics(type) 后 readback 的 switchkinsType 必须进入 rtcpFrame metadata。
4. rtcpFrame.sourceMode 必须是 source-derived-kinematics-wasm。
5. frame metadata 必须记录:
profileId
iniPath
kinematicsModuleId
switchkinsType
coordinates
RUN gate 增加检查:
kinematicsRuntimeReadiness.loaded === true
kinematicsRuntimeReadiness.moduleId === profile.kinematicsModuleId
rtcpFrame.sourceMode === source-derived-kinematics-wasm
测试:
node web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_kinematics_runtime.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs
7. 阶段 3:实现 machine-file staging
目标:task/HAL runtime 看到的 INI、HAL、remap、tool table、G-code 必须来自同一个 LinuxCNC machine context。
文件:
app/src/runtime/linuxcnc-machine-file-staging.js
app/src/state/store.js
实现内容:
1. stageProfileMachineFiles(profile) 使用 profile.iniPath 和当前 iniText 生成 plan。
2. plan 必须包含:
profileId
iniPath
wasmDir
wasmIniPath
wasmProgramPath
files[]
taskHalSession
3. save 必须包含:
files[]
gcodeSources[]
gcodeFiles[]
summary
storageMode
storageCapability
4. files[] 至少包含:
当前 profile INI
当前 profile tool table
remap_subs/428remap.ngc
remap_subs/429remap.ngc
remap_subs/430remap.ngc
当前 profile demos 下的 G-code
5. selectMachineFileProgram(plan, save, sourceRel) 只能选择 LinuxCNC vendored five-axis demo G-code。
6. 选择 G-code 后,machineFileStaging.selectedGcodeSourceRel 和 plan.wasmProgramPath 必须同步更新。
状态更新:
MACHINE_FILE_STAGING_STARTED
MACHINE_FILE_STAGING_COMPLETE
MACHINE_FILE_STAGING_FAILED
LOAD_LINUXCNC_GCODE_SOURCE
失败阻断:
run blocked: LinuxCNC machine files not staged
run blocked: no machine-file G-code opened for task/HAL session
测试:
node web-rtcp-5axis-sim-plan/tests/node/verify_machine_file_staging.mjs
断言:
1. xyzac-trt stage 后 save.summary.kinds.remap >= 3。
2. gcodeSources 包含 xyzac_switchkins_test_1.ngc。
3. selectedGcodeSourceRel 为空时 RUN 被阻断。
4. LOAD_LINUXCNC_GCODE_SOURCE 后 wasmProgramPath 指向 selected source。
5. xyzac/xyzbc 的 INI、tool table、parameter file 不混用。
8. 阶段 4:初始化 task/HAL session
目标:在 RUN 前完成等价于 LinuxCNC EMC_TASK_PLAN_OPEN 的程序打开动作。
文件:
app/src/runtime/linuxcnc-task-hal-runtime.js
app/src/runtime/linuxcnc-task-hal-worker-client.js
app/src/runtime/linuxcnc-task-hal-worker.js
app/src/state/store.js
实现内容:
1. attach taskHalRuntime 后写入 taskHalRuntimeReadiness。
2. initializeTaskHalSession({ openProgram: true }) 执行:
buildTaskHalSessionFromMachineFiles(...)
taskHalRuntime.resetSession()
taskHalRuntime.initSession({ profileId, iniPath, iniText, programPath })
taskHalRuntime.stageFiles(session.files)
taskHalRuntime.openProgram(session.programPath)
dispatch TASK_HAL_SESSION_READY
readStatus()
dispatch TASK_HAL_STATUS_APPLIED
3. taskHalSession 必须记录:
profileId
iniPath
programPath
programSourceRel
files
semanticBoundary
4. readStatus() 必须能返回:
task.file 或 openedProgram.path
task.mode
task.state
task.interpState
task.nextProgramLine
task.openedLineCount
motionStatus.motion
ui.axisPose
ui.activeLine
ui.currentVelocity
ui.switchkinsType
RUN gate 增加检查:
taskHalRuntime.loaded === true
taskHalRuntimeReadiness.taskRuntimeReady === true
taskHalRuntimeReadiness.motionRuntimeReady === true
taskHalRuntimeReadiness.halRuntimeReady === true
taskHalSession.programPath === expectedTaskHalProgramPathForState(state)
task status opened file === taskHalSession.programPath
测试:
node web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_task_hal_runtime.mjs
9. 阶段 5:实现 RUN preconditions gate
目标:所有 RUN 入口都先经过统一 gate。
文件:
app/src/state/store.js
app/src/state/linuxcnc-task-policy.js
函数:
validateRunPreconditions(state, options)
expectedTaskHalProgramPathForState(state)
gateLinuxCncTaskAction(state, action)
实现顺序:
1. 检查 supported profile: xyzac-trt / xyzbc-trt。
2. 检查 INI loaded + ready。
3. 检查 INI path 与 profile.iniPath 一致。
4. 检查 [TRAJ] COORDINATES 与 profile.traj.coordinates 一致。
5. 检查 [KINS] moduleId 与 profile.kinematicsModuleId 一致。
6. 检查 kinematics runtime loaded/moduleId/frame source。
7. 检查 machine files staged。
8. 检查 selectedGcodeSourceRel 存在。
9. 检查 task/HAL runtime readiness。
10. requireTaskHalSession=true 时检查 taskHalSession.programPath 与 expected program path 一致。
11. 检查 task status:
task state ON 或 RUN 前可 set ON
task mode AUTO 或 RUN 前可 set AUTO
homed/no_force_homing 满足
opened file 与 session programPath 一致
返回结构:
{
apiName: "web-rtcp-5axis-run-preconditions",
ok: true,
profileId,
iniPath,
coordinates,
kinematicsModuleId,
selectedGcodeSourceRel,
programPath,
sourceMode: "linuxcnc-task-motion-hal-wasm",
semanticBoundary: "linuxcnc_ini_profile_kinematics_task_hal_run_preconditions"
}
失败返回:
{
ok: false,
operatorMessage: "run blocked: ...",
profileId,
iniPath,
coordinates,
kinematicsModuleId
}
必须覆盖的阻断消息:
run blocked: LinuxCNC INI not loaded
run blocked: LinuxCNC INI contract invalid
run blocked: machine profile and INI path mismatch
run blocked: machine profile and INI coordinates mismatch
run blocked: machine profile and INI kinematics mismatch
run blocked: LinuxCNC kinematics runtime not ready
run blocked: LinuxCNC kinematics module mismatch
run blocked: LinuxCNC kinematics frame not ready
run blocked: LinuxCNC machine files not staged
run blocked: no machine-file G-code opened for task/HAL session
run blocked: task/HAL runtime not ready
run blocked: task/HAL session program mismatch
run blocked: machine is not on
run blocked: machine is not homed
run blocked: task mode is not AUTO
测试:
node web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs
10. 阶段 6:实现 RUN command sequence
目标:RUN 按 LinuxCNC task command 发送,不直接播放 fixture。
文件:
app/src/state/store.js
函数:
runValidatedTaskHalProgramRun()
runTaskHalCommandSequence(commands, options)
实现步骤:
1. preflight = validateRunPreconditions(state, { requireTaskHalSession: false })。
2. preflight 失败时只设置 operatorMessage,不改 activeLine、不启动 loop。
3. expectedProgramPath = expectedTaskHalProgramPathForState(state)。
4. 如果 taskHalSession 不存在,或 programPath 不等于 expectedProgramPath:
initializeTaskHalSession({ openProgram: true })
5. ready = validateRunPreconditions(state, { requireTaskHalSession: true })。
6. ready 失败时只设置 operatorMessage,不启动 loop。
7. stopTaskHalStatusLoop("restarted")。
8. 发送:
EMC_TASK_SET_STATE ON
EMC_TASK_SET_MODE AUTO
EMC_TASK_PLAN_RUN line=linuxcncStartLine
9. runCycles({ taskCycles: initialBatch })
10. readStatus()
11. dispatch TASK_HAL_STATUS_APPLIED
12. 如果 status 仍在 READING,启动 taskHalStatusLoop。
重要修正:
普通 RUN 从头运行时 linuxcncStartLine=0。
不要用 activeLine - programStartLine 自动推导 run-from-line。
run-from-line 必须作为独立功能实现,并单独处理 previous modal state、安全提示和 LinuxCNC restart semantics。
runTaskHalCommandSequence() 要求:
1. allowFixtureSession=false 时绝不创建 fixture task/HAL session。
2. 每条 command 都通过 taskHalRuntime.sendCommand(command)。
3. command 后必须 runCycles/readStatus 一次。
4. status 通过 TASK_HAL_STATUS_APPLIED 统一落入 store。
5. 失败派发 TASK_HAL_COMMAND_FAILED。
11. 阶段 7:实现 taskHalStatusLoop
目标:RUN 后持续推进 task/HAL runtime,并持续消费真实 status。
文件:
app/src/state/store.js
状态:
taskHalStatusLoop: {
active,
sequence,
profileId,
iniPath,
kinematicsModuleId,
tickCount,
batchSize,
intervalMs,
taskPeriodNs,
servoPeriodNs,
lastStatusAt,
lastError,
stopReason
}
函数:
startTaskHalStatusLoop(options)
runTaskHalStatusLoopTick(sequence)
stopTaskHalStatusLoop(reason, options)
shouldContinueTaskHalStatusLoop(state, status)
实现步骤:
1. start 时先 stopTaskHalStatusLoop("restarted", notify=false)。
2. sequence 自增,写入 TASK_HAL_STATUS_LOOP_STARTED。
3. setTimeout 调度 runTaskHalStatusLoopTick(sequence)。
4. tick 内校验 loop.active、sequence、taskHalRuntime.loaded。
5. 调用 taskHalRuntime.runCycles({ taskPeriodNs, servoPeriodNs, taskCycles: batchSize })。
6. 调用 taskHalRuntime.readStatus()。
7. 如果 sequence 未过期,dispatch TASK_HAL_STATUS_APPLIED(status, loopSequence)。
8. shouldContinueTaskHalStatusLoop 为 true 时继续 setTimeout。
9. paused/complete/stopped/error 时 stopTaskHalStatusLoop。
停止条件:
motion.aborted === true -> stopped
interpState === paused 或 motion.paused === true -> paused
interpState === idle 且 nextProgramLine >= openedLineCount -> complete
sequence 不匹配 -> ignore stale tick
runtime missing -> stopped
参数来源:
taskPeriodNs 默认来自 [TASK] CYCLE_TIME。
servoPeriodNs 默认来自 [EMCMOT] SERVO_PERIOD。
batchSize 初始可为 5。
intervalMs 初始可为 25。
测试:
node web-rtcp-5axis-sim-plan/tests/node/verify_run_feedback_loop.mjs
断言:
1. RUN 后 programRuntimeFeedbackHistory 至少 3 条。
2. 每条 sourceMode=linuxcnc-task-motion-hal-wasm。
3. semanticBoundary=linuxcnc_task_motion_hal_wasm_simulation_runtime。
4. taskCycle/servoCycle 单调递增。
5. STOP 后 taskHalStatusLoop.active=false。
12. 阶段 8:统一 TASK_HAL_STATUS_APPLIED
目标:所有 task/HAL 状态进入 UI 前只走一个 reducer patch。
文件:
app/src/state/store.js
函数:
applyTaskHalStatusPatch(state, status, operatorMessage, options)
createTaskHalRuntimeFeedback(state, status, axisPose, activeLine)
resolveTaskHalAxisPose(state, status)
resolveTaskHalKinsType(state, status, activeLine)
rtcpStateFromKinsType(kinsType)
必须更新的 state:
taskHalStatus
taskHalExecutionPending
activeLine
axisPose
kinsType
rtcpState
programExecutionSourceMode
machine.powerOn
machine.taskState
machine.mode
machine.interpState
machine.taskPaused
runState
feed.currentVelocity
programRuntimeFeedback
programRuntimeFeedbackHistory
taskHalStatusLoop.tickCount
taskHalStatusLoop.lastStatusAt
规则:
1. activeLine 来自 status.ui.activeLine,不从 timer 或 sample index 推导。
2. axisPose 优先来自 status.ui.axisPose。
3. velocity 来自 status.ui.currentVelocity 或 motion current velocity。
4. switchkinsType 来自 status.ui.switchkinsType/HAL/motion,不从 G-code 文本猜测。
5. rtcpState 由 switchkinsType 映射。
6. runtime feedback history 最多保留 100 条。
7. axisPose、velocity、activeLine、DTG、kinsType 必须来自同一个 status。
需要重点防止:
task-local 0 值覆盖 HOME/work pose。
canonical preview pose 覆盖 runtime feedback pose。
fixture playback sourceMode 混入 task/HAL history。
测试:
node web-rtcp-5axis-sim-plan/tests/node/verify_rtcp_store.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_run_feedback_loop.mjs
13. 阶段 9:实现 STOP / ABORT / PAUSE / RESUME / STEP
目标:控制按钮与 LinuxCNC task command 对齐。
文件:
app/src/state/store.js
app/src/ui/gmoccapy-shell.js
行为:
STOP/ABORT:
stopTaskHalStatusLoop("stopped")
send EMC_TASK_ABORT
runCycles/readStatus 一次
TASK_HAL_STATUS_APPLIED
PAUSE:
stopTaskHalStatusLoop("paused")
send EMC_TASK_PLAN_PAUSE
runCycles/readStatus 一次
runState=paused
RESUME:
send EMC_TASK_PLAN_RESUME
runCycles/readStatus 一次
如果 interpState 回到 reading,restart taskHalStatusLoop
STEP:
task/HAL runtime loaded 时不走 fixture sample playback
runCycles({ taskCycles: 1 })
readStatus 一次
TASK_HAL_STATUS_APPLIED
测试:
1. RUN 后 PAUSE,loop active=false,runState=paused。
2. RESUME 后 loop 重新 active。
3. STOP 后 loop active=false,motion.aborted/stopped 状态进入 UI。
4. STEP 在 task/HAL loaded 时 sourceMode 仍是 linuxcnc-task-motion-hal-wasm。
14. 阶段 10:UI 显示与交互
目标:UI 只展示 store 中的 LinuxCNC-owned status,不推导运行状态。
文件:
app/src/ui/gmoccapy-shell.js
app/src/styles/gmoccapy.css
需要显示:
1. INI readiness:
loaded/ready/path
kinematics
coordinates
jointCount/axisCount
2. Machine files:
staged/error
file count
gcode source selected
storage mode
3. Task/HAL readiness:
taskRuntimeReady
motionRuntimeReady
halRuntimeReady
profileId
iniPath
4. Runtime feedback:
sourceMode
line
sample
taskCycle
servoCycle
velocity
DTG
switchkinsType
5. Run history:
最近 10 条 programRuntimeFeedbackHistory。
交互要求:
1. 选择 G-code 后必须 initializeTaskHalSession({ openProgram: true }) 或标记 session stale。
2. RUN button 只 dispatch RUN_MACHINE_FILE_PROGRAM / RUN action,不直接操作 line。
3. RUN blocked 时显示 operatorMessage。
4. UI 高亮行读取 state.activeLine。
5. DRO 读取 state.axisPose 或 state.programRuntimeFeedback.axisPose。
6. RTCP canvas 状态读取 state.rtcpState。
15. 阶段 11:full execution boundary
目标:让系统明确声明当前是不是完整 LinuxCNC task/HAL run,而不是含糊显示 ready。
文件:
app/src/runtime/full-execution-boundary.js
app/src/runtime/native-task-hal-audit.js
app/src/state/store.js
实现内容:
1. fullLinuxCncProgramExecutionReady 只在这些条件都满足时为 true:
INI contract ready
profile/INI coordinates match
kinematics runtime ready
machine files staged
task runtime ready
motion runtime ready
HAL runtime ready
selected G-code opened
2. hardwareDrive=false。
3. hostRealtimeKernel=false。
4. externalUserMProcessReady=false。
5. semanticBoundary 明确标注:
linuxcnc_task_motion_hal_wasm_simulation_runtime
测试:
node web-rtcp-5axis-sim-plan/tests/node/verify_full_execution_boundary.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_native_task_hal_audit.mjs
16. 阶段 12:Node smoke 集成
目标:所有关键链路进入 npm run smoke:node。
文件:
app/package.json
加入或确认:
{
"scripts": {
"smoke:node": "node ../tests/node/verify_linuxcnc_kinematics_runtime.mjs && node ../tests/node/verify_linuxcnc_interpreter_runtime.mjs && node ../tests/node/verify_linuxcnc_ini_runtime.mjs && node ../tests/node/verify_run_preconditions.mjs && node ../tests/node/verify_run_feedback_loop.mjs && node ../tests/node/verify_linuxcnc_task_hal_runtime.mjs"
}
}
实际脚本可以保留现有顺序,但必须覆盖:
INI parser
kinematics runtime
machine-file staging
task/HAL runtime
RUN preconditions
RUN feedback loop
store status patch
full boundary
执行:
npm --prefix web-rtcp-5axis-sim-plan/app run smoke:node
17. 阶段 13:浏览器证据采集
目标:证明浏览器 UI 中 RUN 不是一次性 status,也不是 fixture playback。
文件:
qa/web-rtcp-5axis-site-test/capture-toolpath-preview-cases.mjs
新增 case:
07-run-preconditions-and-feedback
浏览器步骤:
1. 打开 app。
2. 等待 state.iniConfigReadiness.loaded === true。
3. 选择 xyzac-trt。
4. 确认 INI/profile/kinematics module 都是 xyzac-trt。
5. 选择 xyzac_switchkins_test_1.ngc。
6. 点击 POWER。
7. 点击 HOME。
8. 点击 AUTO。
9. 点击 RUN。
10. 采集 t=0.2s / 0.5s / 1.0s / 2.0s / 5.0s state。
11. 截图。
断言:
1. taskHalStatusLoop.tickCount 增长。
2. programRuntimeFeedbackHistory.length 增长。
3. 每条 feedback sourceMode 是 linuxcnc-task-motion-hal-wasm。
4. activeLine 等于 UI 高亮行。
5. DRO 等于 state.programRuntimeFeedback.axisPose。
6. RTCP canvas 状态与 state.rtcpState 一致。
7. runtime feedback text 显示 taskCycle/servoCycle/line/velocity。
8. 没有 fixture-line-playback sourceMode。
输出:
qa/web-rtcp-5axis-site-test/output/run-preconditions-feedback.json
qa/web-rtcp-5axis-site-test/screenshots/run-preconditions-feedback/*.png
18. 最终验收命令
基础:
node web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_ini_runtime.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_kinematics_runtime.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_machine_file_staging.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_task_hal_runtime.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_run_feedback_loop.mjs
node web-rtcp-5axis-sim-plan/tests/node/verify_rtcp_store.mjs
npm --prefix web-rtcp-5axis-sim-plan/app run build
完整:
npm --prefix web-rtcp-5axis-sim-plan/app run smoke:node
bash web-rtcp-5axis-sim-plan/tests/browser/verify_gmoccapy_shell_browser.sh
bash web-rtcp-5axis-sim-plan/tests/browser/verify_gmoccapy_dist_browser.sh
node qa/web-rtcp-5axis-site-test/capture-toolpath-preview-cases.mjs
19. 完成标准
功能完成必须同时满足:
1. RUN 前能明确追溯到一个 LinuxCNC INI。
2. INI definition contract 完整通过。
3. profile/INI/kinematics runtime/moduleId/coordinates 一致。
4. machine-file staging 使用同一 profile 的 INI、HAL、remap、tool table、G-code。
5. selected G-code 已通过 taskHalRuntime.openProgram 打开。
6. machine ON、AUTO、homed/no_force_homing gate 明确。
7. RUN 发送 EMC_TASK_SET_STATE / EMC_TASK_SET_MODE / EMC_TASK_PLAN_RUN。
8. RUN 后 taskHalStatusLoop 持续推进 runCycles/readStatus。
9. activeLine、DRO、velocity、DTG、switchkinsType、RTCP 来自同一个 status snapshot。
10. PAUSE/RESUME/STOP/STEP 行为与 LinuxCNC task command 对齐。
11. Node smoke 通过。
12. 浏览器 JSON/截图证据证明 UI 正在消费 task/HAL/motion feedback。
13. fallback playback 不会冒充 LinuxCNC task/HAL runtime。
20. 建议实施顺序
推荐实际开发顺序:
1. 补强 INI parser/validation。
2. 补 run_preconditions 测试。
3. 补 machine-file staging 对 xyzac/xyzbc 混用的断言。
4. 补 taskHalSession opened program status 字段。
5. 修正 RUN line=0 与 run-from-line 分离。
6. 扩展 validateRunPreconditions 的 ON/AUTO/homed/opened-file gate。
7. 确认 taskHalStatusLoop 使用 INI CYCLE_TIME/SERVO_PERIOD。
8. 扩展 TASK_HAL_STATUS_APPLIED 的 same-snapshot 断言。
9. 补 PAUSE/RESUME/STOP/STEP 测试。
10. 更新 UI runtime feedback/history。
11. 跑 smoke:node。
12. 补浏览器证据采集。
每完成一个阶段都先跑该阶段对应 node 测试,再进入下一阶段。