Add RTCP simulation QA updates

This commit is contained in:
2026-06-22 09:30:27 -04:00
parent 61e2fe8441
commit ea8e10031b
51 changed files with 10008 additions and 178 deletions

View File

@@ -0,0 +1,212 @@
# 01 问题复盘
生成时间2026-06-22
## 1. 输入证据
测试报告:
```text
/home/meswork/cnc_wams/qa/web-rtcp-5axis-site-test/output/web-rtcp-5axis-site-test-report-2026-06-22.docx
```
原始自动化结果:
```text
/home/meswork/cnc_wams/qa/web-rtcp-5axis-site-test/output/site-test-report.json
```
截图目录:
```text
/home/meswork/cnc_wams/qa/web-rtcp-5axis-site-test/screenshots/
```
## 2. D1RTCP/运动学边界未自动挂接
### 现象
页面稳定加载后,内部运行时已经加载 LinuxCNC kinematics worker
```text
kinematicsRuntimeReadiness.loaded=true
semanticBoundary=linuxcnc_kinematics_wasm_c_abi
sourceMode=source-derived-kinematics-wasm
```
但 RTCP frame 仍显示:
```text
state.sourceMode=fixture-ui-only
state.frameSourceMode=fixture-ui-only
frameBoundary=fixture_frame_ui_plumbing_not_linuxcnc_kinematics_proof
```
手动执行:
```js
await window.webRtcp5AxisSimulation.refreshKinematicsFrame()
```
后可立即切换为:
```text
state.sourceMode=source-derived-kinematics-wasm
frameBoundary=linuxcnc_kinematics_wasm_c_abi
```
### 影响
- 首屏 RTCP/TCP 姿态并非自动来自 LinuxCNC source-derived kinematics。
- 界面 `Boundary ready` 可能显示 ready但 RTCP frame 仍在 fixture 边界,诊断信息不一致。
- 用户和测试脚本都会误判当前仿真语义边界。
### 涉及代码
```text
app/src/main.js
attachDefaultKinematicsRuntime()
app/src/state/store.js
ATTACH_KINEMATICS_RUNTIME
setState()
buildFrameForState()
scheduleAsyncKinematicsRefresh()
refreshAsyncKinematicsFrame()
app/src/runtime/rtcp-frame.js
buildRtcpFrame()
```
### 初步根因
当前 `setState()` 在异步 worker kinematics runtime 已加载但 frame 尚未刷新时,会通过 `buildFrameForState()` 生成 fixture frame随后又把
```js
frameSourceMode: frame.sourceMode
```
写回 state。这样本来期望进入 LinuxCNC kinematics 的请求状态被 fixture 结果覆盖。
`scheduleAsyncKinematicsRefresh()` 又依赖:
```js
state.frameSourceMode === "source-derived-kinematics-wasm"
```
`frameSourceMode` 已被覆盖成 `fixture-ui-only` 后,自动刷新不再触发。手动调用 `refreshKinematicsFrame()` 能成功,说明底层 worker 能用,问题在自动刷新状态机。
## 3. D23D 预览不可见
### 现象
首屏、程序运行、本地 G-code 导入、Audit 后截图中,左侧预览区均为黑底,未观察到五轴机床、刀具或刀路可见对象。
相关截图:
```text
01-home.png
04-run-state.png
05-local-program-opened.png
06-after-audit.png
```
### 影响
- 五轴/RTCP 仿真最重要的视觉反馈缺失。
- 用户无法通过界面验证 TCP 点、刀轴、程序轨迹和执行轨迹。
- 不满足项目文档中“Three.js 视口非空,能显示机床、刀具、刀路”的第一版完成定义。
### 涉及代码
```text
app/src/visualization/five-axis-scene.js
renderFiveAxisScene()
createScene()
updateToolpathPreview()
renderFallbackPreview()
exposePreviewDataset()
app/src/ui/gmoccapy-shell.js
renderPreview()
app/src/styles/gmoccapy.css
preview/canvas 尺寸和布局
```
### 初步根因
需要重点检查两类问题:
1. `updateToolpathPreview()` 内部使用了 `fitPoints``fitKey`,但当前函数片段中未看到局部定义。若实际执行进入 WebGL 分支,可能触发运行时异常并降级或中断渲染。
2. 当前 WebGL scene 只添加了路径线、tool marker、tool axis没有明确的机床基准模型、工作台、旋转轴、坐标轴等常驻对象。即使路径为空或颜色很暗用户也应看到基础机床对象。
另外fallback 分支必须在无 WebGL 或 WebGL 初始化失败时仍绘制明显对象,而不是仅写一个很小的文字提示。
## 4. D3HOME 后 JOG 坐标连续性异常
### 现象
稳定复核结果:
```text
after HOME:
X=43, Y=-32.15, Z=-11.306
after JOG X+:
X=1, Y=0, Z=0
after JOG Y-:
X=1, Y=-1, Z=0
```
### 影响
- DRO 中的手动移动不连续。
- 操作员会误以为机床从 HOME 坐标突然跳到任务/HAL 局部原点。
- 会话恢复、手动定位、RTCP 视觉反馈都可能被错误坐标污染。
### 涉及代码
```text
app/src/state/store.js
HOME
JOG
TASK_HAL_STATUS_APPLIED
applyTaskHalStatusPatch()
app/src/runtime/linuxcnc-task-hal-runtime.js
normalize/readStatus 输出的 ui.axisPose
app/src/state/linuxcnc-task-policy.js
JOG/HOME gate
```
### 初步根因
当前启用 task/HAL runtime 后JOG 走 task/HAL command path。`applyTaskHalStatusPatch()` 会把 runtime status 中的 `ui.axisPose` 直接覆盖 UI state
```js
const axisPose = clampAxisPoseToProfile({
...state.axisPose,
...ui.axisPose,
}, state.profile);
```
但 task/HAL runtime 返回的 `ui.axisPose` 看起来是 task-local 或 motion-local 坐标,初始值从 0 开始,并非当前 UI DRO/HOME 坐标系。因此 JOG 后坐标被另一套坐标系覆盖。
## 5. 非缺陷说明
以下现象在复核后不作为缺陷记录:
1. 页面初始数秒内 Task/HAL 和 machine-file staging 从 pending 过渡到 ready/staged属于异步启动暂态。
2. `M428/M429/M430` 经稳定态复核均可执行:
```text
M428 -> kins=tcp-xyzac, rtcp=on
M429 -> kins=identity, rtcp=off
M430 -> kins=userk, rtcp=off
```
3. Open 本地 G-code 使用 `programSource=operator-file`,这是当前实现命名,不是功能失败。

