# 03 程序修复详细步骤 生成时间:2026-06-22 本文档面向实际编码人员,按问题给出具体修改步骤。执行前建议先创建修复分支。 ```bash git status --short git switch -c fix/web-rtcp-5axis-working1 ``` ## 1. D1:自动挂接 LinuxCNC kinematics frame ### Step 1.1 增加期望 frame source 状态 文件: ```text web-rtcp-5axis-sim-plan/app/src/state/store.js ``` 在 `initialState` 增加字段: ```js desiredFrameSourceMode: "fixture-ui-only", ``` 保留现有: ```js sourceMode frameSourceMode ``` 三者语义: ```text desiredFrameSourceMode: 用户/运行时希望使用的 frame 来源 frameSourceMode: 当前 rtcpFrame 实际来源 sourceMode: UI 总体展示来源,可继续跟当前 frame source 同步 ``` ### Step 1.2 修改 ATTACH_KINEMATICS_RUNTIME 位置: ```text store.js -> case "ATTACH_KINEMATICS_RUNTIME" ``` runtime loaded 时设置: ```js desiredFrameSourceMode: "source-derived-kinematics-wasm", ``` runtime missing 时设置: ```js desiredFrameSourceMode: "fixture-ui-only", ``` 不要只依赖 `sourceMode` / `frameSourceMode`。 ### Step 1.3 修改 buildFrameForState 当前逻辑大意: ```js const requestedSourceMode = state.frameSourceMode || state.sourceMode; ... if (requestedSourceMode === "source-derived-kinematics-wasm") { if (runtime loaded && !async) { ... } else { sourceMode = "fixture-ui-only"; } } ``` 建议改为: ```js const requestedSourceMode = state.desiredFrameSourceMode || state.frameSourceMode || state.sourceMode; ``` async runtime 已加载但尚未返回 frame 时,可以临时生成 fixture frame,但必须带上可诊断原因,不要覆盖 desired source。 ### Step 1.4 修改 setState 写回策略 当前 `setState()` 中: ```js sourceMode: frame.sourceMode, frameSourceMode: frame.sourceMode, ``` 建议改为: ```js sourceMode: frame.sourceMode, frameSourceMode: frame.sourceMode, desiredFrameSourceMode: next.desiredFrameSourceMode || frame.sourceMode, ``` 关键点:不要因为临时 fixture frame 把 `desiredFrameSourceMode` 变成 fixture。 ### Step 1.5 修改 scheduleAsyncKinematicsRefresh 当前 guard 不应依赖已解析 frame source: ```js if (state.frameSourceMode !== "source-derived-kinematics-wasm") return null; ``` 改为: ```js if (state.desiredFrameSourceMode !== "source-derived-kinematics-wasm") return null; if (!state.kinematicsRuntime?.loaded) return null; if (!isAsyncKinematicsRuntime(state.kinematicsRuntime)) return null; ``` 如果当前 frame 已经 ready 且 activeLine/axisPose/kinsType 未变化,可继续跳过刷新。 ### Step 1.6 修改 refreshAsyncKinematicsFrame 成功写回 成功后确保: ```js sourceMode: "source-derived-kinematics-wasm", frameSourceMode: "source-derived-kinematics-wasm", desiredFrameSourceMode: "source-derived-kinematics-wasm", ``` 失败时: ```js operatorMessage: `LinuxCNC kinematics refresh failed: ${error.message}` ``` 并保留 retry 能力。 ### Step 1.7 修改 main.js 初始化顺序 文件: ```text web-rtcp-5axis-sim-plan/app/src/main.js ``` `attachDefaultKinematicsRuntime()` 已调用: ```js await store.refreshKinematicsFrame(...) ``` 修复后保留该调用,并在 profile/INI 变更订阅中,runtime attach 完成后再次刷新: ```js await attachDefaultKinematicsRuntime(...) await store.refreshKinematicsFrame({ operatorMessage: "..." }) ``` 注意避免无限刷新。可通过 `asyncFrameRefreshSequence` 或 readiness 状态判断。 ### Step 1.8 D1 测试 新增或更新测试: ```text web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html ``` 或新增: ```text web-rtcp-5axis-sim-plan/tests/browser/kinematics_auto_refresh_smoke.html ``` 断言: ```js await waitUntil(() => window.webRtcp5AxisSimulation.getState().sourceMode === "source-derived-kinematics-wasm") assertText('[data-rtcp-diagnostic="boundary"]', 'linuxcnc_kinematics_wasm_c_abi') assertText('[data-rtcp-diagnostic="kinematics-ready"]', 'ready') ``` ## 2. D2:修复 3D 预览可见性 ### Step 2.1 修复 updateToolpathPreview 未定义变量 文件: ```text web-rtcp-5axis-sim-plan/app/src/visualization/five-axis-scene.js ``` 在 `updateToolpathPreview(preview, state)` 中补齐: ```js const toolPosition = executionToolPosition(state, previewPoints); const fitPoints = collectFitPoints(previewPoints, executedPoints, currentSegmentPoints, toolPosition); const fitKey = [ previewPoints.length, executedPoints.length, currentSegmentPoints.length, previewSourceMode(state), state.programExecutionMotionIndex || 0, state.programExecutionSampleIndex || 0, ].join(":"); ``` 确保 `updateToolExecutionMarker()` 使用同一个 `toolPosition`。 ### Step 2.2 增加常驻机床参考模型 新增函数: ```js function createMachineReferenceModel() { ... } function updateMachineReferenceModel(preview, state) { ... } ``` 推荐对象: ```text grid/table base: dark gray plane/box X axis: red line Y axis: green line Z axis: blue line rotary ring: cyan/yellow ring tool holder: small cylinder/cone TCP marker: existing sphere ``` 在 `createScene()` 中: ```js const machineModel = createMachineReferenceModel(); scene.add(machineModel.root); ``` 在 preview 对象中保存: ```js machineModel ``` 在 `updateToolpathPreview()` 中: ```js updateMachineReferenceModel(preview, state); ``` ### Step 2.3 提高 fallback 可见性 在 `renderFallbackPreview()` 中,路径为空也要绘制: ```text 工作台矩形 XYZ 坐标轴 旋转中心 TCP 点 ``` 要求颜色和尺寸足够明显,避免黑底上不可见。 ### Step 2.4 强化 canvas dataset `exposePreviewDataset()` 已存在,修复后确保任意路径都稳定输出: ```text data-three-ready="true" data-three-renderer data-three-scene-objects data-three-path-points data-three-tool-execution-marker ``` 如果 fallback: ```text data-three-fallback-reason ``` 如果 WebGL: ```text data-three-renderer="webgl" ``` ### Step 2.5 D2 测试 新增 pixel 检查: ```js const stats = canvasPixelStats(canvas) assert(stats.nonBlackRatio > 0.02) assert(stats.averageLuminance > 5) ``` 同时检查: ```js Number(canvas.dataset.threeSceneObjects) > 0 canvas.dataset.threeReady === "true" ``` 建议覆盖 desktop 和 mobile viewport。 ## 3. D3:修复 HOME/JOG 坐标连续性 ### Step 3.1 记录 pending JOG 上下文 文件: ```text web-rtcp-5axis-sim-plan/app/src/state/store.js ``` 在 `initialState` 增加: ```js pendingJogCommand: null, ``` 在 `case "JOG"` 的 task/HAL runtime 分支,发送命令前记录: ```js setState({ pendingJogCommand: { axis, direction, increment, basePose: state.axisPose, createdAtLine: state.activeLine, }, operatorMessage: `task/HAL jog ${axis.toUpperCase()} ...`, }) ``` 注意现有代码直接调用 `runTaskHalCommandSequence()`,需要避免两次 setState 引发顺序混乱。可把 pending context 作为 `runTaskHalCommandSequence()` 的 options 传入,最终在 command started patch 中写入。 ### Step 3.2 给 task/HAL status 增加坐标系元数据 文件: ```text web-rtcp-5axis-sim-plan/app/src/runtime/linuxcnc-task-hal-runtime.js ``` 在 `ui` 对象中增加: ```js axisPoseFrame: "task-local", ``` 如果 runtime 能确定是 work pose,则写: ```js axisPoseFrame: "work", ``` 如果能计算增量,增加: ```js axisPoseDelta: { x, y, z, a, b, c } ``` 短期不能准确判断时,不要伪装成 work。 ### Step 3.3 修改 applyTaskHalStatusPatch 当前: ```js const axisPose = clampAxisPoseToProfile({ ...state.axisPose, ...ui.axisPose, }, state.profile); ``` 改为单独函数: ```js const axisPose = resolveTaskHalAxisPose(state, status); ``` 建议实现: ```js function resolveTaskHalAxisPose(state, status) { const ui = status?.ui || {}; if (ui.axisPoseFrame === "work") { return clampAxisPoseToProfile({ ...state.axisPose, ...ui.axisPose }, state.profile); } if (ui.axisPoseDelta) { return addAxisDelta(state.axisPose, ui.axisPoseDelta, state.profile); } if (state.pendingJogCommand && isJogStatus(status)) { const { axis, direction, increment, basePose } = state.pendingJogCommand; return clampAxisPoseToProfile({ ...basePose, [axis]: Number(basePose[axis] || 0) + direction * increment, }, state.profile); } if (!ui.axisPoseFrame && wouldResetNonZeroPoseToLocalZero(state.axisPose, ui.axisPose)) { return state.axisPose; } return clampAxisPoseToProfile({ ...state.axisPose, ...ui.axisPose }, state.profile); } ``` ### Step 3.4 清理 pending JOG `TASK_HAL_STATUS_APPLIED` 后,如果使用了 pending JOG: ```js pendingJogCommand: null ``` 如果 command failed: ```js pendingJogCommand: null ``` ### Step 3.5 HOME 同步 HOME 成功后,UI 和 task/HAL runtime 必须同一坐标基准。短期可在 HOME fallback patch 中明确: ```js axisPose: initialAxisPose ``` task/HAL HOME status 如果返回局部原点,不能覆盖 `initialAxisPose`,除非 status 标记 `axisPoseFrame="work"`。 ### Step 3.6 D3 测试 新增测试步骤: ```js power on manual home capture X/Y/Z jog X+ assert X === previousX + jogIncrement assert Y/Z unchanged jog Y- assert Y === previousY - jogIncrement assert X/Z unchanged ``` 同时验证 DRO 文本: ```js document.querySelector('[data-region="dro"]').textContent ``` ## 4. 本地验证命令 建议按顺序执行: ```bash npm --prefix web-rtcp-5axis-sim-plan/app run build node web-rtcp-5axis-sim-plan/tests/node/verify_rtcp_store.mjs node web-rtcp-5axis-sim-plan/tests/node/verify_five_axis_session.mjs node web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_kinematics_runtime.mjs node web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_task_hal_runtime.mjs ``` 如果项目已有 browser smoke: ```bash web-rtcp-5axis-sim-plan/tests/browser/verify_gmoccapy_shell_browser.sh ``` 修复后再执行 QA 站点测试脚本或新增等价本地测试。