View File

@@ -0,0 +1,240 @@
# 02 修复总体方案
生成时间2026-06-22
## 1. 修复原则
1. 不用 JavaScript 重写 LinuxCNC 运动学语义。
2. RTCP/TCP frame 必须优先来自 LinuxCNC source-derived kinematics WASM。
3. 视觉层只能消费 runtime frame、canonical motion、task/HAL feedback不生成 G-code/CNC 语义。
4. JOG/HOME/DRO 必须明确坐标系,不允许 task-local 坐标无标记覆盖 UI work pose。
5. 每项修复必须新增或更新测试,先复现问题,再验证修复。
## 2. 修复优先级
| 优先级 | 问题 | 原因 |
| --- | --- | --- |
| P0 | D1 RTCP/运动学边界自动挂接 | 影响语义边界可信度,且修复面较集中 |
| P0 | D2 3D 预览可见性 | 影响产品第一视觉和核心仿真价值 |
| P1 | D3 HOME/JOG 坐标连续性 | 影响手动操作正确性,需谨慎处理坐标系 |
## 3. D1 修复方案
### 目标状态
页面稳定加载后无需手动调用,自动达到:
```text
state.sourceMode=source-derived-kinematics-wasm
state.frameSourceMode=source-derived-kinematics-wasm
state.rtcpFrame.semanticBoundary=linuxcnc_kinematics_wasm_c_abi
data-rtcp-diagnostic="kinematics-ready" -> ready
data-rtcp-diagnostic="boundary" -> linuxcnc_kinematics_wasm_c_abi
```
### 推荐设计
把“期望使用的 frame source”和“当前已经解析出的 frame source”分开
```text
desiredFrameSourceMode: source-derived-kinematics-wasm | fixture-ui-only
frameSourceMode: 当前 rtcpFrame.sourceMode
```
或者在不新增字段的情况下,至少保证 `setState()` 不用异步 runtime 的临时 fixture frame 覆盖 kinematics 请求状态。
推荐更清晰的做法:
1. 新增 `desiredFrameSourceMode`
2. `ATTACH_KINEMATICS_RUNTIME` 成功后设置:
```js
desiredFrameSourceMode: "source-derived-kinematics-wasm"
```
3. `buildFrameForState()` 对 async worker 不直接降级修改 desired state只生成临时 fixture frame并标记
```text
asyncFrameRefreshPending=true
```
4. `scheduleAsyncKinematicsRefresh()` 判断:
```js
state.kinematicsRuntime?.loaded &&
state.desiredFrameSourceMode === "source-derived-kinematics-wasm"
```
而不是依赖已经被 fixture 覆盖的 `frameSourceMode`
5. `refreshAsyncKinematicsFrame()` 成功后写入:
```js
sourceMode: "source-derived-kinematics-wasm"
frameSourceMode: "source-derived-kinematics-wasm"
desiredFrameSourceMode: "source-derived-kinematics-wasm"
```
### 防回退要求
任何以下动作后都不能把 frame 永久退回 fixture
```text
ATTACH_INI_CONFIG
SET_PROFILE
MACHINE_FILE_STAGING_COMPLETE
TASK_HAL_STATUS_APPLIED
LOAD_PROGRAM
LOAD_LINUXCNC_GCODE_SOURCE
HOME/JOG/RUN/STEP
```
如果某次 frame 刷新失败,应显示错误并保留 retry 能力,不能静默永久降级。
## 4. D2 修复方案
### 目标状态
首屏不加载任何用户程序时也必须可见:
```text
机床基准/工作台
XYZ 坐标轴
旋转轴标识
刀具/TCP marker
预览路径或占位路径
```
WebGL 不可用时2D fallback 也必须绘制清晰的轴线、路径和 TCP 点。
### 推荐设计
1. 修复 `updateToolpathPreview()` 中未定义变量风险:
```js
const toolPosition = executionToolPosition(state, previewPoints);
const fitPoints = collectFitPoints(previewPoints, executedPoints, currentSegmentPoints, toolPosition);
const fitKey = buildFitKey(...);
```
2.`createScene()` 中加入常驻机床模型:
```text
machineRoot
tableGroup
rotaryA/rotaryB/rotaryC visual rings
toolHolder
axisHelper
grid/reference plane
```
3. 增加单独函数:
```js
createMachineReferenceModel()
updateMachineReferenceModel(preview, state)
```
4. `renderFallbackPreview()` 中绘制:
```text
灰色工作台矩形
绿色/红色/蓝色 XYZ 轴
青色 TCP 点
亮色刀路 polyline
明显的 fallback 标签和点数
```
5. `exposePreviewDataset()` 必须稳定输出:
```text
data-three-ready=true
data-three-renderer=webgl | 2d-fallback
data-three-scene-objects > 0
data-three-path-points > 0 或 data-three-tool-execution-marker=true
```
### 可见性门槛
自动化测试应检查 canvas 像素,不只检查 DOM
```text
nonBlackRatio > 0.02
averageLuminance > 5
```
## 5. D3 修复方案
### 目标状态
HOME 后执行 JOG
```text
after HOME: X=43, Y=-32.15, Z=-11.306
after JOG X+: X=44, Y=-32.15, Z=-11.306
after JOG Y-: X=44, Y=-33.15, Z=-11.306
```
实际增量以 `state.machine.jogIncrement` 为准。
### 推荐设计
必须明确 task/HAL status 中 `ui.axisPose` 的坐标系。
推荐新增字段:
```js
ui.axisPoseFrame = "work" | "machine" | "task-local" | "joint-local"
ui.axisPoseDelta = { x, y, z, a, b, c } // JOG 增量可选
```
处理规则:
1. 如果 `axisPoseFrame === "work"`,可直接覆盖 UI work pose。
2. 如果 `axisPoseFrame === "task-local"` 且本次 motion type 是 JOG优先使用 `axisPoseDelta` 加到当前 state.axisPose。
3. 如果没有 frame 标记,不允许直接覆盖非零 UI pose必须保守保留旧 pose 或走 fallback 增量。
4. HOME 命令要把 task/HAL runtime 的参考 pose 与 UI HOME pose 同步,或者返回 `axisPoseFrame="work"`
### 短期修复方案
如果 task/HAL runtime 暂时不能增加 frame 元数据,可在 store 层先做保护:
```text
当 status motion type 为 JOG 且 ui.axisPose 接近局部原点时,
不要整体覆盖 state.axisPose
改用本次 JOG action 的 axis/direction/increment 计算 UI pose。
```
为了实现这点store 需要在发送 task/HAL JOG 命令时记录 pending jog context
```js
pendingJogCommand: { axis, direction, increment, basePose }
```
收到 `TASK_HAL_STATUS_APPLIED` 后:
```js
axisPose = {
...pendingJogCommand.basePose,
[axis]: pendingJogCommand.basePose[axis] + direction * increment
}
```
这是短期 UI 连续性修复;长期仍应让 runtime 明确坐标系。
## 6. 风险控制
| 风险 | 控制方式 |
| --- | --- |
| D1 修复导致 fixture fallback 不可用 | 保留 fallback但作为显式错误/降级状态,不覆盖 desired frame source |
| D2 加机床模型影响性能 | 常驻模型低面数,路径点仍受 `MAX_TOOLPATH_POINTS` 限制 |
| D3 坐标修复与真实 task/HAL 状态冲突 | 用 `axisPoseFrame` 标记,避免无标记状态直接覆盖 |
| 测试只在 headless 下通过 | 同时跑本地浏览器截图和 headless pixel 检查 |
## 7. 建议提交拆分
1. `fix: keep desired LinuxCNC kinematics frame source across async refresh`
2. `fix: render visible five-axis preview model and fallback scene`
3. `fix: preserve work-pose continuity for task-hal jog feedback`
4. `test: add browser regression for kinematics auto-refresh preview and jog continuity`

View File

@@ -0,0 +1,488 @@
# 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 站点测试脚本或新增等价本地测试。

View File

@@ -0,0 +1,72 @@
# 04 修复溯源矩阵
生成时间2026-06-22
## 1. 总体追溯原则
```text
测试问题 -> 证据 -> 本地代码落点 -> LinuxCNC/项目参考 -> 修复项 -> 验收项
```
任何修复都不能只修改 UI 文案掩盖问题,必须让 runtime state、DOM diagnostics、截图和自动化断言一致。
## 2. 问题到修复追溯表
| 问题 ID | 测试证据 | 本地代码落点 | LinuxCNC/项目参考 | 修复项 | 验收项 |
| --- | --- | --- | --- | --- | --- |
| D1 | `sourceMode=fixture-ui-only``frameBoundary=fixture_frame_ui_plumbing_not_linuxcnc_kinematics_proof`,但 `kinematicsRuntimeReadiness.loaded=true` | `app/src/state/store.js` `ATTACH_KINEMATICS_RUNTIME``setState()``scheduleAsyncKinematicsRefresh()``refreshAsyncKinematicsFrame()``app/src/main.js` `attachDefaultKinematicsRuntime()` | `docs/traceability-matrix.md``Five-axis kinematics``RTCP/TCP frame`LinuxCNC `xyzac-trt-kins.c``xyzbc-trt-kins.c``trtfuncs.c` | 分离 `desiredFrameSourceMode` 与当前 `frameSourceMode`,异步 worker ready 后自动刷新 frame | 首屏 8 秒内 `sourceMode=source-derived-kinematics-wasm`DOM boundary 为 `linuxcnc_kinematics_wasm_c_abi` |
| D2 | 截图中 preview 黑屏,无可见机床/刀路canvas dataset 不稳定 | `app/src/visualization/five-axis-scene.js` `createScene()``updateToolpathPreview()``renderFallbackPreview()``exposePreviewDataset()``app/src/ui/gmoccapy-shell.js` `renderPreview()` | `docs/implementation-plan.md` 5.4 Visualization/PlaybackLinuxCNC `lib/python/vismach.py`、5-axis vismach GUI screenshots | 修复未定义变量加入常驻机床模型fallback 绘制明显对象,增加 pixel smoke | canvas 非空,`data-three-ready=true``sceneObjects>0`,截图可见机床/刀路/TCP |
| D3 | HOME 后 `X=43/Y=-32.15/Z=-11.306`JOG X+ 后跳到 `X=1/Y=0/Z=0` | `app/src/state/store.js` `JOG``HOME``applyTaskHalStatusPatch()``app/src/runtime/linuxcnc-task-hal-runtime.js` status `ui.axisPose` | LinuxCNC task/motion command model`EMC_JOG_INCR``EMC_JOINT_HOME`;项目 `linuxcnc-task-policy.js` | 为 task/HAL axis pose 增加坐标系元数据pending jog context防止局部坐标覆盖 work pose | HOME 后 JOG X+/Y- 基于当前 DRO 连续增减 |
## 3. 文件级追溯
| 文件 | 当前职责 | 本次修复关注点 | 需要新增测试 |
| --- | --- | --- | --- |
| `app/src/main.js` | app 启动、runtime attach、profile 变更订阅 | kinematics runtime attach 后可靠触发 frame refresh | browser smoke 检查首屏 kinematics ready |
| `app/src/state/store.js` | 全局状态机、RTCP frame、task policy、运行控制 | desired/current frame source 分离JOG 坐标连续性task/HAL status 坐标解析 | node store test + browser operator workflow |
| `app/src/runtime/rtcp-frame.js` | RTCP frame 构建和边界标识 | 确认 source-derived frame 输出 diagnostics 明确 | RTCP frame source smoke |
| `app/src/visualization/five-axis-scene.js` | Three.js/fallback 预览渲染 | 可见机床模型、fallback 非空、dataset 稳定 | canvas pixel smoke |
| `app/src/runtime/linuxcnc-task-hal-runtime.js` | task/HAL runtime adapter 和 status normalize | `axisPoseFrame` / `axisPoseDelta` 输出 | task/HAL JOG status test |
| `app/src/state/linuxcnc-task-policy.js` | LinuxCNC task mode/state gate | 一般不需要改;作为 JOG/HOME gate 参考 | 维持现有 gate tests |
## 4. 测试证据追溯
| 证据文件 | 用途 |
| --- | --- |
| `qa/web-rtcp-5axis-site-test/output/web-rtcp-5axis-site-test-report-2026-06-22.docx` | 人类可读测试报告和截图 |
| `qa/web-rtcp-5axis-site-test/output/site-test-report.json` | 自动化原始 findings、state、console、request 数据 |
| `qa/web-rtcp-5axis-site-test/screenshots/01-home.png` | 首屏预览黑屏和初始界面证据 |
| `qa/web-rtcp-5axis-site-test/screenshots/04-run-state.png` | Run 后预览仍不可见证据 |
| `qa/web-rtcp-5axis-site-test/screenshots/05-local-program-opened.png` | 本地程序导入后预览仍不可见证据 |
| `qa/web-rtcp-5axis-site-test/screenshots/06-after-audit.png` | Audit 后预览仍不可见证据 |
## 5. LinuxCNC 参考追溯
| 能力 | LinuxCNC 参考 | Web 边界 |
| --- | --- | --- |
| TRT kinematics | `src/emc/kinematics/trtfuncs.c``xyzac-trt-kins.c``xyzbc-trt-kins.c` | 只能通过 source-derived WASM frame 使用 |
| switchkins | `src/emc/kinematics/switchkins.c``switchkins.h` | `M428/M429/M430` 和 kins type UI |
| task/JOG/HOME | `src/emc/task/emctaskmain.cc``src/emc/nml_intf/emc.hh` | Web task policy + task/HAL WASM simulation |
| vismach visual model | `lib/python/vismach.py`、5-axis vismach configs/xml | Three.js scene graph reference不移植 Python runtime |
## 6. 后续修复批次记录要求
每一批修复完成后,在 `06-work-log-template.md` 模板基础上追加一份记录,例如:
```text
working1-log-2026-06-22-d1.md
working1-log-2026-06-22-d2.md
working1-log-2026-06-22-d3.md
```
每份记录必须包含:
```text
问题 ID
修改文件
关键代码变更
运行测试
截图/日志证据
剩余风险
```

View File

@@ -0,0 +1,195 @@
# 05 修复验收测试计划
生成时间2026-06-22
## 1. 验收目标
修复完成后,必须证明以下目标同时成立:
1. RTCP frame 自动进入 LinuxCNC kinematics WASM 边界。
2. 3D 预览首屏和程序运行后均可见。
3. HOME 后 JOG 坐标连续。
4. 原已通过功能不回归。
## 2. 本地静态和 Node 测试
### 2.1 构建
```bash
npm --prefix web-rtcp-5axis-sim-plan/app run build
```
预期:
```text
exit code 0
无 TypeScript/ES module 打包错误
```
### 2.2 状态和 runtime smoke
按项目已有测试能力执行:
```bash
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
node web-rtcp-5axis-sim-plan/tests/node/verify_full_execution_boundary.mjs
```
如某个测试依赖本地构建产物缺失,应先按项目原有构建流程补齐,不要跳过。
## 3. 浏览器自动化验收
### 3.1 D1 验收:首屏 kinematics 自动 ready
打开本地或部署页面后等待最多 8 秒,断言:
```js
const state = window.webRtcp5AxisSimulation.getState();
state.kinematicsRuntimeReadiness.loaded === true
state.sourceMode === "source-derived-kinematics-wasm"
state.frameSourceMode === "source-derived-kinematics-wasm"
state.rtcpFrame.semanticBoundary === "linuxcnc_kinematics_wasm_c_abi"
```
DOM 断言:
```js
document.querySelector('[data-rtcp-diagnostic="boundary"]').textContent.includes("linuxcnc_kinematics_wasm_c_abi")
document.querySelector('[data-rtcp-diagnostic="kinematics-ready"]').textContent.includes("ready")
```
禁止状态:
```text
fixture_frame_ui_plumbing_not_linuxcnc_kinematics_proof
```
不得作为稳定态出现。
### 3.2 D2 验收:预览非空
断言 canvas dataset
```js
canvas.dataset.threeReady === "true"
Number(canvas.dataset.threeSceneObjects) > 0
canvas.dataset.threeRenderer === "webgl" || canvas.dataset.threeRenderer === "2d-fallback"
```
像素断言:
```js
const stats = readCanvasPixelStats(canvas)
stats.nonBlackRatio > 0.02
stats.averageLuminance > 5
```
截图人工检查:
```text
能看到工作台/坐标轴/刀具或 TCP 点/路径
```
至少覆盖:
```text
首屏
加载 LinuxCNC vendored 程序后
Run 后
Open 本地 G-code 后
Audit 后
```
### 3.3 D3 验收HOME/JOG 坐标连续
自动化步骤:
```js
click POWER
click MANUAL
click HOME
const home = state.axisPose
click X+
assert state.axisPose.x === home.x + state.machine.jogIncrement
assert state.axisPose.y === home.y
assert state.axisPose.z === home.z
click Y-
assert state.axisPose.y === home.y - state.machine.jogIncrement
assert state.axisPose.x === home.x + state.machine.jogIncrement
```
允许浮点误差:
```text
abs(actual - expected) <= 0.001
```
人工检查:
```text
DRO 中 X/Y/Z 数字连续变化,没有跳到 0/1 局部原点。
```
## 4. 全功能回归清单
修复完成后,至少回归以下功能:
| 功能 | 预期 |
| --- | --- |
| 主界面九大区域 | 全部存在 |
| POWER on/off | taskState 正确 |
| E-STOP/RESET | 急停和复位正确 |
| AUTO/MANUAL/JOG/MDI | 模式切换正确 |
| HOME | allHomed=trueDRO 到 HOME pose |
| JOG X+/X-/Y+/Y- | DRO 连续变化 |
| MDI M428 | RTCP ontcp-xyzac |
| MDI M429 | RTCP offidentity |
| MDI M430 | userk |
| TCP/IDENTITY 侧栏按钮 | RTCP 状态正确 |
| Rapid/Feed/Spindle override | 数值可调 |
| Flood/Mist | 状态可切换 |
| View X/Y/Z/Fit/Clear/Full | preview state 正确,画面仍可见 |
| Profile xyzac/xyzbc 切换 | INI 与 runtime 重新 ready |
| Stage LinuxCNC sources | staged files/gcode 数量正常 |
| Load vendored G-code | programSource 正确G-code 行显示 |
| Run/Pause/Resume/Step/Stop/Reload | runState 和 activeLine 正确 |
| Open local G-code | programSource=operator-file |
| Save/Restore Session | OPFS 保存恢复正确 |
| Audit Full Boundary | 审计入口可执行,结果可诊断 |
## 5. QA 报告复跑
修复后建议复用或更新:
```text
/home/meswork/cnc_wams/qa/web-rtcp-5axis-site-test/run-site-test.mjs
```
重点更新:
1. 首屏等待稳定后,不再把 D1 作为 WARN而是强制 PASS/FAIL。
2. canvas 检查加入像素统计。
3. HOME/JOG 检查使用稳定态并验证连续坐标。
4. 重新生成 Word 报告,文件名建议:
```text
web-rtcp-5axis-site-test-report-2026-06-22-after-working1-fix.docx
```
## 6. 验收通过定义
```text
P0: D1 PASS
P0: D2 PASS
P1: D3 PASS
全功能回归无新增 FAIL
console error = 0
page error = 0
request failure = 0
```
如果 headless WebGL 不稳定,必须证明 2D fallback 可见且自动化 pixel 检查通过。

View File

@@ -0,0 +1,100 @@
# 06 修复工作记录模板
生成时间2026-06-22
后续每一轮修复建议复制本模板,新建独立记录文件。
文件命名建议:
```text
working1-log-YYYY-MM-DD-D1.md
working1-log-YYYY-MM-DD-D2.md
working1-log-YYYY-MM-DD-D3.md
```
## Batch
```text
Batch:
Date:
Owner:
Problem IDs:
Branch:
Commit:
```
## 1. 修复目标
```text
本轮要修复什么问题:
预期用户可见结果:
预期 runtime/DOM 诊断结果:
```
## 2. 修改文件
| 文件 | 修改内容 | 原因 |
| --- | --- | --- |
| | | |
## 3. 关键实现说明
```text
状态字段变化:
核心函数变化:
runtime 边界变化:
UI/可视化变化:
```
## 4. LinuxCNC/项目溯源
```text
参考 LinuxCNC 源码或配置:
参考项目文档:
为什么该修复没有重写 CNC 语义:
```
## 5. 测试记录
### 5.1 命令
```bash
# paste commands here
```
### 5.2 结果
```text
PASS/FAIL:
关键输出:
截图路径:
JSON/日志路径:
```
## 6. 回归范围
| 功能 | 是否回归 | 结果 |
| --- | --- | --- |
| D1 kinematics auto refresh | | |
| D2 preview visible | | |
| D3 jog continuity | | |
| POWER/E-STOP/RESET | | |
| MDI M428/M429/M430 | | |
| Load/Run/Step/Stop | | |
| Save/Restore Session | | |
## 7. 剩余风险
```text
仍未解决的问题:
需要后续确认的问题:
可能影响线上部署的问题:
```
## 8. 下一步
```text
Next:
Blockers:
```

View File

@@ -0,0 +1,45 @@
# web-rtcp-5axis-sim-plan working1 修复指导目录
生成时间2026-06-22
本目录用于指导修复测试报告中确认的问题:
```text
/home/meswork/cnc_wams/qa/web-rtcp-5axis-site-test/output/web-rtcp-5axis-site-test-report-2026-06-22.docx
```
本目录只提供修复设计、实施步骤、溯源和验收文档,不直接修改程序代码。后续开发人员可按本目录文件逐步修复本地项目。
## 文档清单
| 文件 | 用途 |
| --- | --- |
| `01-problem-review.md` | 测试问题复盘、证据、影响范围和初步根因 |
| `02-repair-plan.md` | 总体修复方案、优先级、风险和代码落点 |
| `03-implementation-steps.md` | 程序修改的详细步骤,按问题拆分到具体文件和函数 |
| `04-traceability-matrix.md` | 问题、测试证据、源码、LinuxCNC 参考、修复项、验收项的追溯矩阵 |
| `05-acceptance-test-plan.md` | 修复完成后的本地/浏览器/线上回归验收清单 |
| `06-work-log-template.md` | 后续每轮修复过程记录模板 |
## 本轮确认问题
| ID | 问题 | 严重级别 | 修复优先级 |
| --- | --- | --- | --- |
| D1 | 首屏 RTCP/运动学边界未自动挂接 LinuxCNC kinematics | 高 | P0 |
| D2 | 3D 预览画布存在,但未观察到机床/刀路可见对象 | 中 | P0 |
| D3 | HOME 后 JOG 坐标连续性异常 | 中 | P1 |
## 修复目标
1. 页面稳定加载后RTCP frame 自动进入 `source-derived-kinematics-wasm`,边界显示 `linuxcnc_kinematics_wasm_c_abi`
2. 3D 预览在 WebGL 和 2D fallback 两条路径下均可见,至少能看到机床基准、刀具/TCP 标记和路径。
3. HOME 后执行 JOGDRO 坐标必须在当前显示坐标基础上连续变化,不允许跳到另一套坐标原点。
4. 修复后新增或更新自动化测试,防止同类问题回归。
## 推荐执行顺序
1. 先修 D1确保 LinuxCNC kinematics frame 自动刷新链路稳定。
2. 再修 D2确保视觉层有可见基础机床对象和路径对象。
3. 最后修 D3统一 task/HAL runtime 反馈坐标系与 UI DRO 坐标系。
4. 执行 `05-acceptance-test-plan.md` 中的本地和浏览器验收。

View File

@@ -0,0 +1,146 @@
# working1 修复执行记录 D1-D2-D3
生成时间2026-06-22
## Batch
```text
Batch: working1-implementation-d1-d2-d3
Date: 2026-06-22
Owner: Codex
Problem IDs: D1, D2, D3
Branch: current worktree
Commit: not committed
```
## 1. 修复目标
```text
D1: kinematics worker ready 后,页面自动保持 LinuxCNC source-derived kinematics frame不被临时 fixture frame 永久覆盖。
D2: 首屏和路径清空后仍显示可见五轴参考机床、坐标轴、旋转轴、刀具/TCPcanvas dataset 和像素测试可验证。
D3: HOME 后执行 task/HAL JOGDRO/work pose 保持连续增量,不被 task-local/local-zero 轴位覆盖。
```
## 2. 修改文件
| 文件 | 修改内容 | 原因 |
| --- | --- | --- |
| `app/src/state/store.js` | 增加 `desiredFrameSourceMode``pendingJogCommand`;调整 async kinematics refresh新增 `resolveTaskHalAxisPose()` | 分离“期望 frame source”和“当前 frame source”保护 HOME 后 JOG 坐标连续性 |
| `app/src/main.js` | profile kinematics runtime attach 完成后再次调用 `refreshKinematicsFrame()` | 满足 profile/INI 变更后的自动 frame 刷新要求 |
| `app/src/runtime/linuxcnc-task-hal-runtime.js` | `ui.axisPoseFrame` 标记 JOG 为 `task-local`,非 JOG 为 `work` | 让 store 能区分 task/HAL 轴位坐标系 |
| `app/src/visualization/five-axis-scene.js` | 新增 WebGL 五轴参考机床模型和 2D fallback 参考绘制;路径清空时仍显示 TCP | 修复 3D 预览黑屏/不可见和 fallback 信息不足 |
| `tests/browser/gmoccapy_shell_smoke.html` | 更新 reference model dataset 断言;新增 canvas pixel stats 检查 | 防止只通过 DOM dataset 误判可见性 |
| `tests/node/verify_linuxcnc_task_hal_runtime.mjs` | 增加 HOME 后 JOG X+/Y- 连续性断言 | 覆盖 D3 复现路径 |
## 3. 关键实现说明
```text
状态字段变化:
- desiredFrameSourceMode: 保留用户/运行时希望的 frame source。
- pendingJogCommand: 记录 task/HAL JOG 的 axis、direction、increment、basePose。
核心函数变化:
- buildFrameForState(): 优先读取 desiredFrameSourceMode。
- scheduleAsyncKinematicsRefresh(): 以 desiredFrameSourceMode + runtime loaded 作为刷新条件。
- refreshAsyncKinematicsFrame(): 成功后写回 source/frame/desired 均为 source-derived-kinematics-wasm失败显示 operatorMessage 并保留重试能力。
- resolveTaskHalAxisPose(): work pose 直接合并axis delta 累加pending JOG 用 basePose + increment。
UI/可视化变化:
- WebGL scene 增加 table/base、XYZ axis、A/C rotary ring、tool holder。
- 2D fallback 始终绘制工作台、XYZ 轴、旋转中心、TCP 点。
- canvas dataset 输出 machine-reference-and-toolpath / webgl-five-axis-reference。
```
## 4. LinuxCNC/项目溯源
```text
参考项目文档:
- working1/03-implementation-steps.md
- working1/04-traceability-matrix.md
- docs/traceability-matrix.md
参考 LinuxCNC 边界:
- kinematics frame 仍来自 LinuxCNC kinematics WASM C ABI。
- task/HAL JOG 仍通过 EMC_JOG_INCR 模拟 runtime 执行UI 只处理坐标系归并,不重写 CNC 运动语义。
- Three.js 只消费 runtime frame/canonical motion/task-HAL feedback不生成 G-code 或 CNC 语义。
```
## 5. 测试记录
### 5.1 已通过命令
```bash
npm --prefix web-rtcp-5axis-sim-plan/app run build
node web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_task_hal_runtime.mjs
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
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
```
### 5.2 关键输出
```text
gmoccapy_static_build=ok
linuxcnc_task_hal_runtime_smoke=ok
rtcp_store_smoke=ok
five_axis_session_smoke=ok
linuxcnc_kinematics_runtime_smoke=ok
gmoccapy_shell_smoke=ok
gmoccapy_dist_smoke=ok
```
### 5.3 未通过/阻塞命令
```bash
npm --prefix web-rtcp-5axis-sim-plan/app run smoke:node
```
结果:
```text
前置 kinematics/interpreter/INI/task-HAL 均通过。
失败位置: verify_native_task_hal_audit.mjs -> wasm-port/tests/native/verify_task_hal_phase0.sh
失败原因: verify_task_hal_source_manifest.sh 返回非零manifest ready=0。
证据: wasm-port/build/task-hal/verify_task_hal_source_manifest.stdout.log
关键字段:
- task_hal_reference_source_ready=0
- task_hal_vendor_source_ready=0
- task_hal_missing_reference_source_count=20
```
该失败属于 native LinuxCNC reference source/probe 前置条件,不是本轮 Web D1/D2/D3 代码回归。
## 6. 回归范围
| 功能 | 是否回归 | 结果 |
| --- | --- | --- |
| D1 kinematics auto refresh | 是 | PASS |
| D2 preview visible | 是 | PASS |
| D3 jog continuity | 是 | PASS |
| POWER/E-STOP/RESET | 是 | PASS via browser smoke |
| MDI M428/M429/M430 | 是 | PASS via browser/node smoke |
| Load/Run/Step/Stop | 是 | PASS via browser smoke |
| Save/Restore Session | 是 | PASS via browser/node smoke |
| Native source manifest/probe | 是 | BLOCKED: missing native reference sources |
## 7. 剩余风险
```text
1. native task/HAL source manifest 仍未 ready缺少 20 个 native reference source完整 smoke:node 不能全绿。
2. WebGL reference model 已通过 smoke但仍建议后续保留人工截图复核首屏和 clear-preview 后视觉效果。
3. task/HAL axisPoseFrame 当前按 JOG/non-JOG 推断;长期方案仍应由 runtime 输出更细的 work/task-local/axisPoseDelta 元数据。
```
## 8. 下一步
```text
Next:
- 补齐或恢复 wasm-port/tools/task-hal-source-manifest.txt 对应的 native LinuxCNC reference sources。
- native source manifest ready 后重跑 npm --prefix web-rtcp-5axis-sim-plan/app run smoke:node。
- 部署到 https://82.156.24.101:8092/ 后重跑 QA 站点测试,更新 Word 测试报告。
Blockers:
- wasm-port/build/task-hal/verify_task_hal_source_manifest.stdout.log 显示 native reference source 缺失。
```