补齐 xyzbc-trt Web 路径证据
This commit is contained in:
@@ -3601,7 +3601,7 @@ verifyExpectedOutput(
|
|||||||
"file_execute_count=41",
|
"file_execute_count=41",
|
||||||
"file_saw_error=0",
|
"file_saw_error=0",
|
||||||
"canon_event=SET_G5X_OFFSET index=1 x=1 y=2 z=-3",
|
"canon_event=SET_G5X_OFFSET index=1 x=1 y=2 z=-3",
|
||||||
"canon_event=SET_G92_OFFSET x=-41.1962 y=-46.1962 z=-72",
|
"canon_event=SET_G92_OFFSET x=-43.0622 y=-47.4282 z=-72",
|
||||||
"canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=-4",
|
"canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=-4",
|
||||||
"canon_event=MESSAGE: -101.600000",
|
"canon_event=MESSAGE: -101.600000",
|
||||||
"canon_event=PROGRAM_END",
|
"canon_event=PROGRAM_END",
|
||||||
|
|||||||
@@ -10583,3 +10583,156 @@ nginx: configuration file /etc/nginx/nginx.conf test is successful
|
|||||||
- 提交内容摘要:该提交包含 274 个文件变化,新增 `web-rtcp-5axis-xyzbc-trt-sim-plan/` 项目文件、测试、文档、截图、证据文件以及 `xyzbc.var` 等内容,共 157294 行新增、1394 行删除。
|
- 提交内容摘要:该提交包含 274 个文件变化,新增 `web-rtcp-5axis-xyzbc-trt-sim-plan/` 项目文件、测试、文档、截图、证据文件以及 `xyzbc.var` 等内容,共 157294 行新增、1394 行删除。
|
||||||
- 推送执行:执行 `git push origin master`,推送成功,远端 `master` 从 `83075c4` 更新到 `c4a721f`。
|
- 推送执行:执行 `git push origin master`,推送成功,远端 `master` 从 `83075c4` 更新到 `c4a721f`。
|
||||||
- 后续处理:根据工作区 AGENTS.md 规则,将本次 GPT/Codex 执行过程以中文追加到本日志文件,并准备作为单独日志提交继续推送到云仓库。
|
- 后续处理:根据工作区 AGENTS.md 规则,将本次 GPT/Codex 执行过程以中文追加到本日志文件,并准备作为单独日志提交继续推送到云仓库。
|
||||||
|
|
||||||
|
## 2026-07-02 08:17:36 EDT - 按 xyzbc-trt working 继续编写程序
|
||||||
|
|
||||||
|
1. 收到请求:按 `/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working` 开始编写程序。
|
||||||
|
2. 读取工作区 AGENTS.md 要求,确认每轮结束必须将完整执行过程用中文追加到 `/home/mes123456/cnc_wams/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md`。
|
||||||
|
3. 检查 `web-rtcp-5axis-xyzbc-trt-sim-plan/working`,确认已有 01-09 工作文档、evidence JSON 和截图证据。
|
||||||
|
4. 读取 `working/README.md`、`01-项目功能内容.md`、`02-项目程序开发详细步骤.md`、`04-任务矩阵.md`,确认基础 app 已存在,未完成重点为 pathSampling、previewPath、executionPath、pathComparison、全量 evidence 字段、POSTGUI HAL、Ngcgui、演示程序全集、kinematics pins、TRAJ/AXIS/JOINT 限制和新 native 基线重采集。
|
||||||
|
5. 检查目标项目源码结构,读取 `tools/collect-web-xyzbc-trt-evidence.mjs`、`tools/compare-xyzbc-trt-evidence.mjs`、`tools/collect-native-xyzbc-trt-evidence.py`,发现 Web/native/compare 脚本尚未实现完整 path 字段和全量对标字段,native 脚本默认路径仍指向历史 `/home/mes123456/linuxcnc-master`。
|
||||||
|
6. 读取 `app/src/profiles/xyzbc-trt.js`、`app/src/runtime/linuxcnc-ini-runtime.js`、`app/src/runtime/linuxcnc-machine-file-staging.js`、`tests/node/verify_xyzbc_trt_web_app.mjs`,确认 profile 已覆盖 XYZBC、M428/M429/M430、HAL pins、Vismach feedback nets 和 OPFS staging,但 `samplePrograms` 仍包含旧的 `3D_Chips.ngc`,未把 `boat-xyzbc.ngc` 明确作为 xyzbc-trt 演示程序。
|
||||||
|
7. 修改 `app/src/profiles/xyzbc-trt.js`,将 `samplePrograms` 调整为 `xyzbc_switchkins.ngc` 与 `boat-xyzbc.ngc`,对齐 `working` 的演示程序全集要求。
|
||||||
|
8. 修改 `tools/collect-web-xyzbc-trt-evidence.mjs`,新增统一 `SAMPLE_PERIOD_MS = 20`,并向 Web evidence 写入 `pathSampling`、`previewPath`、`executionPath`。
|
||||||
|
9. 在 Web evidence 中新增全量对标字段:`startupSequence`、`iniDisplay`、`halNets`、`kinematicsPins`、`axisJointLimits`、`switchkinsTransitions`、`uiEquivalence`、`vismachEquivalent`、`ngcguiSubroutines`、`demoPrograms`。
|
||||||
|
10. 在 Web evidence 的 `coverage` 中新增 `boatProgramStaged`、`ngcguiSubroutinesStaged`、`postguiHalEquivalent`、`kinematicsPinsCovered`、`axisJointLimitsCovered`、`previewPathAvailable`、`executionPathAvailable` 等检查项。
|
||||||
|
11. Web path 采集逻辑设计为:如果缺少 `wasm-port/build/wasm/core/linuxcnc_interp.js/.wasm`,则 `previewPath` 明确写入 blocked;如果缺 task/HAL artifact,则 `executionPath` 明确写入 blocked,不用占位样本伪造通过。
|
||||||
|
12. 修改 `tools/compare-xyzbc-trt-evidence.mjs`,新增 `pathComparison`,覆盖 `previewVsPreview`、`executionVsExecution`、`previewVsExecutionNative`、`previewVsExecutionWeb`。
|
||||||
|
13. 在 compare 中新增路径检查:采样周期必须为 20ms、native/Web preview path 必须有样本、native/Web execution path 必须有样本、native preview/execution 和 Web preview/execution 必须可比。
|
||||||
|
14. 在 compare 中新增误差统计字段:`maxTcpErrorMm`、`rmsTcpErrorMm`、`maxJointError`、`rmsJointError`、`maxToolAxisAngleDeg`、`sampleCountDelta`、`missingSamples`。
|
||||||
|
15. 修改 `tools/collect-native-xyzbc-trt-evidence.py`,将默认 source root 切换为 `/home/mes123456/cnc_wams/linuxcnc`,默认 INI 和默认程序均指向该路径。
|
||||||
|
16. 在 native 脚本中新增 `pathSampling`、`previewPath`、`executionPath` 字段;其中 preview path 当前明确 blocked,execution path 可在 `--run` 捕获 `stat()` 事件后重采样为 20ms 样本。
|
||||||
|
17. 在 native 脚本中新增 `sourceRoot`、`startupSequence`、`halNets`、`kinematicsPins`,并扩展 HAL pin 采集,包含 `x-rot-point`、`y-rot-point`、`z-rot-point`、`conventional-directions`。
|
||||||
|
18. 扩展 native 执行事件,记录 `velocity`、`feedrate`、`spindle`,供执行路径样本字段使用。
|
||||||
|
19. 修改 `tests/node/verify_xyzbc_trt_web_app.mjs`,新增断言:profile `samplePrograms` 必须是 `xyzbc_switchkins.ngc` 和 `boat-xyzbc.ngc`;staging 必须包含 `xyzbc_switchkins_sub.ngc`、`centering.ngc`、`helix_bc.ngc`。
|
||||||
|
20. 执行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node`,通过,输出 `xyzbc_trt_web_app_smoke=ok`。
|
||||||
|
21. 执行 `python3 -m py_compile web-rtcp-5axis-xyzbc-trt-sim-plan/tools/collect-native-xyzbc-trt-evidence.py`,通过,说明 native 采集脚本语法正确。
|
||||||
|
22. 执行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web`,成功生成 `working/evidence/web-xyzbc-trt-evidence.json`。
|
||||||
|
23. 检查 Web evidence,确认 `pathSampling.samplePeriodMs = 20`,`previewPath.samplePeriodMs = 20`,`executionPath.samplePeriodMs = 20`,并确认 `startupSequence`、`halNets`、`ngcguiSubroutines`、`demoPrograms` 等字段已写入。
|
||||||
|
24. 执行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare`,成功生成 `working/evidence/compare-xyzbc-trt-evidence.json`,状态为真实 fail,`fail_count=7`。
|
||||||
|
25. compare 结果为 23 项检查中 16 项通过,7 项失败均指向当前真实缺口:缺少 `wasm-port/build/wasm` artifact,以及 native/Web preview/execution path 样本尚不可比。
|
||||||
|
26. 执行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run build`,失败于 `ENOENT: no such file or directory, scandir '/home/mes123456/cnc_wams/wasm-port/build/wasm/kinematics'`。
|
||||||
|
27. 读取 `app/scripts/build-static.mjs`,确认构建脚本要求真实复制 kinematics/core/tp/task-hal WASM 产物;决定不把 build 改为静默通过,避免掩盖 artifact 缺口。
|
||||||
|
28. 更新 `working/04-任务矩阵.md`,将 T-017、T-020、T-021、T-023、T-024、T-027、T-030、T-031、T-032、T-033、T-035、T-037 等状态同步为完成、部分完成或待前置。
|
||||||
|
29. 更新 `working/03-推进台账.md`,新增本轮“evidence 字段与路径对比实现轮次”,记录目标、已做事项、修改文件、验证情况和下一步。
|
||||||
|
30. 更新 `working/05-验收证据.md`,新增 evidence 字段与路径对比实现验收章节,记录 smoke、py_compile、evidence:web、evidence:compare 和 build 的结果。
|
||||||
|
31. 更新 `working/06-决策记录.md`,新增 D-013,明确缺少 WASM artifact 时 build 和 path compare 继续显式失败,不伪造通过。
|
||||||
|
32. 执行 `git diff --check -- web-rtcp-5axis-xyzbc-trt-sim-plan`,无输出,说明当前修改没有 whitespace error。
|
||||||
|
33. 检查 Git 状态,发现运行失败 build 产生了未跟踪 `app/dist/`,运行 py_compile 产生了未跟踪 `tools/__pycache__/`。
|
||||||
|
34. 删除本轮产生的临时目录 `web-rtcp-5axis-xyzbc-trt-sim-plan/app/dist` 和 `web-rtcp-5axis-xyzbc-trt-sim-plan/tools/__pycache__`。
|
||||||
|
35. 再次检查状态,确认本轮保留的改动为 profile、测试、三个 evidence/compare 脚本、working 文档、Web/compare evidence JSON。
|
||||||
|
36. 检查 4174 端口,未发现占用。
|
||||||
|
37. 启动目标 Web 开发服务器:在 `web-rtcp-5axis-xyzbc-trt-sim-plan/app` 执行 `npm run dev`,当前服务运行在 `http://127.0.0.1:4174/`。
|
||||||
|
38. 本轮结论:已经按 working 要求继续编写程序,完成了 Web/native evidence 字段扩展、路径对比框架、profile 演示程序修正、Node smoke 扩展和文档同步;当前剩余阻塞仍是需要构建 `wasm-port/build/wasm` artifact 并用 `/home/mes123456/cnc_wams/linuxcnc` 重新采集 native evidence。
|
||||||
|
|
||||||
|
## 2026-07-02 08:25 EDT - 生成 wasm-port WASM artifact
|
||||||
|
|
||||||
|
1. 收到用户请求:生成 WASM artifact。
|
||||||
|
2. 读取 `wasm-port/README.md`、`wasm-port/AGENTS.md` 和构建脚本,确认必须在 `wasm-port/build/wasm` 下生成产物,不能修改 upstream LinuxCNC 源码,构建脚本为增量式。
|
||||||
|
3. 检查 `wasm-port/build/wasm`,确认此前没有目标 `.js/.wasm` 产物。
|
||||||
|
4. 首次直接执行构建命令失败且无输出,定位原因为当前 shell 未加载 Emscripten 环境,`command -v emcc` 不可用。
|
||||||
|
5. 搜索到 Emscripten 环境脚本 `/home/mes123456/emsdk/emsdk_env.sh` 和编译器 `/home/mes123456/emsdk/upstream/emscripten/emcc`。
|
||||||
|
6. 显式执行 `source /home/mes123456/emsdk/emsdk_env.sh`,确认 `emcc 6.0.2` 可用。
|
||||||
|
7. 执行 `wasm-port/tools/build_wasm_core.sh`,生成 interpreter core artifact。
|
||||||
|
8. 执行 `wasm-port/tools/build_kinematics_wasm.sh`,生成 kinematics artifact,其中包含目标 `linuxcnc_xyzbc_trt_kinematics.js/.wasm`。
|
||||||
|
9. 执行 `wasm-port/tools/build_tp_wasm.sh`,生成 trajectory planner artifact。
|
||||||
|
10. 执行 `wasm-port/tools/build_task_hal_wasm.sh`,生成 task/HAL artifact,脚本输出 `linuxcnc_task_hal_wasm_build=ok`。
|
||||||
|
11. 核对目标产物存在:
|
||||||
|
- `wasm-port/build/wasm/core/linuxcnc_interp.js`
|
||||||
|
- `wasm-port/build/wasm/core/linuxcnc_interp.wasm`
|
||||||
|
- `wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.js`
|
||||||
|
- `wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.wasm`
|
||||||
|
- `wasm-port/build/wasm/tp/linuxcnc_tp.js`
|
||||||
|
- `wasm-port/build/wasm/tp/linuxcnc_tp.wasm`
|
||||||
|
- `wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js`
|
||||||
|
- `wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm`
|
||||||
|
12. 统计 `wasm-port/build/wasm` 下共有 40 个 `.js/.wasm` artifact。
|
||||||
|
13. 查看构建 stderr 日志,确认主要为既有 warning,例如 `rs274ngc_pre.cc` 的格式化 warning 和 shim 中 C23 参数名 warning,没有导致构建失败。
|
||||||
|
14. 执行 `SKIP_KINEMATICS_BUILD=1 wasm-port/tests/wasm/node/verify_kinematics_wasm.sh`,通过,输出 `kinematics_wasm_node_smoke=ok`,其中包含 `xyzbc-trt` kinematics smoke。
|
||||||
|
15. 执行 `SKIP_TP_BUILD=1 wasm-port/tests/wasm/node/verify_tp_wasm.sh`,通过,输出 `tp_wasm_node_smoke=ok`。
|
||||||
|
16. 执行 `SKIP_TASK_HAL_BUILD=1 wasm-port/tests/wasm/node/verify_task_hal_wasm.sh`,通过,输出 `linuxcnc_task_runtime_smoke=ok`、`task_status_from_linuxcnc_runtime=ok`、`task_commands_drive_motion_runtime=ok`、`mdi_jog_task_motion_hal_sync=ok`。
|
||||||
|
17. 执行 `SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh`,产物被加载,但测试失败于 `interp_g10_l11_wasm`,断言期望 `canon_event=SET_G92_OFFSET x=-41.1962 y=-46.1962 z=-72`;因此 interpreter/remap 完整验收仍未完成。
|
||||||
|
18. 在目标项目执行 `node tests/node/verify_xyzbc_trt_web_app.mjs`,通过,输出 `xyzbc_trt_web_app_smoke=ok`。
|
||||||
|
19. 执行 `node tools/collect-web-xyzbc-trt-evidence.mjs`,重新生成 `working/evidence/web-xyzbc-trt-evidence.json`。
|
||||||
|
20. 检查 Web evidence,确认 `status=ready-for-wasm-runtime`、`coverage.wasmArtifactsReady=true`、`wasm.missing=[]`。
|
||||||
|
21. 检查 Web evidence,确认 `previewPath.sampleCount=6174`,说明 Web preview path 已由 `linuxcnc_interp` WASM 生成 20ms 样本。
|
||||||
|
22. 检查 Web evidence,确认剩余 blocker 为 `web-execution-path-unavailable`,原因是 task/HAL execution path collector 尚未实现。
|
||||||
|
23. 执行 `node tools/compare-xyzbc-trt-evidence.mjs`,重新生成 `working/evidence/compare-xyzbc-trt-evidence.json`,结果为 `fail_count=6`。
|
||||||
|
24. 检查 compare summary,确认当前 23 项检查中 17 项通过,剩余失败项均与 native preview/execution path 样本缺失或 Web execution path 样本缺失有关,不再包含缺 WASM artifact。
|
||||||
|
25. 更新 `working/04-任务矩阵.md`:将 T-018 标记为完成,将 T-011 和 T-013 标记为完成,将 T-012 标记为部分完成,将 T-021 标记为完成,并同步 T-017/T-022 状态。
|
||||||
|
26. 更新 `working/03-推进台账.md`:新增“生成 WASM artifact”轮次,记录构建命令、产物、验证结果和后续事项。
|
||||||
|
27. 更新 `working/05-验收证据.md`:新增 WASM artifact 验收记录,列出目标 artifact、通过的验证命令和未通过的 interpreter 回归项。
|
||||||
|
28. 更新 `working/06-决策记录.md`:新增 D-012,明确 artifact 已生成,后续 blocker 改为路径采集与 interpreter 回归。
|
||||||
|
29. 核对 Git 状态,确认 `wasm-port/build/wasm` 产物未显示为跟踪改动,说明它们属于生成/忽略产物;本轮保留的跟踪改动为 evidence JSON 与 working 文档。
|
||||||
|
30. 本轮结论:WASM artifact 已生成,Web evidence 中 artifact 缺失 blocker 已消除;kinematics、TP、task/HAL 验证通过;interpreter 仍有 `interp_g10_l11_wasm` 回归断言失败,native/Web path 对比仍需补齐 native path 重采集和 Web execution path collector。
|
||||||
|
|
||||||
|
## 2026-07-02 08:33 EDT - 完整 interpreter/remap 验收
|
||||||
|
|
||||||
|
1. 收到用户请求:完整 interpreter/remap 验收。
|
||||||
|
2. 检查 `wasm-port/tests/wasm/node/verify_interp_wasm.mjs`,定位上一轮失败点为 `interp_g10_l11_wasm`。
|
||||||
|
3. 读取 `verifyExpectedOutput()` 逻辑,确认该测试逐行检查 output 是否包含 expected 字符串。
|
||||||
|
4. 读取 `g10-l11` 相关测试代码,确认它通过 `writeG10RegressionFiles("g10-l11", ["test.ngc", "test.tbl"])` 写入 `test.ngc`、`test.tbl` 和最小 `test.ini`,再调用 `interp.runFileWithIni()`。
|
||||||
|
5. 读取 `wasm-port/vendor/linuxcnc/tests/interp/g10/g10-l11/test.ngc` 和 `test.tbl`,确认用例覆盖 G10 L2、G10 L11、G92、G54/G55/G56 和工具长度补偿。
|
||||||
|
6. 编写一次性 Node ESM 调试命令,使用 `createLinuxCncInterpSdk()` 单独 staging 并运行 `g10-l11`,打印实际 `SET_G92_OFFSET`、`SET_G5X_OFFSET`、`USE_TOOL_LENGTH_OFFSET`、`MESSAGE` 和 file 状态。
|
||||||
|
7. 调试输出显示 WASM 实际 canonical 事件包含 `SET_G92_OFFSET x=-43.0622 y=-47.4282 z=-72`,而不是旧断言中的 `x=-41.1962 y=-46.1962 z=-72`。
|
||||||
|
8. 读取 `wasm-port/vendor/linuxcnc/tests/interp/g10/g10-l11/expected`,确认 vendored LinuxCNC 当前 expected 同样为 `SET_G92_OFFSET(-43.0622, -47.4282, -72.0000)`。
|
||||||
|
9. 判断失败原因:WASM interpreter/remap 行为与 vendored LinuxCNC expected 一致,Node 验收脚本中的 G10 L11 期望值过期。
|
||||||
|
10. 修改 `wasm-port/tests/wasm/node/verify_interp_wasm.mjs`,将 `interp_g10_l11_wasm` 的 expected 从 `x=-41.1962 y=-46.1962 z=-72` 更新为 `x=-43.0622 y=-47.4282 z=-72`。
|
||||||
|
11. 执行 `source /home/mes123456/emsdk/emsdk_env.sh` 后运行 `SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh`。
|
||||||
|
12. 现有 artifact 验证通过,输出 `interp_wasm_node_smoke=ok`。
|
||||||
|
13. 同一命令输出中仍有既有提示 `link (updating variable file): No such file or directory`,以及 `G88.1`、`M410` remap modalgroup warning;这些提示未影响退出码。
|
||||||
|
14. 继续运行完整入口 `wasm-port/tests/wasm/node/verify_interp_wasm.sh`,不设置 `SKIP_INTERP_BUILD`,确认增量构建入口和完整 Node 验收均可通过。
|
||||||
|
15. 完整入口同样输出 `interp_wasm_node_smoke=ok`,退出码为 0。
|
||||||
|
16. 更新 `web-rtcp-5axis-xyzbc-trt-sim-plan/working/04-任务矩阵.md`,将 T-012 “完整 interpreter/remap WASM 验证”标记为完成。
|
||||||
|
17. 更新 `working/03-推进台账.md`,新增“完整 interpreter/remap 验收”轮次,记录复现、对标 vendored expected、修改内容、验证命令和下一步。
|
||||||
|
18. 同步修改 `working/03-推进台账.md` 中上一轮历史状态描述,避免继续显示该项仍未修复。
|
||||||
|
19. 更新 `working/05-验收证据.md`,把 interpreter/remap 从未通过项改为通过项,并记录 `SKIP_INTERP_BUILD=1` 与完整入口两条命令都输出 `interp_wasm_node_smoke=ok`。
|
||||||
|
20. 更新 `working/06-决策记录.md`,新增 D-013,说明 G10 L11 期望值对齐 vendored LinuxCNC expected,而不是修改 WASM 运行逻辑。
|
||||||
|
21. 执行 `git diff --check`,检查 `verify_interp_wasm.mjs` 和本轮 working 文档,没有 whitespace error。
|
||||||
|
22. 检查 Git 状态,确认本轮新增跟踪改动为 `wasm-port/tests/wasm/node/verify_interp_wasm.mjs` 与 working 文档;已有 Web profile、evidence 脚本、evidence JSON 等改动属于前序轮次保留状态。
|
||||||
|
23. 本轮结论:完整 interpreter/remap WASM 验收已通过;T-012 已完成。剩余项目级 compare 失败仍集中在 native preview/execution path 重采集和 Web task/HAL execution path collector。
|
||||||
|
|
||||||
|
## 2026-07-02 - Web task/HAL executionPath 采集实现
|
||||||
|
|
||||||
|
1. 收到用户请求:接续上一轮,按 `/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working` 持续编写程序。
|
||||||
|
2. 读取工作区文件列表、Git 状态和 `working` 目录文档,确认当前项目已有未提交改动,应作为上一轮成果继续使用,不回退。
|
||||||
|
3. 读取 `working/03-推进台账.md`、`04-任务矩阵.md`、`05-验收证据.md`、`02-项目程序开发详细步骤.md`,确认当前主要缺口为 Web execution path 采集、native path 重采集和浏览器 smoke。
|
||||||
|
4. 读取 `tools/collect-web-xyzbc-trt-evidence.mjs`、`tools/compare-xyzbc-trt-evidence.mjs` 和 `app/src/profiles/xyzbc-trt.js`,确认 Web evidence 已有 previewPath 占位和 executionPath 占位,compare 已检查 pathComparison。
|
||||||
|
5. 检查 `wasm-port/build/wasm`,确认当前已存在 `linuxcnc_interp.js/.wasm`、`linuxcnc_xyzbc_trt_kinematics.js/.wasm`、`linuxcnc_tp.js/.wasm`、`linuxcnc_task_hal.js/.wasm`。
|
||||||
|
6. 读取 `app/src/runtime/linuxcnc-task-hal-runtime.js`、`linuxcnc-task-hal-worker-client.js`、`wasm-port/runtime/sdk/src/linuxcnc-task-hal.js` 和相关 Node 测试,确认 task/HAL runtime 支持 `initSession`、`stageFiles`、`openProgram`、`loadProgramMotionPlan`、`sendCommand`、`runCycles`、`readStatus`。
|
||||||
|
7. 运行一次 Node ESM 探针,使用 `createLinuxCncInterpreterRuntime()` 加载 `xyzbc_switchkins.ngc`,确认 interpreter 生成 29 个 motion 事件、TP planner 生成 6174 个原始样本,总时长约 61.461 秒。
|
||||||
|
8. 修改 `tools/collect-web-xyzbc-trt-evidence.mjs`:在 `collectPathEvidence()` 中将 executionPath 从占位错误改为调用 `pathFromTaskHalExecution()`。
|
||||||
|
9. 新增 `pathFromTaskHalExecution()`:复用 interpreter motion 和 planner timing,创建 task/HAL session,stage 机器文件,open 程序,load motion plan,发送 `EMC_TASK_SET_STATE ON`、`EMC_TASK_SET_MODE AUTO`、`EMC_TASK_PLAN_RUN`,按 20ms task cycle 读取状态并生成 executionPath 样本。
|
||||||
|
10. 新增 `xyzbcAxesFromTaskHalStatus()`,显式按 `joint.0/1/2/3/4` 映射 `X/Y/Z/B/C`,避免通用 XYZAC UI 字段把 B/C 轴写错。
|
||||||
|
11. 新增 `spindleFromTaskHalStatus()`、`currentMotionTypeForLine()`、`firstFiniteNumber()` 等 helper,用于从 task/HAL status 归一化 path 样本。
|
||||||
|
12. 首次运行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web`,生成 Web evidence 成功,确认 `executionPath.sampleCount=569`、`source=web-linuxcnc-task-hal-execution`、`taskHal.completed=true`、`blockers=[]`。
|
||||||
|
13. 运行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare`,compare 结果为 `fail_count=5`,确认 Web execution path blocker 已消失,剩余失败均来自 native path 样本缺失。
|
||||||
|
14. 运行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node`,通过,输出 `xyzbc_trt_web_app_smoke=ok`。
|
||||||
|
15. 运行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run build`,通过,输出 `gmoccapy_static_build=ok`,同时产生未跟踪构建目录 `app/dist/`。
|
||||||
|
16. 发现 Web previewPath 原先标记为 20ms,但实际使用 TP 1ms 原始样本;判断这是证据质量问题,需要在采集脚本中真实重采样。
|
||||||
|
17. 修改 `pathFromWebMotion()`,新增 `resamplePlannerSamples()` 和 `interpolatePlannerSample()`,将 TP planner 原始样本按 20ms 重新采样后再写入 `previewPath.samples`。
|
||||||
|
18. 新增 `lerpNumber()`,用于预览路径重采样时线性插值轴坐标、速度和剩余距离。
|
||||||
|
19. 重新运行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web`,通过;确认 `previewPath.sampleCount=3074`、`executionPath.sampleCount=569`、`blockers=[]`。
|
||||||
|
20. 重新运行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare`,结果为 `fail_count=5`,当前 23 项检查中 18 项通过,剩余 5 项均为 native preview/execution path 缺样本或缺 20ms 周期字段。
|
||||||
|
21. 修改 `tests/node/verify_xyzbc_trt_web_app.mjs`,增加 interpreter + task/HAL 执行反馈 smoke:加载 staged `xyzbc_switchkins.ngc`,执行 task/HAL 若干 20ms cycle,断言 HAL sync、activeLine 和 XYZ 轴反馈可读。
|
||||||
|
22. 重新运行 `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node`,通过,输出 `xyzbc_trt_web_app_smoke=ok`。
|
||||||
|
23. 更新 `working/04-任务矩阵.md`:T-017 改为 18/23 通过且剩余 5 项为 native path;T-021 改为 3074 个 20ms 重采样 preview 样本;T-022 标记为完成,记录 569 个 20ms task/HAL execution 样本。
|
||||||
|
24. 更新 `working/03-推进台账.md`:新增“Web task/HAL executionPath 采集实现轮次”,记录实现内容、验证结果和下一步。
|
||||||
|
25. 更新 `working/05-验收证据.md`:将顶部当前结果更新为 `fail_count=5` 和 `gmoccapy_static_build=ok`,新增 Web task/HAL executionPath 采集验收小节。
|
||||||
|
26. 执行 `python3 -m py_compile tools/collect-native-xyzbc-trt-evidence.py` 和 `git diff --check -- web-rtcp-5axis-xyzbc-trt-sim-plan`,均无输出,表示通过。
|
||||||
|
27. 检查 compare JSON 摘要,确认 `passCount=18`、`failCount=5`、`blockers=[]`、`webStatus=ready-for-wasm-runtime`。
|
||||||
|
28. 检查 Git 状态,确认本轮主要修改为 `collect-web-xyzbc-trt-evidence.mjs`、`verify_xyzbc_trt_web_app.mjs`、Web/compare evidence JSON 和 working 文档;同时保留前序轮次已有的 profile、native collector、compare、wasm-port 测试和日志改动。
|
||||||
|
29. 本轮结论:Web 侧 previewPath 和 executionPath 都已具备真实 20ms 样本;compare 剩余失败已收敛到 native evidence 尚未重采集 path 样本。下一步应在 `/home/mes123456/cnc_wams/linuxcnc/scripts/rip-environment` 下重跑 native evidence,再重跑 compare。
|
||||||
|
|
||||||
|
## 2026-07-02 - 安装 curl
|
||||||
|
|
||||||
|
1. 收到用户请求:安装 `curl`。
|
||||||
|
2. 执行 `command -v curl`,确认当前系统未找到 `curl` 可执行文件。
|
||||||
|
3. 检查包管理器,确认系统使用 `apt-get`。
|
||||||
|
4. 执行 `id -u` 和 `sudo -n true`,确认当前用户不是 root,但具备无交互 sudo 权限。
|
||||||
|
5. 执行 `sudo apt-get update && sudo apt-get install -y curl`。
|
||||||
|
6. apt 更新命中 Debian trixie、trixie-updates、trixie-security、Microsoft VS Code 和 Google Chrome 软件源。
|
||||||
|
7. apt 安装 `curl` 包,版本为 `8.14.1-2+deb13u3`,新增占用约 507 kB。
|
||||||
|
8. 安装过程中 debconf 因无交互终端回退到 Noninteractive,未影响安装结果。
|
||||||
|
9. 执行 `curl --version | head -1`,验证 `curl 8.14.1` 已可用。
|
||||||
|
10. 本轮结论:`curl` 已安装完成,可直接在后续命令中使用。
|
||||||
|
|||||||
@@ -135,6 +135,6 @@ export const xyzbcTrtProfile = {
|
|||||||
},
|
},
|
||||||
samplePrograms: [
|
samplePrograms: [
|
||||||
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc",
|
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc",
|
||||||
"linuxcnc/nc_files/3D_Chips.ngc",
|
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/boat-xyzbc.ngc",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,8 +8,15 @@ import {
|
|||||||
selectMachineFileProgram,
|
selectMachineFileProgram,
|
||||||
stageProfileMachineFiles,
|
stageProfileMachineFiles,
|
||||||
} from "../../app/src/runtime/linuxcnc-machine-file-staging.js";
|
} from "../../app/src/runtime/linuxcnc-machine-file-staging.js";
|
||||||
|
import { createLinuxCncInterpreterRuntime } from "../../app/src/runtime/linuxcnc-interpreter-runtime.js";
|
||||||
|
import {
|
||||||
|
buildTaskHalProgramMotionPlan,
|
||||||
|
buildTaskHalSessionFromMachineFiles,
|
||||||
|
wrapTaskHalSdk,
|
||||||
|
} from "../../app/src/runtime/linuxcnc-task-hal-runtime.js";
|
||||||
import { getFiveAxisProfile } from "../../app/src/profiles/index.js";
|
import { getFiveAxisProfile } from "../../app/src/profiles/index.js";
|
||||||
import { createSimulationStore } from "../../app/src/state/store.js";
|
import { createSimulationStore } from "../../app/src/state/store.js";
|
||||||
|
import { createLinuxCncTaskHalSdk } from "../../../wasm-port/runtime/sdk/src/linuxcnc-task-hal.js";
|
||||||
|
|
||||||
const profile = getFiveAxisProfile();
|
const profile = getFiveAxisProfile();
|
||||||
|
|
||||||
@@ -23,6 +30,10 @@ assert.equal(profile.machineFileStaging.defaultProgramFilename, "xyzbc_switchkin
|
|||||||
assert.equal(profile.panelSchema.id, "xyzbc-trt-switchkins-pyvcp");
|
assert.equal(profile.panelSchema.id, "xyzbc-trt-switchkins-pyvcp");
|
||||||
assert.ok(profile.halPins.includes("xyzbc-trt-kins.x-offset"));
|
assert.ok(profile.halPins.includes("xyzbc-trt-kins.x-offset"));
|
||||||
assert.ok(profile.halPins.includes("motion.switchkins-type"));
|
assert.ok(profile.halPins.includes("motion.switchkins-type"));
|
||||||
|
assert.deepEqual(profile.samplePrograms, [
|
||||||
|
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc",
|
||||||
|
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/boat-xyzbc.ngc",
|
||||||
|
]);
|
||||||
assert.ok(profile.remaps.some((remap) => remap.code === "M428" && remap.switchkinsType === 1));
|
assert.ok(profile.remaps.some((remap) => remap.code === "M428" && remap.switchkinsType === 1));
|
||||||
assert.ok(profile.remaps.some((remap) => remap.code === "M429" && remap.switchkinsType === 0));
|
assert.ok(profile.remaps.some((remap) => remap.code === "M429" && remap.switchkinsType === 0));
|
||||||
assert.ok(profile.remaps.some((remap) => remap.code === "M430" && remap.switchkinsType === 2));
|
assert.ok(profile.remaps.some((remap) => remap.code === "M430" && remap.switchkinsType === 2));
|
||||||
@@ -63,6 +74,9 @@ assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc-trt.tb
|
|||||||
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/428remap.ngc") && file.kind === "remap"));
|
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/428remap.ngc") && file.kind === "remap"));
|
||||||
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/429remap.ngc") && file.kind === "remap"));
|
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/429remap.ngc") && file.kind === "remap"));
|
||||||
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/430remap.ngc") && file.kind === "remap"));
|
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/430remap.ngc") && file.kind === "remap"));
|
||||||
|
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/xyzbc_switchkins_sub.ngc") && file.kind === "remap"));
|
||||||
|
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/centering.ngc") && file.kind === "remap"));
|
||||||
|
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/helix_bc.ngc") && file.kind === "remap"));
|
||||||
assert.ok(staged.save.gcodeSources.some((source) => source.filename === "xyzbc_switchkins.ngc"));
|
assert.ok(staged.save.gcodeSources.some((source) => source.filename === "xyzbc_switchkins.ngc"));
|
||||||
assert.ok(staged.save.gcodeSources.some((source) => source.filename === "boat-xyzbc.ngc"));
|
assert.ok(staged.save.gcodeSources.some((source) => source.filename === "boat-xyzbc.ngc"));
|
||||||
assert.equal(staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc.var") && file.kind === "parameters"), true);
|
assert.equal(staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc.var") && file.kind === "parameters"), true);
|
||||||
@@ -75,6 +89,54 @@ const selectedPlan = selectMachineFileProgram(
|
|||||||
assert.equal(selectedPlan.selectedProgramFilename, "xyzbc_switchkins.ngc");
|
assert.equal(selectedPlan.selectedProgramFilename, "xyzbc_switchkins.ngc");
|
||||||
assert.ok(selectedPlan.wasmProgramPath.endsWith("/demos/xyzbc_switchkins.ngc"));
|
assert.ok(selectedPlan.wasmProgramPath.endsWith("/demos/xyzbc_switchkins.ngc"));
|
||||||
|
|
||||||
|
const interpreter = await createLinuxCncInterpreterRuntime();
|
||||||
|
const execution = interpreter.runMachineFileProgram({
|
||||||
|
plan: selectedPlan,
|
||||||
|
files: staged.save.files,
|
||||||
|
executionMode: "fiveAxisRemap",
|
||||||
|
});
|
||||||
|
assert.equal(execution.summary.machineFileExecutionReady, true);
|
||||||
|
assert.equal(execution.plannerTiming.plannerRuntimeReady, true);
|
||||||
|
assert.equal(execution.plannerTiming.samples.length > 0, true);
|
||||||
|
|
||||||
|
const taskHalWasm = await readFile(
|
||||||
|
new URL("../../../wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm", import.meta.url),
|
||||||
|
);
|
||||||
|
const taskHal = wrapTaskHalSdk(await createLinuxCncTaskHalSdk({
|
||||||
|
wasmBinary: taskHalWasm,
|
||||||
|
print() {},
|
||||||
|
printErr() {},
|
||||||
|
}));
|
||||||
|
const taskHalSession = buildTaskHalSessionFromMachineFiles({
|
||||||
|
profile,
|
||||||
|
plan: selectedPlan,
|
||||||
|
save: staged.save,
|
||||||
|
selectedProgramRel: selectedPlan.selectedProgramSourceRel,
|
||||||
|
});
|
||||||
|
taskHal.initSession(taskHalSession);
|
||||||
|
taskHal.stageFiles(taskHalSession.files);
|
||||||
|
taskHal.openProgram(taskHalSession.programPath);
|
||||||
|
taskHal.loadProgramMotionPlan(buildTaskHalProgramMotionPlan({
|
||||||
|
programPath: taskHalSession.programPath,
|
||||||
|
motion: execution.motion,
|
||||||
|
timing: execution.plannerTiming,
|
||||||
|
linearUnits: execution.plannerTiming.linearUnits || "mm",
|
||||||
|
programLines: staged.save.files
|
||||||
|
.find((file) => (file.wasmPath || file.path) === taskHalSession.programPath)
|
||||||
|
?.text.split(/\r?\n/) || [],
|
||||||
|
}));
|
||||||
|
taskHal.sendCommand({ type: "EMC_TASK_SET_STATE", state: "ON" });
|
||||||
|
taskHal.sendCommand({ type: "EMC_TASK_SET_MODE", mode: "AUTO" });
|
||||||
|
taskHal.sendCommand({ type: "EMC_TASK_PLAN_RUN", line: 0 });
|
||||||
|
taskHal.runCycles({ taskPeriodNs: 20000000, servoPeriodNs: 1000000, taskCycles: 3 });
|
||||||
|
const taskHalStatus = taskHal.readStatus();
|
||||||
|
assert.equal(taskHalStatus.summary.taskRuntimeReady, true);
|
||||||
|
assert.equal(taskHalStatus.summary.halSyncReady, true);
|
||||||
|
assert.equal(taskHalStatus.ui.activeLine >= 1, true);
|
||||||
|
assert.equal(Number.isFinite(taskHalStatus.motionStatus.axis.x), true);
|
||||||
|
assert.equal(Number.isFinite(taskHalStatus.motionStatus.axis.y), true);
|
||||||
|
assert.equal(Number.isFinite(taskHalStatus.motionStatus.axis.z), true);
|
||||||
|
|
||||||
const store = createSimulationStore();
|
const store = createSimulationStore();
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
assert.equal(state.machineProfile, "xyzbc-trt");
|
assert.equal(state.machineProfile, "xyzbc-trt");
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import time
|
|||||||
|
|
||||||
|
|
||||||
AXES = ["X", "Y", "Z", "A", "B", "C", "U", "V", "W"]
|
AXES = ["X", "Y", "Z", "A", "B", "C", "U", "V", "W"]
|
||||||
DEFAULT_INI = "/home/mes123456/linuxcnc-master/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini"
|
SAMPLE_PERIOD_MS = 20
|
||||||
DEFAULT_PROGRAM = "/home/mes123456/linuxcnc-master/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc"
|
DEFAULT_SOURCE_ROOT = "/home/mes123456/cnc_wams/linuxcnc"
|
||||||
|
DEFAULT_INI = f"{DEFAULT_SOURCE_ROOT}/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini"
|
||||||
|
DEFAULT_PROGRAM = f"{DEFAULT_SOURCE_ROOT}/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc"
|
||||||
DEFAULT_OUTPUT = "/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/native-xyzbc-trt-evidence.json"
|
DEFAULT_OUTPUT = "/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/native-xyzbc-trt-evidence.json"
|
||||||
|
|
||||||
|
|
||||||
@@ -31,7 +33,11 @@ def main():
|
|||||||
"status": "blocked",
|
"status": "blocked",
|
||||||
"blocker": "python_linuxcnc_import_failed",
|
"blocker": "python_linuxcnc_import_failed",
|
||||||
"error": f"{type(exc).__name__}: {exc}",
|
"error": f"{type(exc).__name__}: {exc}",
|
||||||
"hint": "Run through /home/mes123456/linuxcnc-master/scripts/rip-environment python3",
|
"hint": f"Run through {DEFAULT_SOURCE_ROOT}/scripts/rip-environment python3",
|
||||||
|
"sourceRoot": DEFAULT_SOURCE_ROOT,
|
||||||
|
"pathSampling": create_path_sampling(),
|
||||||
|
"previewPath": empty_path("linuxcnc-preview", "python linuxcnc import failed"),
|
||||||
|
"executionPath": empty_path("linuxcnc-stat", "python linuxcnc import failed"),
|
||||||
})
|
})
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
@@ -53,6 +59,7 @@ def main():
|
|||||||
"status": "ok",
|
"status": "ok",
|
||||||
"collectedAt": iso_now(),
|
"collectedAt": iso_now(),
|
||||||
"executionMode": "auto-run" if args.run else "snapshot-only",
|
"executionMode": "auto-run" if args.run else "snapshot-only",
|
||||||
|
"sourceRoot": source_root_for_ini(args.ini),
|
||||||
"iniPath": args.ini,
|
"iniPath": args.ini,
|
||||||
"programPath": args.program,
|
"programPath": args.program,
|
||||||
"programExists": pathlib.Path(args.program).exists(),
|
"programExists": pathlib.Path(args.program).exists(),
|
||||||
@@ -66,6 +73,26 @@ def main():
|
|||||||
"commandResult": command_result,
|
"commandResult": command_result,
|
||||||
"hal": hal,
|
"hal": hal,
|
||||||
"errors": errors,
|
"errors": errors,
|
||||||
|
"pathSampling": create_path_sampling(),
|
||||||
|
"previewPath": empty_path("linuxcnc-preview", "native AXIS preview/canon path capture is not implemented in this collector"),
|
||||||
|
"executionPath": execution_path_from_command(command_result),
|
||||||
|
"startupSequence": [
|
||||||
|
".desktop",
|
||||||
|
"rip-environment",
|
||||||
|
"linuxcncsvr",
|
||||||
|
"rtapi_app",
|
||||||
|
"milltask",
|
||||||
|
"halui",
|
||||||
|
"LIB:basic_sim.tcl",
|
||||||
|
"xyzbc-trt-kins",
|
||||||
|
"xyzbc-trt-gui",
|
||||||
|
"axis.py",
|
||||||
|
"xyzbc-trt.xml",
|
||||||
|
"switchkins_postgui.hal",
|
||||||
|
"OPEN_FILE ./demos/xyzbc_switchkins.ngc",
|
||||||
|
],
|
||||||
|
"halNets": native_hal_nets(hal),
|
||||||
|
"kinematicsPins": native_kinematics_pins(hal),
|
||||||
"coverage": {
|
"coverage": {
|
||||||
"axisProfile": before.get("axisMask") == 55 or after.get("axisMask") == 55,
|
"axisProfile": before.get("axisMask") == 55 or after.get("axisMask") == 55,
|
||||||
"xyzbcProgramOpen": str(after.get("file") or before.get("file") or "").endswith("xyzbc_switchkins.ngc"),
|
"xyzbcProgramOpen": str(after.get("file") or before.get("file") or "").endswith("xyzbc_switchkins.ngc"),
|
||||||
@@ -73,6 +100,8 @@ def main():
|
|||||||
"jointFeedbackReadable": all(f"joint.{i}.pos-fb" in hal.get("pins", {}) for i in range(5)),
|
"jointFeedbackReadable": all(f"joint.{i}.pos-fb" in hal.get("pins", {}) for i in range(5)),
|
||||||
"taskStateReadable": after.get("taskState") is not None,
|
"taskStateReadable": after.get("taskState") is not None,
|
||||||
"positionReadable": bool(after.get("position")),
|
"positionReadable": bool(after.get("position")),
|
||||||
|
"previewPathAvailable": False,
|
||||||
|
"executionPathAvailable": command_result is not None and len(command_result.get("events", [])) > 0,
|
||||||
},
|
},
|
||||||
"semanticBoundary": "native_linuxcnc_axis_vismach_xyzbc_trt_runtime",
|
"semanticBoundary": "native_linuxcnc_axis_vismach_xyzbc_trt_runtime",
|
||||||
}
|
}
|
||||||
@@ -109,6 +138,9 @@ def run_program(linuxcnc, stat, command, program_path, timeout):
|
|||||||
"readLine": snapshot.get("readLine"),
|
"readLine": snapshot.get("readLine"),
|
||||||
"position": snapshot.get("position"),
|
"position": snapshot.get("position"),
|
||||||
"jointActualPosition": snapshot.get("jointActualPosition"),
|
"jointActualPosition": snapshot.get("jointActualPosition"),
|
||||||
|
"velocity": snapshot.get("velocity"),
|
||||||
|
"feedrate": snapshot.get("feedrate"),
|
||||||
|
"spindle": snapshot.get("spindle"),
|
||||||
})
|
})
|
||||||
if snapshot.get("interpState") == linuxcnc.INTERP_IDLE and len(result["events"]) > 2:
|
if snapshot.get("interpState") == linuxcnc.INTERP_IDLE and len(result["events"]) > 2:
|
||||||
result["status"] = "completed"
|
result["status"] = "completed"
|
||||||
@@ -160,6 +192,10 @@ def collect_hal_snapshot():
|
|||||||
["halcmd", "show", "pin", "xyzbc-trt-kins.tool-offset"],
|
["halcmd", "show", "pin", "xyzbc-trt-kins.tool-offset"],
|
||||||
["halcmd", "show", "pin", "xyzbc-trt-kins.x-offset"],
|
["halcmd", "show", "pin", "xyzbc-trt-kins.x-offset"],
|
||||||
["halcmd", "show", "pin", "xyzbc-trt-kins.z-offset"],
|
["halcmd", "show", "pin", "xyzbc-trt-kins.z-offset"],
|
||||||
|
["halcmd", "show", "pin", "xyzbc-trt-kins.x-rot-point"],
|
||||||
|
["halcmd", "show", "pin", "xyzbc-trt-kins.y-rot-point"],
|
||||||
|
["halcmd", "show", "pin", "xyzbc-trt-kins.z-rot-point"],
|
||||||
|
["halcmd", "show", "pin", "xyzbc-trt-kins.conventional-directions"],
|
||||||
]
|
]
|
||||||
raw = []
|
raw = []
|
||||||
for command in commands:
|
for command in commands:
|
||||||
@@ -225,6 +261,158 @@ def count_program_lines(path):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def create_path_sampling():
|
||||||
|
return {
|
||||||
|
"samplePeriodMs": SAMPLE_PERIOD_MS,
|
||||||
|
"timeBase": "program-relative-ms",
|
||||||
|
"resampling": "linear-position-slerp-or-axis-linear",
|
||||||
|
"coordinateSystem": "machine-xyzbc-and-tcp",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def empty_path(source, reason):
|
||||||
|
return {
|
||||||
|
"source": source,
|
||||||
|
"samplePeriodMs": SAMPLE_PERIOD_MS,
|
||||||
|
"status": "blocked",
|
||||||
|
"unavailableReason": reason,
|
||||||
|
"sampleCount": 0,
|
||||||
|
"samples": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def execution_path_from_command(command_result):
|
||||||
|
events = (command_result or {}).get("events", [])
|
||||||
|
if not events:
|
||||||
|
return empty_path("linuxcnc-stat", "collector was run without --run or no execution events were captured")
|
||||||
|
samples = []
|
||||||
|
event_index = 0
|
||||||
|
max_ms = int(round(float(events[-1].get("elapsedSeconds") or 0) * 1000))
|
||||||
|
for sample_index, time_ms in enumerate(range(0, max_ms + SAMPLE_PERIOD_MS, SAMPLE_PERIOD_MS)):
|
||||||
|
while (
|
||||||
|
event_index + 1 < len(events)
|
||||||
|
and float(events[event_index + 1].get("elapsedSeconds") or 0) * 1000 <= time_ms
|
||||||
|
):
|
||||||
|
event_index += 1
|
||||||
|
event = events[event_index]
|
||||||
|
samples.append(path_sample_from_event(sample_index, time_ms, event))
|
||||||
|
return {
|
||||||
|
"source": "linuxcnc-stat",
|
||||||
|
"samplePeriodMs": SAMPLE_PERIOD_MS,
|
||||||
|
"status": "ok" if samples else "blocked",
|
||||||
|
"unavailableReason": None if samples else "no execution samples after resampling",
|
||||||
|
"sampleCount": len(samples),
|
||||||
|
"samples": samples,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def path_sample_from_event(sample_index, time_ms, event):
|
||||||
|
position = event.get("position") or {}
|
||||||
|
joints = event.get("jointActualPosition") or {}
|
||||||
|
b = number_or_zero(joints.get("3", position.get("b")))
|
||||||
|
c = number_or_zero(joints.get("4", position.get("c")))
|
||||||
|
joint = {
|
||||||
|
"x": number_or_zero(joints.get("0", position.get("x"))),
|
||||||
|
"y": number_or_zero(joints.get("1", position.get("y"))),
|
||||||
|
"z": number_or_zero(joints.get("2", position.get("z"))),
|
||||||
|
"b": b,
|
||||||
|
"c": c,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"sampleIndex": sample_index,
|
||||||
|
"timeMs": time_ms,
|
||||||
|
"line": int(number_or_zero(event.get("currentLine"))),
|
||||||
|
"motionType": "unknown",
|
||||||
|
"activeKinematics": "unknown",
|
||||||
|
"tool": {
|
||||||
|
"id": 0,
|
||||||
|
"length": 0,
|
||||||
|
"diameter": 0,
|
||||||
|
},
|
||||||
|
"joint": joint,
|
||||||
|
"tcp": {
|
||||||
|
"x": joint["x"],
|
||||||
|
"y": joint["y"],
|
||||||
|
"z": joint["z"],
|
||||||
|
},
|
||||||
|
"toolAxis": tool_axis_from_bc(b, c),
|
||||||
|
"feed": number_or_zero(event.get("feedrate")),
|
||||||
|
"spindle": spindle_speed(event.get("spindle")),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def tool_axis_from_bc(b_deg, c_deg):
|
||||||
|
b = math.radians(number_or_zero(b_deg))
|
||||||
|
c = math.radians(number_or_zero(c_deg))
|
||||||
|
return {
|
||||||
|
"i": math.sin(b) * math.cos(c),
|
||||||
|
"j": math.sin(b) * math.sin(c),
|
||||||
|
"k": math.cos(b),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def spindle_speed(spindle):
|
||||||
|
if isinstance(spindle, list) and spindle:
|
||||||
|
first = spindle[0]
|
||||||
|
if isinstance(first, dict):
|
||||||
|
return number_or_zero(first.get("speed"))
|
||||||
|
if isinstance(spindle, dict):
|
||||||
|
return number_or_zero(spindle.get("speed"))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def native_hal_nets(hal):
|
||||||
|
pins = hal.get("pins", {})
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"signal": "kinstype-select",
|
||||||
|
"source": "motion.analog-out-03",
|
||||||
|
"target": "motion.switchkins-type",
|
||||||
|
"present": "motion.switchkins-type" in pins,
|
||||||
|
},
|
||||||
|
*[
|
||||||
|
{
|
||||||
|
"signal": f"joint-{index}-feedback",
|
||||||
|
"source": f"joint.{index}.pos-fb",
|
||||||
|
"target": ["table-x", "saddle-y", "spindle-z", "tilt-b", "rotate-c"][index],
|
||||||
|
"present": f"joint.{index}.pos-fb" in pins,
|
||||||
|
}
|
||||||
|
for index in range(5)
|
||||||
|
],
|
||||||
|
{
|
||||||
|
"signal": "tool-offset",
|
||||||
|
"source": "motion.tooloffset.z",
|
||||||
|
"target": "xyzbc-trt-kins.tool-offset",
|
||||||
|
"present": "motion.tooloffset.z" in pins or "xyzbc-trt-kins.tool-offset" in pins,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def native_kinematics_pins(hal):
|
||||||
|
pins = hal.get("pins", {})
|
||||||
|
return {
|
||||||
|
"xOffset": hal_pin_value(pins, "xyzbc-trt-kins.x-offset"),
|
||||||
|
"zOffset": hal_pin_value(pins, "xyzbc-trt-kins.z-offset"),
|
||||||
|
"xRotPoint": hal_pin_value(pins, "xyzbc-trt-kins.x-rot-point"),
|
||||||
|
"yRotPoint": hal_pin_value(pins, "xyzbc-trt-kins.y-rot-point"),
|
||||||
|
"zRotPoint": hal_pin_value(pins, "xyzbc-trt-kins.z-rot-point"),
|
||||||
|
"conventionalDirections": hal_pin_value(pins, "xyzbc-trt-kins.conventional-directions"),
|
||||||
|
"toolOffset": hal_pin_value(pins, "xyzbc-trt-kins.tool-offset"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def hal_pin_value(pins, name):
|
||||||
|
return (pins.get(name) or {}).get("value")
|
||||||
|
|
||||||
|
|
||||||
|
def source_root_for_ini(ini_path):
|
||||||
|
marker = "/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini"
|
||||||
|
value = str(ini_path)
|
||||||
|
if value.endswith(marker):
|
||||||
|
return value[: -len(marker)]
|
||||||
|
return DEFAULT_SOURCE_ROOT
|
||||||
|
|
||||||
|
|
||||||
def value(obj, attr):
|
def value(obj, attr):
|
||||||
return normalize_json(getattr(obj, attr, None))
|
return normalize_json(getattr(obj, attr, None))
|
||||||
|
|
||||||
@@ -268,6 +456,16 @@ def parse_number(value):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def number_or_zero(value):
|
||||||
|
try:
|
||||||
|
number = float(value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return 0
|
||||||
|
if math.isnan(number) or math.isinf(number):
|
||||||
|
return 0
|
||||||
|
return number
|
||||||
|
|
||||||
|
|
||||||
def write_json(path, payload):
|
def write_json(path, payload):
|
||||||
output = pathlib.Path(path)
|
output = pathlib.Path(path)
|
||||||
output.parent.mkdir(parents=True, exist_ok=True)
|
output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import { createSimulationStore } from "../app/src/state/store.js";
|
import { createSimulationStore } from "../app/src/state/store.js";
|
||||||
import { getFiveAxisProfile } from "../app/src/profiles/index.js";
|
import { getFiveAxisProfile } from "../app/src/profiles/index.js";
|
||||||
|
|
||||||
|
const SAMPLE_PERIOD_MS = 20;
|
||||||
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
||||||
const projectRoot = resolve(repoRoot, "web-rtcp-5axis-xyzbc-trt-sim-plan");
|
const projectRoot = resolve(repoRoot, "web-rtcp-5axis-xyzbc-trt-sim-plan");
|
||||||
const outputPath = process.argv[2]
|
const outputPath = process.argv[2]
|
||||||
@@ -35,6 +36,13 @@ const store = createSimulationStore();
|
|||||||
const storeStage = await store.stageMachineFiles({ storage: createMemorySessionStorage() });
|
const storeStage = await store.stageMachineFiles({ storage: createMemorySessionStorage() });
|
||||||
store.dispatch({ type: "LOAD_LINUXCNC_GCODE_SOURCE", sourceRel });
|
store.dispatch({ type: "LOAD_LINUXCNC_GCODE_SOURCE", sourceRel });
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
|
const paths = await collectPathEvidence({
|
||||||
|
profile,
|
||||||
|
staged,
|
||||||
|
selectedPlan,
|
||||||
|
wasmArtifacts,
|
||||||
|
});
|
||||||
|
const semanticFields = buildSemanticFields({ profile, ini, staged, state });
|
||||||
|
|
||||||
const evidence = {
|
const evidence = {
|
||||||
apiName: "xyzbc-trt-web-opfs-wasm-evidence",
|
apiName: "xyzbc-trt-web-opfs-wasm-evidence",
|
||||||
@@ -50,6 +58,7 @@ const evidence = {
|
|||||||
kinematics: profile.kinematics,
|
kinematics: profile.kinematics,
|
||||||
kinematicsModuleId: profile.kinematicsModuleId,
|
kinematicsModuleId: profile.kinematicsModuleId,
|
||||||
defaultProgramFilename: profile.machineFileStaging?.defaultProgramFilename,
|
defaultProgramFilename: profile.machineFileStaging?.defaultProgramFilename,
|
||||||
|
samplePrograms: profile.samplePrograms,
|
||||||
switchkinsTypes: profile.kinematicsParameters?.switchkinsTypes,
|
switchkinsTypes: profile.kinematicsParameters?.switchkinsTypes,
|
||||||
halPins: profile.halPins,
|
halPins: profile.halPins,
|
||||||
},
|
},
|
||||||
@@ -64,6 +73,10 @@ const evidence = {
|
|||||||
toolTable: ini.emcio.toolTable,
|
toolTable: ini.emcio.toolTable,
|
||||||
parameterFile: ini.rs274ngc.parameterFile,
|
parameterFile: ini.rs274ngc.parameterFile,
|
||||||
jointConfig: ini.jointConfig,
|
jointConfig: ini.jointConfig,
|
||||||
|
display: ini.display,
|
||||||
|
traj: ini.traj,
|
||||||
|
axisLimits: ini.axisLimits,
|
||||||
|
hal: ini.hal,
|
||||||
},
|
},
|
||||||
opfsStaging: {
|
opfsStaging: {
|
||||||
storageMode: staged.save.storageMode,
|
storageMode: staged.save.storageMode,
|
||||||
@@ -94,6 +107,10 @@ const evidence = {
|
|||||||
programLineCount: state.programLines.length,
|
programLineCount: state.programLines.length,
|
||||||
selectedGcodeSourceRel: state.machineFileStaging.selectedGcodeSourceRel,
|
selectedGcodeSourceRel: state.machineFileStaging.selectedGcodeSourceRel,
|
||||||
},
|
},
|
||||||
|
pathSampling: createPathSampling(),
|
||||||
|
previewPath: paths.previewPath,
|
||||||
|
executionPath: paths.executionPath,
|
||||||
|
...semanticFields,
|
||||||
wasm: wasmArtifacts,
|
wasm: wasmArtifacts,
|
||||||
coverage: {
|
coverage: {
|
||||||
profileDefaultXyzbc: profile.id === "xyzbc-trt",
|
profileDefaultXyzbc: profile.id === "xyzbc-trt",
|
||||||
@@ -106,6 +123,20 @@ const evidence = {
|
|||||||
toolTableStaged: staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc-trt.tbl")),
|
toolTableStaged: staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc-trt.tbl")),
|
||||||
parameterFileStaged: staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc.var")),
|
parameterFileStaged: staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc.var")),
|
||||||
defaultProgramStaged: staged.save.gcodeSources.some((source) => source.filename === "xyzbc_switchkins.ngc"),
|
defaultProgramStaged: staged.save.gcodeSources.some((source) => source.filename === "xyzbc_switchkins.ngc"),
|
||||||
|
boatProgramStaged: staged.save.gcodeSources.some((source) => source.filename === "boat-xyzbc.ngc"),
|
||||||
|
ngcguiSubroutinesStaged: ["xyzbc_switchkins_sub.ngc", "centering.ngc", "helix_bc.ngc"].every((name) => (
|
||||||
|
staged.save.files.some((file) => file.sourceRel.endsWith(`/remap_subs/${name}`))
|
||||||
|
)),
|
||||||
|
postguiHalEquivalent: semanticFields.halNets.some((net) => net.source === "pyvcp.type1-button" && net.target === "halui.mdi-command-01"),
|
||||||
|
kinematicsPinsCovered: semanticFields.kinematicsPins.xOffset === -20
|
||||||
|
&& semanticFields.kinematicsPins.zOffset === -15
|
||||||
|
&& semanticFields.kinematicsPins.conventionalDirections === 0,
|
||||||
|
axisJointLimitsCovered: semanticFields.axisJointLimits.coordinates === "XYZBC"
|
||||||
|
&& semanticFields.axisJointLimits.jointCount === 5
|
||||||
|
&& Boolean(semanticFields.axisJointLimits.axisLimits.B)
|
||||||
|
&& Boolean(semanticFields.axisJointLimits.axisLimits.C),
|
||||||
|
previewPathAvailable: paths.previewPath.sampleCount > 0,
|
||||||
|
executionPathAvailable: paths.executionPath.sampleCount > 0,
|
||||||
wasmArtifactsReady: wasmArtifacts.ready,
|
wasmArtifactsReady: wasmArtifacts.ready,
|
||||||
},
|
},
|
||||||
blockers: [
|
blockers: [
|
||||||
@@ -119,6 +150,14 @@ const evidence = {
|
|||||||
id: "missing-parameter-file-staging",
|
id: "missing-parameter-file-staging",
|
||||||
detail: "xyzbc.var is referenced by INI but absent from wasm-port/vendor manifest in this workspace.",
|
detail: "xyzbc.var is referenced by INI but absent from wasm-port/vendor manifest in this workspace.",
|
||||||
}]),
|
}]),
|
||||||
|
...(paths.previewPath.sampleCount > 0 ? [] : [{
|
||||||
|
id: "web-preview-path-unavailable",
|
||||||
|
detail: paths.previewPath.unavailableReason,
|
||||||
|
}]),
|
||||||
|
...(paths.executionPath.sampleCount > 0 ? [] : [{
|
||||||
|
id: "web-execution-path-unavailable",
|
||||||
|
detail: paths.executionPath.unavailableReason,
|
||||||
|
}]),
|
||||||
],
|
],
|
||||||
semanticBoundary: "web_opfs_wasm_runtime_readiness_for_linuxcnc_xyzbc_trt",
|
semanticBoundary: "web_opfs_wasm_runtime_readiness_for_linuxcnc_xyzbc_trt",
|
||||||
};
|
};
|
||||||
@@ -165,3 +204,491 @@ async function commandExists(command) {
|
|||||||
child.on("exit", (code) => resolveCommand(code === 0));
|
child.on("exit", (code) => resolveCommand(code === 0));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function collectPathEvidence({ profile, staged, selectedPlan, wasmArtifacts }) {
|
||||||
|
if (!wasmArtifacts.files.includes("wasm-port/build/wasm/core/linuxcnc_interp.js")
|
||||||
|
|| !wasmArtifacts.files.includes("wasm-port/build/wasm/core/linuxcnc_interp.wasm")) {
|
||||||
|
return {
|
||||||
|
previewPath: emptyPath("web-preview", "missing linuxcnc_interp WASM artifacts"),
|
||||||
|
executionPath: emptyPath("web-task-hal", "missing task/HAL WASM runtime artifacts"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { createLinuxCncInterpreterRuntime } = await import("../app/src/runtime/linuxcnc-interpreter-runtime.js");
|
||||||
|
const runtime = await createLinuxCncInterpreterRuntime();
|
||||||
|
const execution = runtime.runMachineFileProgram({
|
||||||
|
plan: selectedPlan,
|
||||||
|
files: staged.save.files,
|
||||||
|
executionMode: "fiveAxisRemap",
|
||||||
|
});
|
||||||
|
const previewPath = pathFromWebMotion(execution, profile);
|
||||||
|
return {
|
||||||
|
previewPath,
|
||||||
|
executionPath: wasmArtifacts.ready
|
||||||
|
? await pathFromTaskHalExecution({ profile, staged, selectedPlan, execution })
|
||||||
|
: emptyPath("web-task-hal", "missing task/HAL WASM runtime artifacts"),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
previewPath: emptyPath("web-preview", error instanceof Error ? error.message : String(error)),
|
||||||
|
executionPath: emptyPath("web-task-hal", "preview runtime failed before task/HAL execution capture"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pathFromWebMotion(execution, profile) {
|
||||||
|
const plannerSamples = execution.plannerTiming?.samples || [];
|
||||||
|
const motion = execution.motion || [];
|
||||||
|
const motionByIndex = new Map(motion.map((event, index) => [index, event]));
|
||||||
|
const resampled = resamplePlannerSamples(plannerSamples, SAMPLE_PERIOD_MS);
|
||||||
|
const samples = resampled.map((sample, index) => {
|
||||||
|
const event = motionByIndex.get(sample.motionIndex) || {};
|
||||||
|
const axes = sample.axes || event.axes || {};
|
||||||
|
return normalizePathSample({
|
||||||
|
sampleIndex: index,
|
||||||
|
timeMs: sample.timeMs,
|
||||||
|
line: sample.line ?? event.line ?? 0,
|
||||||
|
motionType: motionTypeFromCanonical(sample.type || event.type),
|
||||||
|
activeKinematics: activeKinematics(event),
|
||||||
|
axes,
|
||||||
|
tool: firstTool(profile),
|
||||||
|
feed: sample.currentVelocityMmPerMin ?? event.feedRate ?? 0,
|
||||||
|
spindle: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
source: "web-linuxcnc-interpreter-preview",
|
||||||
|
samplePeriodMs: SAMPLE_PERIOD_MS,
|
||||||
|
status: samples.length > 0 ? "ok" : "blocked",
|
||||||
|
unavailableReason: samples.length > 0 ? null : "interpreter produced no planner samples",
|
||||||
|
sampleCount: samples.length,
|
||||||
|
samples,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resamplePlannerSamples(plannerSamples = [], samplePeriodMs = SAMPLE_PERIOD_MS) {
|
||||||
|
if (!Array.isArray(plannerSamples) || plannerSamples.length === 0) return [];
|
||||||
|
const normalized = plannerSamples
|
||||||
|
.map((sample) => ({
|
||||||
|
...sample,
|
||||||
|
timeMs: Math.round(Number(sample.timeSeconds || 0) * 1000),
|
||||||
|
}))
|
||||||
|
.filter((sample) => Number.isFinite(sample.timeMs))
|
||||||
|
.sort((left, right) => left.timeMs - right.timeMs);
|
||||||
|
if (normalized.length === 0) return [];
|
||||||
|
const firstMs = 0;
|
||||||
|
const lastMs = normalized.at(-1).timeMs;
|
||||||
|
const output = [];
|
||||||
|
let rightIndex = 0;
|
||||||
|
for (let timeMs = firstMs; timeMs <= lastMs; timeMs += samplePeriodMs) {
|
||||||
|
while (rightIndex < normalized.length - 1 && normalized[rightIndex].timeMs < timeMs) {
|
||||||
|
rightIndex += 1;
|
||||||
|
}
|
||||||
|
const right = normalized[rightIndex];
|
||||||
|
const left = normalized[Math.max(0, rightIndex - 1)] || right;
|
||||||
|
output.push(interpolatePlannerSample(left, right, timeMs));
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
function interpolatePlannerSample(left, right, timeMs) {
|
||||||
|
if (!left || !right || left.timeMs === right.timeMs) {
|
||||||
|
return { ...(right || left), timeMs };
|
||||||
|
}
|
||||||
|
const ratio = Math.max(0, Math.min(1, (timeMs - left.timeMs) / (right.timeMs - left.timeMs)));
|
||||||
|
const axes = {};
|
||||||
|
for (const axis of ["x", "y", "z", "a", "b", "c", "u", "v", "w"]) {
|
||||||
|
axes[axis] = lerpNumber(left.axes?.[axis], right.axes?.[axis], ratio);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...right,
|
||||||
|
timeMs,
|
||||||
|
axes,
|
||||||
|
currentVelocityMmPerMin: lerpNumber(left.currentVelocityMmPerMin, right.currentVelocityMmPerMin, ratio),
|
||||||
|
currentVelocity: lerpNumber(left.currentVelocity, right.currentVelocity, ratio),
|
||||||
|
distanceToGo: lerpNumber(left.distanceToGo, right.distanceToGo, ratio),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pathFromTaskHalExecution({ profile, staged, selectedPlan, execution }) {
|
||||||
|
try {
|
||||||
|
const { createLinuxCncTaskHalSdk } = await import("../../wasm-port/runtime/sdk/src/linuxcnc-task-hal.js");
|
||||||
|
const {
|
||||||
|
buildTaskHalProgramMotionPlan,
|
||||||
|
buildTaskHalSessionFromMachineFiles,
|
||||||
|
wrapTaskHalSdk,
|
||||||
|
} = await import("../app/src/runtime/linuxcnc-task-hal-runtime.js");
|
||||||
|
const wasmBinary = await readFile(resolve(repoRoot, "wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm"));
|
||||||
|
const taskHal = wrapTaskHalSdk(await createLinuxCncTaskHalSdk({
|
||||||
|
wasmBinary,
|
||||||
|
print() {},
|
||||||
|
printErr() {},
|
||||||
|
}));
|
||||||
|
const session = buildTaskHalSessionFromMachineFiles({
|
||||||
|
profile,
|
||||||
|
plan: selectedPlan,
|
||||||
|
save: staged.save,
|
||||||
|
selectedProgramRel: selectedPlan.selectedProgramSourceRel,
|
||||||
|
});
|
||||||
|
const programFile = staged.save.files.find((file) => (
|
||||||
|
(file.wasmPath || file.path) === session.programPath
|
||||||
|
));
|
||||||
|
const programLines = String(programFile?.text || "").split(/\r?\n/);
|
||||||
|
taskHal.initSession(session);
|
||||||
|
taskHal.stageFiles(session.files);
|
||||||
|
taskHal.openProgram(session.programPath);
|
||||||
|
taskHal.loadProgramMotionPlan(buildTaskHalProgramMotionPlan({
|
||||||
|
programPath: session.programPath,
|
||||||
|
motion: execution.motion,
|
||||||
|
timing: execution.plannerTiming,
|
||||||
|
linearUnits: execution.plannerTiming?.linearUnits || "mm",
|
||||||
|
programLines,
|
||||||
|
}));
|
||||||
|
taskHal.sendCommand({ type: "EMC_TASK_SET_STATE", state: "ON" });
|
||||||
|
taskHal.sendCommand({ type: "EMC_TASK_SET_MODE", mode: "AUTO" });
|
||||||
|
taskHal.sendCommand({ type: "EMC_TASK_PLAN_RUN", line: 0 });
|
||||||
|
|
||||||
|
const totalSeconds = Number(execution.plannerTiming?.totalSeconds || 0);
|
||||||
|
const cycleCount = Math.max(
|
||||||
|
Math.ceil((totalSeconds * 1000) / SAMPLE_PERIOD_MS) + 5,
|
||||||
|
Number(execution.plannerTiming?.samples?.length || 0),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
const samples = [];
|
||||||
|
let previousKey = null;
|
||||||
|
let completed = false;
|
||||||
|
for (let index = 0; index < cycleCount; index += 1) {
|
||||||
|
taskHal.runCycles({
|
||||||
|
taskPeriodNs: SAMPLE_PERIOD_MS * 1000000,
|
||||||
|
servoPeriodNs: 1000000,
|
||||||
|
taskCycles: 1,
|
||||||
|
});
|
||||||
|
const status = taskHal.readStatus();
|
||||||
|
const motion = status.motionStatus?.motion || {};
|
||||||
|
const task = status.task || {};
|
||||||
|
const activeLine = status.ui?.activeLine ?? motion.programLine ?? 0;
|
||||||
|
const sample = normalizePathSample({
|
||||||
|
sampleIndex: samples.length,
|
||||||
|
timeMs: samples.length * SAMPLE_PERIOD_MS,
|
||||||
|
line: activeLine,
|
||||||
|
motionType: motionTypeFromCanonical(currentMotionTypeForLine(execution.motion, activeLine)),
|
||||||
|
activeKinematics: activeKinematics({
|
||||||
|
switchkinsType: status.ui?.switchkinsType ?? motion.switchkinsType,
|
||||||
|
}),
|
||||||
|
axes: xyzbcAxesFromTaskHalStatus(status),
|
||||||
|
tool: firstTool(profile),
|
||||||
|
feed: status.ui?.currentVelocity ?? Number(motion.currentVel || motion.currentVelocity || 0) * 60,
|
||||||
|
spindle: spindleFromTaskHalStatus(status),
|
||||||
|
});
|
||||||
|
const sampleKey = JSON.stringify({ line: sample.line, joint: sample.joint, feed: sample.feed });
|
||||||
|
samples.push(sample);
|
||||||
|
completed = String(task.execState || "").toUpperCase() === "DONE"
|
||||||
|
&& String(task.interpState || "").toUpperCase() === "IDLE"
|
||||||
|
&& samples.length > 2
|
||||||
|
&& sampleKey === previousKey;
|
||||||
|
previousKey = sampleKey;
|
||||||
|
if (completed) break;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
source: "web-linuxcnc-task-hal-execution",
|
||||||
|
samplePeriodMs: SAMPLE_PERIOD_MS,
|
||||||
|
status: samples.length > 0 ? "ok" : "blocked",
|
||||||
|
unavailableReason: samples.length > 0 ? null : "task/HAL execution produced no status samples",
|
||||||
|
sampleCount: samples.length,
|
||||||
|
samples,
|
||||||
|
taskHal: {
|
||||||
|
semanticBoundary: "linuxcnc_task_motion_hal_wasm_simulation_runtime",
|
||||||
|
sessionProgramPath: session.programPath,
|
||||||
|
completed,
|
||||||
|
eventCount: taskHal.readEvents()?.events?.length || 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return emptyPath("web-task-hal", error instanceof Error ? error.message : String(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSemanticFields({ profile, ini, staged, state }) {
|
||||||
|
const postguiNets = [
|
||||||
|
{ signal: "kinstype.is-0", source: "kinstype.is-0", target: "pyvcp.multilabel.0.legend0", boundary: "postgui-hal" },
|
||||||
|
{ signal: "kinstype.is-1", source: "kinstype.is-1", target: "pyvcp.multilabel.0.legend1", boundary: "postgui-hal" },
|
||||||
|
{ signal: "kinstype.is-2", source: "kinstype.is-2", target: "pyvcp.multilabel.0.legend2", boundary: "postgui-hal" },
|
||||||
|
{ signal: "vismach-clear", source: "pyvcp.vismach-clear", target: "vismach.plotclear", boundary: "postgui-hal" },
|
||||||
|
{ signal: "type0-button", source: "pyvcp.type0-button", target: "halui.mdi-command-00", command: "M429", boundary: "postgui-hal" },
|
||||||
|
{ signal: "type1-button", source: "pyvcp.type1-button", target: "halui.mdi-command-01", command: "M428", boundary: "postgui-hal" },
|
||||||
|
{ signal: "type2-button", source: "pyvcp.type2-button", target: "halui.mdi-command-02", command: "M430", boundary: "postgui-hal" },
|
||||||
|
];
|
||||||
|
const profileNets = [
|
||||||
|
profile.hal?.halcmd?.switchkinsSelectNet,
|
||||||
|
...(profile.hal?.halcmd?.feedbackNets || []),
|
||||||
|
...(profile.hal?.halcmd?.offsetNets || []),
|
||||||
|
].filter(Boolean).map((net) => ({ ...net, boundary: "ini-halcmd" }));
|
||||||
|
const gcodeFiles = staged.save.gcodeFiles || [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
startupSequence: [
|
||||||
|
".desktop",
|
||||||
|
"rip-environment",
|
||||||
|
"linuxcncsvr",
|
||||||
|
"rtapi_app",
|
||||||
|
"milltask",
|
||||||
|
"halui",
|
||||||
|
"LIB:basic_sim.tcl",
|
||||||
|
"xyzbc-trt-kins",
|
||||||
|
"xyzbc-trt-gui",
|
||||||
|
"axis.py",
|
||||||
|
"xyzbc-trt.xml",
|
||||||
|
"switchkins_postgui.hal",
|
||||||
|
"OPEN_FILE ./demos/xyzbc_switchkins.ngc",
|
||||||
|
],
|
||||||
|
iniDisplay: {
|
||||||
|
...ini.display,
|
||||||
|
coordinates: ini.traj.coordinates,
|
||||||
|
positionFeedback: ini.display.positionFeedback,
|
||||||
|
positionOffset: ini.display.positionOffset,
|
||||||
|
},
|
||||||
|
halNets: [
|
||||||
|
...profileNets,
|
||||||
|
...postguiNets,
|
||||||
|
],
|
||||||
|
kinematicsPins: {
|
||||||
|
xOffset: profile.offsets?.x,
|
||||||
|
zOffset: profile.offsets?.z,
|
||||||
|
xRotPoint: profile.offsets?.xRotPoint,
|
||||||
|
yRotPoint: profile.offsets?.yRotPoint,
|
||||||
|
zRotPoint: profile.offsets?.zRotPoint,
|
||||||
|
conventionalDirections: profile.offsets?.conventionalDirections,
|
||||||
|
toolOffsetSource: "motion.tooloffset.z",
|
||||||
|
pins: profile.halPins,
|
||||||
|
},
|
||||||
|
axisJointLimits: {
|
||||||
|
coordinates: ini.traj.coordinates,
|
||||||
|
linearUnits: ini.traj.linearUnits,
|
||||||
|
angularUnits: ini.traj.angularUnits,
|
||||||
|
jogAxes: ini.display.jogAxes,
|
||||||
|
geometry: ini.display.geometry,
|
||||||
|
traj: ini.traj,
|
||||||
|
axisLimits: ini.axisLimits,
|
||||||
|
jointCount: ini.jointConfig.length,
|
||||||
|
jointConfig: ini.jointConfig,
|
||||||
|
},
|
||||||
|
switchkinsTransitions: (profile.kinematicsParameters?.switchkinsTypes || []).map((type) => ({
|
||||||
|
...type,
|
||||||
|
remap: profile.remaps?.find((remap) => remap.code === type.mdiCommand) || null,
|
||||||
|
haluiCommand: type.mdiCommand,
|
||||||
|
halPin: "motion.switchkins-type",
|
||||||
|
})),
|
||||||
|
uiEquivalence: {
|
||||||
|
firstViewport: "axis-equivalent-cnc-console",
|
||||||
|
regions: [
|
||||||
|
"program",
|
||||||
|
"dro",
|
||||||
|
"status",
|
||||||
|
"mdi-switchkins",
|
||||||
|
"override",
|
||||||
|
"tool",
|
||||||
|
"preview-execution",
|
||||||
|
],
|
||||||
|
activeProgram: state.activeProgram,
|
||||||
|
selectedGcodeSourceRel: state.machineFileStaging.selectedGcodeSourceRel,
|
||||||
|
pyvcpPanelSchema: profile.panelSchema?.id,
|
||||||
|
switchkinsButtons: ["IDENTITY", "TCP:XYZBC", "USERK"],
|
||||||
|
},
|
||||||
|
vismachEquivalent: {
|
||||||
|
sourceGui: "src/hal/user_comps/vismach/xyzbc-trt-gui.py",
|
||||||
|
webModel: "app/src/visualization/five-axis-scene.js",
|
||||||
|
pins: [
|
||||||
|
"table-x",
|
||||||
|
"saddle-y",
|
||||||
|
"spindle-z",
|
||||||
|
"tilt-b",
|
||||||
|
"rotate-c",
|
||||||
|
"tool-offset",
|
||||||
|
"x-offset",
|
||||||
|
"z-offset",
|
||||||
|
],
|
||||||
|
clearTraceSignal: "pyvcp.vismach-clear => vismach.plotclear",
|
||||||
|
},
|
||||||
|
ngcguiSubroutines: ["xyzbc_switchkins_sub.ngc", "centering.ngc", "helix_bc.ngc"].map((filename) => ({
|
||||||
|
filename,
|
||||||
|
staged: gcodeFiles.some((file) => file.filename === filename),
|
||||||
|
sourceRel: gcodeFiles.find((file) => file.filename === filename)?.sourceRel || null,
|
||||||
|
})),
|
||||||
|
demoPrograms: ["xyzbc_switchkins.ngc", "boat-xyzbc.ngc"].map((filename) => ({
|
||||||
|
filename,
|
||||||
|
default: filename === profile.machineFileStaging?.defaultProgramFilename,
|
||||||
|
staged: staged.save.gcodeSources.some((source) => source.filename === filename),
|
||||||
|
sourceRel: staged.save.gcodeSources.find((source) => source.filename === filename)?.sourceRel || null,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPathSampling() {
|
||||||
|
return {
|
||||||
|
samplePeriodMs: SAMPLE_PERIOD_MS,
|
||||||
|
timeBase: "program-relative-ms",
|
||||||
|
resampling: "linear-position-slerp-or-axis-linear",
|
||||||
|
coordinateSystem: "machine-xyzbc-and-tcp",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function emptyPath(source, reason) {
|
||||||
|
return {
|
||||||
|
source,
|
||||||
|
samplePeriodMs: SAMPLE_PERIOD_MS,
|
||||||
|
status: "blocked",
|
||||||
|
unavailableReason: reason,
|
||||||
|
sampleCount: 0,
|
||||||
|
samples: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePathSample({
|
||||||
|
sampleIndex,
|
||||||
|
timeMs,
|
||||||
|
line,
|
||||||
|
motionType,
|
||||||
|
activeKinematics,
|
||||||
|
axes,
|
||||||
|
tool,
|
||||||
|
feed,
|
||||||
|
spindle,
|
||||||
|
}) {
|
||||||
|
const joint = {
|
||||||
|
x: numberOrZero(axes.x),
|
||||||
|
y: numberOrZero(axes.y),
|
||||||
|
z: numberOrZero(axes.z),
|
||||||
|
b: numberOrZero(axes.b),
|
||||||
|
c: numberOrZero(axes.c),
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
sampleIndex,
|
||||||
|
timeMs,
|
||||||
|
line: Number(line) || 0,
|
||||||
|
motionType,
|
||||||
|
activeKinematics,
|
||||||
|
tool,
|
||||||
|
joint,
|
||||||
|
tcp: {
|
||||||
|
x: joint.x,
|
||||||
|
y: joint.y,
|
||||||
|
z: joint.z,
|
||||||
|
},
|
||||||
|
toolAxis: toolAxisFromBc(joint.b, joint.c),
|
||||||
|
feed: numberOrZero(feed),
|
||||||
|
spindle: numberOrZero(spindle),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstTool(profile) {
|
||||||
|
const tool = profile.toolTable?.tools?.[0] || {};
|
||||||
|
return {
|
||||||
|
id: Number(tool.tool) || 0,
|
||||||
|
length: Number(tool.zOffset) || 0,
|
||||||
|
diameter: Number(tool.diameter) || 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function xyzbcAxesFromTaskHalStatus(status = {}) {
|
||||||
|
const axis = status.motionStatus?.axis || {};
|
||||||
|
const pins = status.halSnapshot?.pins || {};
|
||||||
|
return {
|
||||||
|
x: firstFiniteNumber(
|
||||||
|
axis.x,
|
||||||
|
pins["joint.0.motor-pos-fb"]?.value,
|
||||||
|
pins["joint.0.pos-fb"]?.value,
|
||||||
|
pins["axis.0.pos-fb"]?.value,
|
||||||
|
pins["joint.0.motor-pos-cmd"]?.value,
|
||||||
|
),
|
||||||
|
y: firstFiniteNumber(
|
||||||
|
axis.y,
|
||||||
|
pins["joint.1.motor-pos-fb"]?.value,
|
||||||
|
pins["joint.1.pos-fb"]?.value,
|
||||||
|
pins["axis.1.pos-fb"]?.value,
|
||||||
|
pins["joint.1.motor-pos-cmd"]?.value,
|
||||||
|
),
|
||||||
|
z: firstFiniteNumber(
|
||||||
|
axis.z,
|
||||||
|
pins["joint.2.motor-pos-fb"]?.value,
|
||||||
|
pins["joint.2.pos-fb"]?.value,
|
||||||
|
pins["axis.2.pos-fb"]?.value,
|
||||||
|
pins["joint.2.motor-pos-cmd"]?.value,
|
||||||
|
),
|
||||||
|
b: firstFiniteNumber(
|
||||||
|
axis.b,
|
||||||
|
pins["joint.3.motor-pos-fb"]?.value,
|
||||||
|
pins["joint.3.pos-fb"]?.value,
|
||||||
|
pins["axis.3.pos-fb"]?.value,
|
||||||
|
pins["joint.3.motor-pos-cmd"]?.value,
|
||||||
|
),
|
||||||
|
c: firstFiniteNumber(
|
||||||
|
axis.c,
|
||||||
|
pins["joint.4.motor-pos-fb"]?.value,
|
||||||
|
pins["joint.4.pos-fb"]?.value,
|
||||||
|
pins["axis.4.pos-fb"]?.value,
|
||||||
|
pins["joint.4.motor-pos-cmd"]?.value,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function spindleFromTaskHalStatus(status = {}) {
|
||||||
|
const pins = status.halSnapshot?.pins || {};
|
||||||
|
return firstFiniteNumber(
|
||||||
|
status.motionStatus?.motion?.spindleSpeed,
|
||||||
|
pins["spindle.0.speed-out"]?.value,
|
||||||
|
pins["motion.spindle-speed-out"]?.value,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentMotionTypeForLine(motion = [], line = 0) {
|
||||||
|
const number = Number(line);
|
||||||
|
if (!Number.isFinite(number) || number <= 0) return null;
|
||||||
|
return motion.find((event) => Number(event.line) === number)?.type || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function activeKinematics(event) {
|
||||||
|
if (event.kinsType === "identity" || event.switchkinsType === 0) return "identity";
|
||||||
|
if (event.kinsType === "tcp" || event.switchkinsType === 1) return "xyzbc-tcp";
|
||||||
|
if (event.kinsType === "userk" || event.switchkinsType === 2) return "userk";
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
function motionTypeFromCanonical(type) {
|
||||||
|
if (type === "STRAIGHT_TRAVERSE") return "G0";
|
||||||
|
if (type === "STRAIGHT_FEED") return "G1";
|
||||||
|
if (type === "ARC_FEED") return "G2/G3";
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
function toolAxisFromBc(bDeg, cDeg) {
|
||||||
|
const b = bDeg * Math.PI / 180;
|
||||||
|
const c = cDeg * Math.PI / 180;
|
||||||
|
return {
|
||||||
|
i: Math.sin(b) * Math.cos(c),
|
||||||
|
j: Math.sin(b) * Math.sin(c),
|
||||||
|
k: Math.cos(b),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberOrZero(value) {
|
||||||
|
const number = Number(value);
|
||||||
|
return Number.isFinite(number) ? number : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lerpNumber(left, right, ratio) {
|
||||||
|
const leftNumber = Number(left);
|
||||||
|
const rightNumber = Number(right);
|
||||||
|
if (!Number.isFinite(leftNumber)) return Number.isFinite(rightNumber) ? rightNumber : 0;
|
||||||
|
if (!Number.isFinite(rightNumber)) return leftNumber;
|
||||||
|
return leftNumber + (rightNumber - leftNumber) * ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstFiniteNumber(...values) {
|
||||||
|
for (const value of values) {
|
||||||
|
const number = Number(value);
|
||||||
|
if (Number.isFinite(number)) return number;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const outputPath = process.argv[4] || resolve(projectRoot, "working/evidence/com
|
|||||||
|
|
||||||
const nativeEvidence = JSON.parse(await readFile(nativePath, "utf8"));
|
const nativeEvidence = JSON.parse(await readFile(nativePath, "utf8"));
|
||||||
const webEvidence = JSON.parse(await readFile(webPath, "utf8"));
|
const webEvidence = JSON.parse(await readFile(webPath, "utf8"));
|
||||||
|
const pathComparison = comparePathEvidence(nativeEvidence, webEvidence);
|
||||||
|
|
||||||
const checks = [
|
const checks = [
|
||||||
check("profile", "native axis mask is XYZBC", nativeEvidence.coverage?.axisProfile === true, {
|
check("profile", "native axis mask is XYZBC", nativeEvidence.coverage?.axisProfile === true, {
|
||||||
@@ -48,6 +49,49 @@ const checks = [
|
|||||||
check("parameters", "parameter file staged", webEvidence.coverage?.parameterFileStaged === true, {
|
check("parameters", "parameter file staged", webEvidence.coverage?.parameterFileStaged === true, {
|
||||||
parameterFile: webEvidence.ini?.parameterFile,
|
parameterFile: webEvidence.ini?.parameterFile,
|
||||||
}),
|
}),
|
||||||
|
check("program", "web boat-xyzbc demo program staged", webEvidence.coverage?.boatProgramStaged === true, {
|
||||||
|
demoPrograms: webEvidence.demoPrograms,
|
||||||
|
}),
|
||||||
|
check("ngcgui", "web Ngcgui/remap subroutines staged", webEvidence.coverage?.ngcguiSubroutinesStaged === true, {
|
||||||
|
ngcguiSubroutines: webEvidence.ngcguiSubroutines,
|
||||||
|
}),
|
||||||
|
check("postgui-hal", "web PyVCP to HALUI POSTGUI nets represented", webEvidence.coverage?.postguiHalEquivalent === true, {
|
||||||
|
halNets: webEvidence.halNets,
|
||||||
|
}),
|
||||||
|
check("kinematics", "web kinematics HAL pins represented", webEvidence.coverage?.kinematicsPinsCovered === true, {
|
||||||
|
kinematicsPins: webEvidence.kinematicsPins,
|
||||||
|
}),
|
||||||
|
check("limits", "web TRAJ/AXIS/JOINT limits represented", webEvidence.coverage?.axisJointLimitsCovered === true, {
|
||||||
|
axisJointLimits: webEvidence.axisJointLimits,
|
||||||
|
}),
|
||||||
|
check("path-preview", "native and web preview path sample period is 20ms", pathComparison.previewVsPreview.periodsMatch === true, {
|
||||||
|
nativeSamplePeriodMs: pathComparison.previewVsPreview.nativeSamplePeriodMs,
|
||||||
|
webSamplePeriodMs: pathComparison.previewVsPreview.webSamplePeriodMs,
|
||||||
|
}),
|
||||||
|
check("path-preview", "native and web preview paths have samples", pathComparison.previewVsPreview.comparable === true, {
|
||||||
|
nativeSampleCount: pathComparison.previewVsPreview.nativeSampleCount,
|
||||||
|
webSampleCount: pathComparison.previewVsPreview.webSampleCount,
|
||||||
|
unavailable: pathComparison.previewVsPreview.unavailable,
|
||||||
|
}),
|
||||||
|
check("path-execution", "native and web execution path sample period is 20ms", pathComparison.executionVsExecution.periodsMatch === true, {
|
||||||
|
nativeSamplePeriodMs: pathComparison.executionVsExecution.nativeSamplePeriodMs,
|
||||||
|
webSamplePeriodMs: pathComparison.executionVsExecution.webSamplePeriodMs,
|
||||||
|
}),
|
||||||
|
check("path-execution", "native and web execution paths have samples", pathComparison.executionVsExecution.comparable === true, {
|
||||||
|
nativeSampleCount: pathComparison.executionVsExecution.nativeSampleCount,
|
||||||
|
webSampleCount: pathComparison.executionVsExecution.webSampleCount,
|
||||||
|
unavailable: pathComparison.executionVsExecution.unavailable,
|
||||||
|
}),
|
||||||
|
check("path-preview-execution-consistency", "native preview and execution paths are comparable", pathComparison.previewVsExecutionNative.comparable === true, {
|
||||||
|
nativePreviewSampleCount: pathComparison.previewVsExecutionNative.leftSampleCount,
|
||||||
|
nativeExecutionSampleCount: pathComparison.previewVsExecutionNative.rightSampleCount,
|
||||||
|
unavailable: pathComparison.previewVsExecutionNative.unavailable,
|
||||||
|
}),
|
||||||
|
check("path-preview-execution-consistency", "web preview and execution paths are comparable", pathComparison.previewVsExecutionWeb.comparable === true, {
|
||||||
|
webPreviewSampleCount: pathComparison.previewVsExecutionWeb.leftSampleCount,
|
||||||
|
webExecutionSampleCount: pathComparison.previewVsExecutionWeb.rightSampleCount,
|
||||||
|
unavailable: pathComparison.previewVsExecutionWeb.unavailable,
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
const failed = checks.filter((item) => item.status !== "pass");
|
const failed = checks.filter((item) => item.status !== "pass");
|
||||||
@@ -71,6 +115,7 @@ const report = {
|
|||||||
webStatus: webEvidence.status,
|
webStatus: webEvidence.status,
|
||||||
},
|
},
|
||||||
checks,
|
checks,
|
||||||
|
pathComparison,
|
||||||
requiredImprovements: failed.map((item) => ({
|
requiredImprovements: failed.map((item) => ({
|
||||||
category: item.category,
|
category: item.category,
|
||||||
requirement: item.requirement,
|
requirement: item.requirement,
|
||||||
@@ -96,3 +141,137 @@ function check(category, requirement, passed, evidence = {}) {
|
|||||||
evidence,
|
evidence,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function comparePathEvidence(nativeEvidence, webEvidence) {
|
||||||
|
const samplePeriodMs = 20;
|
||||||
|
return {
|
||||||
|
samplePeriodMs,
|
||||||
|
previewVsPreview: compareNamedPaths({
|
||||||
|
left: nativeEvidence.previewPath,
|
||||||
|
right: webEvidence.previewPath,
|
||||||
|
leftName: "native",
|
||||||
|
rightName: "web",
|
||||||
|
expectedSamplePeriodMs: samplePeriodMs,
|
||||||
|
}),
|
||||||
|
executionVsExecution: compareNamedPaths({
|
||||||
|
left: nativeEvidence.executionPath,
|
||||||
|
right: webEvidence.executionPath,
|
||||||
|
leftName: "native",
|
||||||
|
rightName: "web",
|
||||||
|
expectedSamplePeriodMs: samplePeriodMs,
|
||||||
|
}),
|
||||||
|
previewVsExecutionNative: compareNamedPaths({
|
||||||
|
left: nativeEvidence.previewPath,
|
||||||
|
right: nativeEvidence.executionPath,
|
||||||
|
leftName: "nativePreview",
|
||||||
|
rightName: "nativeExecution",
|
||||||
|
expectedSamplePeriodMs: samplePeriodMs,
|
||||||
|
}),
|
||||||
|
previewVsExecutionWeb: compareNamedPaths({
|
||||||
|
left: webEvidence.previewPath,
|
||||||
|
right: webEvidence.executionPath,
|
||||||
|
leftName: "webPreview",
|
||||||
|
rightName: "webExecution",
|
||||||
|
expectedSamplePeriodMs: samplePeriodMs,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareNamedPaths({ left, right, leftName, rightName, expectedSamplePeriodMs }) {
|
||||||
|
const leftSamplePeriodMs = left?.samplePeriodMs ?? null;
|
||||||
|
const rightSamplePeriodMs = right?.samplePeriodMs ?? null;
|
||||||
|
const periodsMatch = leftSamplePeriodMs === expectedSamplePeriodMs
|
||||||
|
&& rightSamplePeriodMs === expectedSamplePeriodMs;
|
||||||
|
const leftSamples = Array.isArray(left?.samples) ? left.samples : [];
|
||||||
|
const rightSamples = Array.isArray(right?.samples) ? right.samples : [];
|
||||||
|
const comparable = periodsMatch && leftSamples.length > 0 && rightSamples.length > 0;
|
||||||
|
const unavailable = [
|
||||||
|
...(leftSamples.length > 0 ? [] : [`${leftName}: ${left?.unavailableReason || "missing samples"}`]),
|
||||||
|
...(rightSamples.length > 0 ? [] : [`${rightName}: ${right?.unavailableReason || "missing samples"}`]),
|
||||||
|
...(periodsMatch ? [] : [`sample period mismatch ${leftSamplePeriodMs}/${rightSamplePeriodMs}`]),
|
||||||
|
];
|
||||||
|
const stats = comparable ? pathStats(leftSamples, rightSamples) : emptyStats(leftSamples, rightSamples);
|
||||||
|
return {
|
||||||
|
status: comparable ? "pass" : "fail",
|
||||||
|
comparable,
|
||||||
|
periodsMatch,
|
||||||
|
[`${leftName}SamplePeriodMs`]: leftSamplePeriodMs,
|
||||||
|
[`${rightName}SamplePeriodMs`]: rightSamplePeriodMs,
|
||||||
|
nativeSamplePeriodMs: leftName === "native" ? leftSamplePeriodMs : undefined,
|
||||||
|
webSamplePeriodMs: rightName === "web" ? rightSamplePeriodMs : undefined,
|
||||||
|
leftSampleCount: leftSamples.length,
|
||||||
|
rightSampleCount: rightSamples.length,
|
||||||
|
nativeSampleCount: leftName === "native" ? leftSamples.length : undefined,
|
||||||
|
webSampleCount: rightName === "web" ? rightSamples.length : undefined,
|
||||||
|
unavailable,
|
||||||
|
...stats,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function pathStats(leftSamples, rightSamples) {
|
||||||
|
const count = Math.min(leftSamples.length, rightSamples.length);
|
||||||
|
const missingSamples = [];
|
||||||
|
let maxTcpErrorMm = 0;
|
||||||
|
let sumTcpErrorSquared = 0;
|
||||||
|
let maxJointError = 0;
|
||||||
|
let sumJointErrorSquared = 0;
|
||||||
|
let maxToolAxisAngleDeg = 0;
|
||||||
|
for (let index = 0; index < count; index += 1) {
|
||||||
|
const left = leftSamples[index];
|
||||||
|
const right = rightSamples[index];
|
||||||
|
if (left.sampleIndex !== right.sampleIndex || left.timeMs !== right.timeMs) {
|
||||||
|
missingSamples.push({ index, leftSampleIndex: left.sampleIndex, rightSampleIndex: right.sampleIndex });
|
||||||
|
}
|
||||||
|
const tcpError = vectorError(left.tcp, right.tcp, ["x", "y", "z"]);
|
||||||
|
const jointError = vectorError(left.joint, right.joint, ["x", "y", "z", "b", "c"]);
|
||||||
|
const angleError = toolAxisAngleDeg(left.toolAxis, right.toolAxis);
|
||||||
|
maxTcpErrorMm = Math.max(maxTcpErrorMm, tcpError);
|
||||||
|
sumTcpErrorSquared += tcpError ** 2;
|
||||||
|
maxJointError = Math.max(maxJointError, jointError);
|
||||||
|
sumJointErrorSquared += jointError ** 2;
|
||||||
|
maxToolAxisAngleDeg = Math.max(maxToolAxisAngleDeg, angleError);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
maxTcpErrorMm,
|
||||||
|
rmsTcpErrorMm: count > 0 ? Math.sqrt(sumTcpErrorSquared / count) : 0,
|
||||||
|
maxJointError,
|
||||||
|
rmsJointError: count > 0 ? Math.sqrt(sumJointErrorSquared / count) : 0,
|
||||||
|
maxToolAxisAngleDeg,
|
||||||
|
sampleCountDelta: Math.abs(leftSamples.length - rightSamples.length),
|
||||||
|
missingSamples,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function emptyStats(leftSamples, rightSamples) {
|
||||||
|
return {
|
||||||
|
maxTcpErrorMm: null,
|
||||||
|
rmsTcpErrorMm: null,
|
||||||
|
maxJointError: null,
|
||||||
|
rmsJointError: null,
|
||||||
|
maxToolAxisAngleDeg: null,
|
||||||
|
sampleCountDelta: Math.abs(leftSamples.length - rightSamples.length),
|
||||||
|
missingSamples: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function vectorError(left = {}, right = {}, keys = []) {
|
||||||
|
return Math.sqrt(keys.reduce((sum, key) => (
|
||||||
|
sum + (numberOrZero(left[key]) - numberOrZero(right[key])) ** 2
|
||||||
|
), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function toolAxisAngleDeg(left = {}, right = {}) {
|
||||||
|
const dot = numberOrZero(left.i) * numberOrZero(right.i)
|
||||||
|
+ numberOrZero(left.j) * numberOrZero(right.j)
|
||||||
|
+ numberOrZero(left.k) * numberOrZero(right.k);
|
||||||
|
const leftLen = vectorError(left, { i: 0, j: 0, k: 0 }, ["i", "j", "k"]);
|
||||||
|
const rightLen = vectorError(right, { i: 0, j: 0, k: 0 }, ["i", "j", "k"]);
|
||||||
|
if (leftLen <= 0 || rightLen <= 0) return 0;
|
||||||
|
const cosine = Math.max(-1, Math.min(1, dot / (leftLen * rightLen)));
|
||||||
|
return Math.acos(cosine) * 180 / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberOrZero(value) {
|
||||||
|
const number = Number(value);
|
||||||
|
return Number.isFinite(number) ? number : 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,89 @@
|
|||||||
# 03-推进台账
|
# 03-推进台账
|
||||||
|
|
||||||
|
## 2026-07-02 Web task/HAL executionPath 采集实现轮次
|
||||||
|
|
||||||
|
### 本轮目标
|
||||||
|
|
||||||
|
接续 `working` 任务矩阵,补齐 Web 侧 `executionPath.samples`,让 Web evidence 的预览路径和执行路径都使用 20ms 采样周期,并重新生成 compare 证据。
|
||||||
|
|
||||||
|
### 已做事项
|
||||||
|
|
||||||
|
- 修改 `tools/collect-web-xyzbc-trt-evidence.mjs`:
|
||||||
|
- 复用 `linuxcnc_interp` 生成 machine-file canonical motion 和 TP timing。
|
||||||
|
- 将 TP planner 原始样本重采样为 20ms `previewPath.samples`。
|
||||||
|
- 将同一 motion plan 装入 `linuxcnc_task_hal` WASM。
|
||||||
|
- 按 20ms task cycle 读取 task/HAL status,生成 `executionPath.samples`。
|
||||||
|
- 对 `xyzbc-trt` 显式按 `joint.0/1/2/3/4` 映射 `X/Y/Z/B/C`,避免 B/C 轴被 XYZAC 兼容字段误映射。
|
||||||
|
- 扩展 `tests/node/verify_xyzbc_trt_web_app.mjs`,增加 interpreter + task/HAL 执行反馈 smoke 断言。
|
||||||
|
- 重新生成:
|
||||||
|
- `working/evidence/web-xyzbc-trt-evidence.json`
|
||||||
|
- `working/evidence/compare-xyzbc-trt-evidence.json`
|
||||||
|
- 更新 `04-任务矩阵.md`,将 T-022 标记为完成,并更新 T-017/T-021 的当前样本和通过数。
|
||||||
|
|
||||||
|
### 验证情况
|
||||||
|
|
||||||
|
- `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web` 通过。
|
||||||
|
- `web-xyzbc-trt-evidence.json` 当前摘要:
|
||||||
|
- `wasm.ready=true`
|
||||||
|
- `blockers=[]`
|
||||||
|
- `previewPath.samplePeriodMs=20`
|
||||||
|
- `previewPath.sampleCount=3074`
|
||||||
|
- `executionPath.samplePeriodMs=20`
|
||||||
|
- `executionPath.sampleCount=569`
|
||||||
|
- `executionPath.source=web-linuxcnc-task-hal-execution`
|
||||||
|
- `executionPath.taskHal.completed=true`
|
||||||
|
- `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare` 已生成真实失败报告:当前 18/23 通过,剩余 5 项均为 native evidence 缺少 `previewPath`/`executionPath` 样本和 20ms 周期字段。
|
||||||
|
- `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node` 通过。
|
||||||
|
- `npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run build` 通过,输出 `gmoccapy_static_build=ok`。
|
||||||
|
|
||||||
|
### 下一步
|
||||||
|
|
||||||
|
1. 在 `/home/mes123456/cnc_wams/linuxcnc/scripts/rip-environment` 下重跑 native evidence,使 `native-xyzbc-trt-evidence.json` 写入 `pathSampling`、`previewPath` 和 20ms `executionPath`。
|
||||||
|
2. 重跑 Web evidence 和 compare,使剩余 native path 相关失败进入可比误差统计。
|
||||||
|
3. 继续目标浏览器 smoke,验证 worker runtime ready 和 canvas 非空。
|
||||||
|
|
||||||
|
## 2026-07-02 evidence 字段与路径对比实现轮次
|
||||||
|
|
||||||
|
### 本轮目标
|
||||||
|
|
||||||
|
按 `working` 目录继续编写 `xyzbc-trt` Web 数控仿真程序,优先补齐可以在当前环境闭环验证的 evidence JSON、路径对比和 staging 覆盖。
|
||||||
|
|
||||||
|
### 已做事项
|
||||||
|
|
||||||
|
- 将 `xyzbc-trt` profile 的 `samplePrograms` 修正为 `xyzbc_switchkins.ngc` 和 `boat-xyzbc.ngc`。
|
||||||
|
- 扩展 Web evidence:新增 `pathSampling`、`previewPath`、`executionPath`、`startupSequence`、`iniDisplay`、`halNets`、`kinematicsPins`、`axisJointLimits`、`switchkinsTransitions`、`uiEquivalence`、`vismachEquivalent`、`ngcguiSubroutines`、`demoPrograms`。
|
||||||
|
- 扩展 compare evidence:新增 `pathComparison`,覆盖 `previewVsPreview`、`executionVsExecution`、`previewVsExecutionNative`、`previewVsExecutionWeb`,并输出 TCP、joint、toolAxis、样本差异和缺样本统计。
|
||||||
|
- 修改 native evidence 脚本默认基线为 `/home/mes123456/cnc_wams/linuxcnc`,并在 `--run` 时可把 `stat()` 执行事件重采样为 20ms `executionPath`。
|
||||||
|
- 扩展 Node smoke,检查 `samplePrograms`、Ngcgui 子程序和 `boat-xyzbc.ngc` staging。
|
||||||
|
- 重新生成 Web evidence 和 compare evidence。当前 compare 为真实 fail:WASM artifact 缺失,native/Web path 样本仍不可比。
|
||||||
|
|
||||||
|
### 修改文件
|
||||||
|
|
||||||
|
```text
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/app/src/profiles/xyzbc-trt.js
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_xyzbc_trt_web_app.mjs
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/tools/collect-web-xyzbc-trt-evidence.mjs
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/tools/collect-native-xyzbc-trt-evidence.py
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/tools/compare-xyzbc-trt-evidence.mjs
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/web-xyzbc-trt-evidence.json
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/compare-xyzbc-trt-evidence.json
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/working/04-任务矩阵.md
|
||||||
|
```
|
||||||
|
|
||||||
|
### 验证情况
|
||||||
|
|
||||||
|
- 已通过:`npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node`。
|
||||||
|
- 已通过:`python3 -m py_compile web-rtcp-5axis-xyzbc-trt-sim-plan/tools/collect-native-xyzbc-trt-evidence.py`。
|
||||||
|
- 已通过生成:`npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web`。
|
||||||
|
- 已生成真实失败 compare:`npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare`,当前 23 项检查中 16 项通过,7 项失败均指向 WASM artifact 和 path 样本缺口。
|
||||||
|
- 已确认 `npm run build` 仍因 `wasm-port/build/wasm/kinematics` 缺失失败;该结果符合当前 WASM artifact 前置状态。
|
||||||
|
|
||||||
|
### 下一步
|
||||||
|
|
||||||
|
1. 构建 `wasm-port/build/wasm` 的 kinematics/core/tp/task-hal artifact。
|
||||||
|
2. 在 `/home/mes123456/cnc_wams/linuxcnc/scripts/rip-environment` 下重跑 native evidence,使 `sourceRoot` 和 path 字段进入 `native-xyzbc-trt-evidence.json`。
|
||||||
|
3. 重跑 Web evidence 和 compare,确认 pathComparison 从 blocker 转为可比误差统计。
|
||||||
|
|
||||||
## 2026-07-02 07:31 EDT - DOCX 设计任务书整合到 working
|
## 2026-07-02 07:31 EDT - DOCX 设计任务书整合到 working
|
||||||
|
|
||||||
### 本轮目标
|
### 本轮目标
|
||||||
@@ -278,3 +362,74 @@ web-rtcp-5axis-xyzbc-trt-sim-plan/working/06-决策记录.md
|
|||||||
1. 用当前运行的新 native 实例重采集 `native-xyzbc-trt-evidence.json`。
|
1. 用当前运行的新 native 实例重采集 `native-xyzbc-trt-evidence.json`。
|
||||||
2. 将现有历史 `/home/mes123456/linuxcnc-master` evidence 替换为 `/home/mes123456/cnc_wams/linuxcnc` evidence。
|
2. 将现有历史 `/home/mes123456/linuxcnc-master` evidence 替换为 `/home/mes123456/cnc_wams/linuxcnc` evidence。
|
||||||
3. 基于新 evidence 重跑 Web compare。
|
3. 基于新 evidence 重跑 Web compare。
|
||||||
|
|
||||||
|
## 2026-07-02 生成 WASM artifact
|
||||||
|
|
||||||
|
### 本轮目标
|
||||||
|
|
||||||
|
为 `web-rtcp-5axis-xyzbc-trt-sim-plan` 生成缺失的 `wasm-port/build/wasm` artifact,使 Web evidence 不再因缺少 `.js/.wasm` 构建产物而 blocked。
|
||||||
|
|
||||||
|
### 已做事项
|
||||||
|
|
||||||
|
- 显式加载 `/home/mes123456/emsdk/emsdk_env.sh`,确认 `emcc 6.0.2` 可用。
|
||||||
|
- 执行以下构建脚本:
|
||||||
|
- `wasm-port/tools/build_wasm_core.sh`
|
||||||
|
- `wasm-port/tools/build_kinematics_wasm.sh`
|
||||||
|
- `wasm-port/tools/build_tp_wasm.sh`
|
||||||
|
- `wasm-port/tools/build_task_hal_wasm.sh`
|
||||||
|
- 已生成目标项目所需 artifact:
|
||||||
|
- `wasm-port/build/wasm/core/linuxcnc_interp.js`
|
||||||
|
- `wasm-port/build/wasm/core/linuxcnc_interp.wasm`
|
||||||
|
- `wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.js`
|
||||||
|
- `wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.wasm`
|
||||||
|
- `wasm-port/build/wasm/tp/linuxcnc_tp.js`
|
||||||
|
- `wasm-port/build/wasm/tp/linuxcnc_tp.wasm`
|
||||||
|
- `wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js`
|
||||||
|
- `wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm`
|
||||||
|
- `wasm-port/build/wasm` 下当前共有 40 个 `.js/.wasm` artifact。
|
||||||
|
|
||||||
|
### 验证情况
|
||||||
|
|
||||||
|
- `node tests/node/verify_xyzbc_trt_web_app.mjs` 通过。
|
||||||
|
- `SKIP_KINEMATICS_BUILD=1 wasm-port/tests/wasm/node/verify_kinematics_wasm.sh` 通过,包含 `xyzbc-trt` kinematics smoke。
|
||||||
|
- `SKIP_TP_BUILD=1 wasm-port/tests/wasm/node/verify_tp_wasm.sh` 通过。
|
||||||
|
- `SKIP_TASK_HAL_BUILD=1 wasm-port/tests/wasm/node/verify_task_hal_wasm.sh` 通过。
|
||||||
|
- 历史状态:`SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh` 当时未通过,原因是 `interp_g10_l11_wasm` 的 G10 L11 期望值过期;该项已在后续“完整 interpreter/remap 验收”轮次修复。
|
||||||
|
- 重新运行 `node tools/collect-web-xyzbc-trt-evidence.mjs`,`web-xyzbc-trt-evidence.json` 显示 `wasmArtifactsReady=true`、`missing=[]`,并生成 Web preview path 6174 个 20ms 样本。
|
||||||
|
- 重新运行 `node tools/compare-xyzbc-trt-evidence.mjs`,当前 17/23 通过;剩余失败项集中在 native preview/execution path 样本缺失,以及 Web execution path collector 尚未实现。
|
||||||
|
|
||||||
|
### 下一步
|
||||||
|
|
||||||
|
1. 已完成:`verify_interp_wasm.sh` 中 `interp_g10_l11_wasm` 的 G10 L11 断言已对齐 vendored LinuxCNC expected。
|
||||||
|
2. 用 `/home/mes123456/cnc_wams/linuxcnc` 当前 native 实例重采集 `native-xyzbc-trt-evidence.json` 的 preview/execution path。
|
||||||
|
3. 实现 Web task/HAL execution path 采集,补齐 `web-xyzbc-trt-evidence.json.executionPath.samples`。
|
||||||
|
|
||||||
|
## 2026-07-02 完整 interpreter/remap 验收
|
||||||
|
|
||||||
|
### 本轮目标
|
||||||
|
|
||||||
|
完成 `wasm-port/tests/wasm/node/verify_interp_wasm.sh` 的 interpreter/remap WASM 验收,使 `linuxcnc_interp.js/.wasm` 不仅生成,而且通过完整 Node 回归。
|
||||||
|
|
||||||
|
### 已做事项
|
||||||
|
|
||||||
|
- 单独复现 `interp_g10_l11_wasm`,打印实际 canonical 输出。
|
||||||
|
- 对照 `wasm-port/vendor/linuxcnc/tests/interp/g10/g10-l11/expected`,确认 WASM 实际输出与 vendored LinuxCNC 当前 expected 一致:
|
||||||
|
- `SET_G92_OFFSET(-43.0622, -47.4282, -72.0000)`
|
||||||
|
- 修正 `wasm-port/tests/wasm/node/verify_interp_wasm.mjs` 中过期的 G10 L11 断言:
|
||||||
|
- 旧值:`x=-41.1962 y=-46.1962 z=-72`
|
||||||
|
- 新值:`x=-43.0622 y=-47.4282 z=-72`
|
||||||
|
|
||||||
|
### 验证情况
|
||||||
|
|
||||||
|
- `SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh` 通过,输出 `interp_wasm_node_smoke=ok`。
|
||||||
|
- `wasm-port/tests/wasm/node/verify_interp_wasm.sh` 通过,输出 `interp_wasm_node_smoke=ok`。
|
||||||
|
- 运行中仍有既有提示:
|
||||||
|
- `link (updating variable file): No such file or directory`
|
||||||
|
- `G88.1` 与 `M410` remap modalgroup warning
|
||||||
|
- 上述提示不影响退出码,完整 interpreter/remap 验收已通过。
|
||||||
|
|
||||||
|
### 下一步
|
||||||
|
|
||||||
|
1. 继续 native preview/execution path 重采集。
|
||||||
|
2. 实现 Web task/HAL execution path collector。
|
||||||
|
3. 重跑 Web/native compare,使剩余 6 个 path 相关失败项进入可比误差统计。
|
||||||
|
|||||||
@@ -12,33 +12,33 @@
|
|||||||
| T-008 | Stage remap/tool/PyVCP 文件 | 完成 | staging 包含 `428remap.ngc`、`429remap.ngc`、`430remap.ngc`、`xyzbc-trt.tbl`、`xyzbc-trt.xml` |
|
| T-008 | Stage remap/tool/PyVCP 文件 | 完成 | staging 包含 `428remap.ngc`、`429remap.ngc`、`430remap.ngc`、`xyzbc-trt.tbl`、`xyzbc-trt.xml` |
|
||||||
| T-009 | 增加目标 Node smoke | 完成 | `tests/node/verify_xyzbc_trt_web_app.mjs` 存在并通过 |
|
| T-009 | 增加目标 Node smoke | 完成 | `tests/node/verify_xyzbc_trt_web_app.mjs` 存在并通过 |
|
||||||
| T-010 | 导入 `xyzbc.var` 基线参数文件 | 完成 | `wasm-port/vendor`/manifest 包含 `xyzbc.var`,OPFS staging 可保存参数文件 |
|
| T-010 | 导入 `xyzbc.var` 基线参数文件 | 完成 | `wasm-port/vendor`/manifest 包含 `xyzbc.var`,OPFS staging 可保存参数文件 |
|
||||||
| T-011 | 完整 kinematics WASM 验证 | 待前置 | `wasm-port/tests/wasm/node/verify_kinematics_wasm.sh` 通过 |
|
| T-011 | 完整 kinematics WASM 验证 | 完成 | `SKIP_KINEMATICS_BUILD=1 wasm-port/tests/wasm/node/verify_kinematics_wasm.sh` 通过,覆盖 `xyzbc-trt` |
|
||||||
| T-012 | 完整 interpreter/remap WASM 验证 | 待前置 | `wasm-port/tests/wasm/node/verify_interp_wasm.sh` 通过并覆盖 `xyzbc_switchkins.ngc` |
|
| T-012 | 完整 interpreter/remap WASM 验证 | 完成 | `wasm-port/tests/wasm/node/verify_interp_wasm.sh` 通过,输出 `interp_wasm_node_smoke=ok` |
|
||||||
| T-013 | 完整 task/HAL WASM 验证 | 待前置 | `wasm-port/tests/wasm/node/verify_task_hal_wasm.sh` 通过 |
|
| T-013 | 完整 task/HAL WASM 验证 | 完成 | `SKIP_TASK_HAL_BUILD=1 wasm-port/tests/wasm/node/verify_task_hal_wasm.sh` 通过 |
|
||||||
| T-014 | 目标浏览器 smoke | 待前置 | 页面默认显示 `xyzbc-trt`,worker runtime ready,canvas 非空 |
|
| T-014 | 目标浏览器 smoke | 待前置 | 页面默认显示 `xyzbc-trt`,worker runtime ready,canvas 非空 |
|
||||||
| T-015 | native 真实执行 JSON 采集 | 完成 | `native-xyzbc-trt-evidence.json` 记录真实执行事件,状态 completed |
|
| T-015 | native 真实执行 JSON 采集 | 完成 | `native-xyzbc-trt-evidence.json` 记录真实执行事件,状态 completed |
|
||||||
| T-016 | Web OPFS/WASM readiness JSON 采集 | 完成 | `web-xyzbc-trt-evidence.json` 记录 Web profile/INI/staging/WASM readiness |
|
| T-016 | Web OPFS/WASM readiness JSON 采集 | 完成 | `web-xyzbc-trt-evidence.json` 记录 Web profile/INI/staging/WASM readiness |
|
||||||
| T-017 | native/Web JSON 对比 | 部分完成 | `compare-xyzbc-trt-evidence.json` 已生成;当前 11/12 通过,剩余 WASM artifact |
|
| T-017 | native/Web JSON 对比 | 部分完成 | `compare-xyzbc-trt-evidence.json` 已生成;当前 18/23 通过,剩余 5 项均为 native preview/execution path 样本缺失 |
|
||||||
| T-018 | WASM artifact 构建前置 | 待前置 | `emcc 6.0.2` 已可用,需运行构建脚本让 `wasm-port/build/wasm` 生成所需 `.js/.wasm` |
|
| T-018 | WASM artifact 构建前置 | 完成 | 已生成 core/kinematics/tp/task-hal 所需 `.js/.wasm`,`web-xyzbc-trt-evidence.json.wasm.missing=[]` |
|
||||||
| T-019 | native 刀具预览路径 JSON 采集 | 待实现 | `native-xyzbc-trt-evidence.json.previewPath.samples` 使用 20ms 周期记录 LinuxCNC 预览刀路 |
|
| T-019 | native 刀具预览路径 JSON 采集 | 待实现 | `native-xyzbc-trt-evidence.json.previewPath.samples` 使用 20ms 周期记录 LinuxCNC 预览刀路 |
|
||||||
| T-020 | native 刀具执行路径 JSON 采集 | 待实现 | `native-xyzbc-trt-evidence.json.executionPath.samples` 使用 20ms 周期记录真实执行反馈路径 |
|
| T-020 | native 刀具执行路径 JSON 采集 | 部分完成 | native 脚本已从 `stat()` 执行事件重采样为 20ms `executionPath`;需用新基线重跑生成证据 |
|
||||||
| T-021 | Web 刀具预览路径 JSON 采集 | 待实现 | `web-xyzbc-trt-evidence.json.previewPath.samples` 使用 20ms 周期记录 Web 预览刀路 |
|
| T-021 | Web 刀具预览路径 JSON 采集 | 完成 | `web-xyzbc-trt-evidence.json.previewPath.samples` 已由 `linuxcnc_interp`/TP WASM 生成 3074 个 20ms 重采样样本 |
|
||||||
| T-022 | Web 刀具执行路径 JSON 采集 | 待前置 | `web-xyzbc-trt-evidence.json.executionPath.samples` 使用 20ms 周期记录 WASM task/HAL 执行反馈路径 |
|
| T-022 | Web 刀具执行路径 JSON 采集 | 完成 | `web-xyzbc-trt-evidence.json.executionPath.samples` 已由 WASM task/HAL 执行反馈生成 569 个 20ms 样本 |
|
||||||
| T-023 | 刀路采样周期一致性检查 | 待实现 | compare 检查 native/Web `pathSampling.samplePeriodMs === 20`,否则 fail |
|
| T-023 | 刀路采样周期一致性检查 | 完成 | compare 检查 native/Web path `samplePeriodMs === 20`,否则 fail |
|
||||||
| T-024 | 刀路误差统计对比 | 待实现 | compare 输出 preview/execution 的 TCP、joint、toolAxis 误差统计和缺样本清单 |
|
| T-024 | 刀路误差统计对比 | 完成 | compare 输出 preview/execution 的 TCP、joint、toolAxis 误差统计和缺样本清单 |
|
||||||
| T-025 | 全量对标追踪矩阵 | 完成 | `07-全量对标追踪矩阵.md` 逐项映射 `xyzbc-trt-runtime-files.md` 的运行功能 |
|
| T-025 | 全量对标追踪矩阵 | 完成 | `07-全量对标追踪矩阵.md` 逐项映射 `xyzbc-trt-runtime-files.md` 的运行功能 |
|
||||||
| T-026 | AXIS 主界面等效功能 | 待实现 | Web 首屏包含程序、坐标、状态、MDI/switchkins、override、工具和预览/执行区域 |
|
| T-026 | AXIS 主界面等效功能 | 待实现 | Web 首屏包含程序、坐标、状态、MDI/switchkins、override、工具和预览/执行区域 |
|
||||||
| T-027 | POSTGUI HAL/PyVCP 连接等效 | 待实现 | JSON 记录 `pyvcp.* -> halui.mdi-command-* -> M428/M429/M430` 和 multilabel 状态 |
|
| T-027 | POSTGUI HAL/PyVCP 连接等效 | 完成 | Web evidence JSON 记录 `pyvcp.* -> halui.mdi-command-* -> M428/M429/M430` 和 multilabel 状态 |
|
||||||
| T-028 | basic_sim 等效 task/HAL | 待前置 | Web task/HAL runtime 覆盖模拟回零、manual toolchange、spindle、joint feedback |
|
| T-028 | basic_sim 等效 task/HAL | 待前置 | Web task/HAL runtime 覆盖模拟回零、manual toolchange、spindle、joint feedback |
|
||||||
| T-029 | Vismach 3D 模型 pin 对标 | 待实现 | Web 3D 模型由 `table-x/saddle-y/spindle-z/tilt-b/rotate-c/tool-offset/x-offset/z-offset` 驱动 |
|
| T-029 | Vismach 3D 模型 pin 对标 | 待实现 | Web 3D 模型由 `table-x/saddle-y/spindle-z/tilt-b/rotate-c/tool-offset/x-offset/z-offset` 驱动 |
|
||||||
| T-030 | Ngcgui/remap 子程序全集 | 待实现 | staging 和执行覆盖 `xyzbc_switchkins_sub.ngc`、`centering.ngc`、`helix_bc.ngc` |
|
| T-030 | Ngcgui/remap 子程序全集 | 部分完成 | staging 和 Web evidence 覆盖 `xyzbc_switchkins_sub.ngc`、`centering.ngc`、`helix_bc.ngc`;执行仍待 WASM |
|
||||||
| T-031 | 演示程序全集 | 待实现 | staging 和 UI 程序选择覆盖 `xyzbc_switchkins.ngc` 与 `boat-xyzbc.ngc` |
|
| T-031 | 演示程序全集 | 完成 | staging、profile samplePrograms 和 Web evidence 覆盖 `xyzbc_switchkins.ngc` 与 `boat-xyzbc.ngc` |
|
||||||
| T-032 | TRAJ/AXIS/JOINT 限制对标 | 待实现 | JSON/UI 覆盖 XYZBC 单位、速度/加速度、B/C 限位、JOG_AXES、GEOMETRY |
|
| T-032 | TRAJ/AXIS/JOINT 限制对标 | 部分完成 | Web evidence 覆盖 XYZBC 单位、速度/加速度、B/C 限位、JOG_AXES、GEOMETRY;UI 全量复核仍待浏览器/WASM |
|
||||||
| T-033 | kinematics HAL pins 对标 | 待实现 | JSON 记录 `x-offset=-20`、`z-offset=-15`、rot-point、conventional-directions、tool-offset |
|
| T-033 | kinematics HAL pins 对标 | 完成 | Web/native evidence 脚本记录 `x-offset=-20`、`z-offset=-15`、rot-point、conventional-directions、tool-offset |
|
||||||
| T-034 | tool table 到 tool-offset 闭环 | 待实现 | T/P/Z/D 解析影响 kinematics tool-offset、Web 模型刀长和 path JSON |
|
| T-034 | tool table 到 tool-offset 闭环 | 待实现 | T/P/Z/D 解析影响 kinematics tool-offset、Web 模型刀长和 path JSON |
|
||||||
| T-035 | 全量 evidence JSON 字段 | 待实现 | native/Web JSON 包含 `startupSequence`、`iniDisplay`、`halNets`、`kinematicsPins`、`axisJointLimits`、`uiEquivalence` |
|
| T-035 | 全量 evidence JSON 字段 | 部分完成 | Web JSON 已包含 `startupSequence`、`iniDisplay`、`halNets`、`kinematicsPins`、`axisJointLimits`、`uiEquivalence`;native 脚本已补字段,需重采集 |
|
||||||
| T-036 | native 基线切换到 `/home/mes123456/cnc_wams/linuxcnc` | 完成 | `scripts/rip-environment`、`bin/axis`、`bin/xyzbc-trt-gui`、`rtlib/xyzbc-trt-kins.so` 已生成,RIP 环境可导入 `linuxcnc` Python 模块 |
|
| T-036 | native 基线切换到 `/home/mes123456/cnc_wams/linuxcnc` | 完成 | `scripts/rip-environment`、`bin/axis`、`bin/xyzbc-trt-gui`、`rtlib/xyzbc-trt-kins.so` 已生成,RIP 环境可导入 `linuxcnc` Python 模块 |
|
||||||
| T-037 | 基于新 native 基线重采集 JSON | 待前置 | `native-xyzbc-trt-evidence.json` 的 `sourceRoot` 为 `/home/mes123456/cnc_wams/linuxcnc`,执行状态 completed |
|
| T-037 | 基于新 native 基线重采集 JSON | 部分完成 | native 脚本默认基线已切到 `/home/mes123456/cnc_wams/linuxcnc`;需在 RIP 环境下重新运行采集命令 |
|
||||||
| T-038 | Web 界面按新 native 运行结果复核 | 待实现 | UI/逻辑与 `/home/mes123456/cnc_wams/linuxcnc` 真实 `xyzbc-trt` 的状态流、按钮、路径、HAL pin 对齐 |
|
| T-038 | Web 界面按新 native 运行结果复核 | 待实现 | UI/逻辑与 `/home/mes123456/cnc_wams/linuxcnc` 真实 `xyzbc-trt` 的状态流、按钮、路径、HAL pin 对齐 |
|
||||||
| T-039 | 创建并执行 `xyzbc-trt` 快捷方式 | 完成 | `linuxcnc-rtcp-5axis-shortcuts/table-rotary-tilting/xyzbc-trt.desktop` 已创建,进程从 `/home/mes123456/cnc_wams/linuxcnc` 启动 |
|
| T-039 | 创建并执行 `xyzbc-trt` 快捷方式 | 完成 | `linuxcnc-rtcp-5axis-shortcuts/table-rotary-tilting/xyzbc-trt.desktop` 已创建,进程从 `/home/mes123456/cnc_wams/linuxcnc` 启动 |
|
||||||
| T-040 | DOCX 任务书整合到 working | 完成 | `working/09-设计任务书与技术方案整合.md` 保留 DOCX 1-11 章、5 张图片引用、任务书、技术方案、程序逻辑和状态联锁内容 |
|
| T-040 | DOCX 任务书整合到 working | 完成 | `working/09-设计任务书与技术方案整合.md` 保留 DOCX 1-11 章、5 张图片引用、任务书、技术方案、程序逻辑和状态联锁内容 |
|
||||||
@@ -46,5 +46,5 @@
|
|||||||
状态说明:
|
状态说明:
|
||||||
|
|
||||||
- 完成:本轮已实现并可通过静态/Node 验证。
|
- 完成:本轮已实现并可通过静态/Node 验证。
|
||||||
- 待前置:代码已接入,但当前缺少 `wasm-port/build/wasm` artifact,需先构建。
|
- 待前置:代码已接入,但仍依赖未完成的外部或运行环境步骤。
|
||||||
- 待实现:已记录验收要求,仍需修改采集或对比脚本。
|
- 待实现:已记录验收要求,仍需修改采集或对比脚本。
|
||||||
|
|||||||
@@ -1,5 +1,108 @@
|
|||||||
# 05-验收证据
|
# 05-验收证据
|
||||||
|
|
||||||
|
## evidence 字段与路径对比实现验收
|
||||||
|
|
||||||
|
### 命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node
|
||||||
|
python3 -m py_compile web-rtcp-5axis-xyzbc-trt-sim-plan/tools/collect-native-xyzbc-trt-evidence.py
|
||||||
|
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web
|
||||||
|
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare
|
||||||
|
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 结果
|
||||||
|
|
||||||
|
```text
|
||||||
|
xyzbc_trt_web_app_smoke=ok
|
||||||
|
collect-native-xyzbc-trt-evidence.py py_compile=ok
|
||||||
|
web_xyzbc_trt_evidence=.../working/evidence/web-xyzbc-trt-evidence.json
|
||||||
|
compare_xyzbc_trt_status=fail fail_count=5
|
||||||
|
gmoccapy_static_build=ok
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON 证据
|
||||||
|
|
||||||
|
```text
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/web-xyzbc-trt-evidence.json
|
||||||
|
web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/compare-xyzbc-trt-evidence.json
|
||||||
|
```
|
||||||
|
|
||||||
|
当前 Web evidence 已包含:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pathSampling.samplePeriodMs = 20
|
||||||
|
previewPath.samplePeriodMs = 20
|
||||||
|
executionPath.samplePeriodMs = 20
|
||||||
|
startupSequence
|
||||||
|
iniDisplay
|
||||||
|
halNets
|
||||||
|
kinematicsPins
|
||||||
|
axisJointLimits
|
||||||
|
switchkinsTransitions
|
||||||
|
uiEquivalence
|
||||||
|
vismachEquivalent
|
||||||
|
ngcguiSubroutines
|
||||||
|
demoPrograms
|
||||||
|
```
|
||||||
|
|
||||||
|
当前 compare evidence 已包含:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pathComparison.previewVsPreview
|
||||||
|
pathComparison.executionVsExecution
|
||||||
|
pathComparison.previewVsExecutionNative
|
||||||
|
pathComparison.previewVsExecutionWeb
|
||||||
|
```
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
- 静态/profile/staging/evidence 字段已实现并通过 Node smoke。
|
||||||
|
- compare 仍保持失败是正确结果:Web WASM artifact、preview path 和 execution path 已具备,native evidence 尚未用新基线重采集 path 样本。
|
||||||
|
- `npm run build` 当前已通过,说明目标 app 可在现有 WASM artifact 下完成静态构建。
|
||||||
|
|
||||||
|
## Web task/HAL executionPath 采集验收
|
||||||
|
|
||||||
|
### 命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:web
|
||||||
|
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run evidence:compare
|
||||||
|
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run smoke:node
|
||||||
|
npm --prefix web-rtcp-5axis-xyzbc-trt-sim-plan/app run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 结果
|
||||||
|
|
||||||
|
```text
|
||||||
|
web_xyzbc_trt_evidence=.../working/evidence/web-xyzbc-trt-evidence.json
|
||||||
|
compare_xyzbc_trt_status=fail fail_count=5
|
||||||
|
xyzbc_trt_web_app_smoke=ok
|
||||||
|
gmoccapy_static_build=ok
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON 摘要
|
||||||
|
|
||||||
|
```text
|
||||||
|
web.wasm.ready = true
|
||||||
|
web.blockers = []
|
||||||
|
web.previewPath.samplePeriodMs = 20
|
||||||
|
web.previewPath.sampleCount = 3074
|
||||||
|
web.executionPath.samplePeriodMs = 20
|
||||||
|
web.executionPath.sampleCount = 569
|
||||||
|
web.executionPath.source = web-linuxcnc-task-hal-execution
|
||||||
|
web.executionPath.taskHal.completed = true
|
||||||
|
compare.summary.passCount = 18
|
||||||
|
compare.summary.failCount = 5
|
||||||
|
```
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
- Web 侧 `previewPath` 已由 `linuxcnc_interp`/TP WASM 轨迹重采样为 20ms。
|
||||||
|
- Web 侧 `executionPath` 已由 `linuxcnc_task_hal` WASM 执行反馈生成 20ms 样本。
|
||||||
|
- compare 剩余 5 项失败均来自 native evidence 尚无 `previewPath`/`executionPath` 样本和采样周期字段。
|
||||||
|
|
||||||
## DOCX 整合验收
|
## DOCX 整合验收
|
||||||
|
|
||||||
### 源文件
|
### 源文件
|
||||||
@@ -465,9 +568,44 @@ missingSamples
|
|||||||
|
|
||||||
当前状态:
|
当前状态:
|
||||||
|
|
||||||
- 现有三份 `working/evidence/*.json` 还没有上述路径字段。
|
- `web-xyzbc-trt-evidence.json` 已通过采集脚本重新生成,`wasmArtifactsReady=true`、`missing=[]`。
|
||||||
- 本轮未手工改写 JSON 证据文件;后续必须通过采集脚本重新生成。
|
- Web preview path 已由 `linuxcnc_interp` WASM 生成 6174 个 20ms 样本。
|
||||||
- Web 执行路径采集依赖 WASM artifact,artifact 缺失时 compare 必须保留 fail/blocker。
|
- Web execution path 采集仍未实现,当前 blocker 为 `web-execution-path-unavailable`。
|
||||||
|
- native preview/execution path 仍需用 `/home/mes123456/cnc_wams/linuxcnc` 真实运行实例重采集。
|
||||||
|
- `compare-xyzbc-trt-evidence.json` 当前 17/23 通过,剩余 6 项均与 native path 样本或 Web execution path 样本缺失有关。
|
||||||
|
|
||||||
|
### WASM artifact 验收记录
|
||||||
|
|
||||||
|
本轮已生成并核对以下目标 artifact:
|
||||||
|
|
||||||
|
```text
|
||||||
|
wasm-port/build/wasm/core/linuxcnc_interp.js
|
||||||
|
wasm-port/build/wasm/core/linuxcnc_interp.wasm
|
||||||
|
wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.js
|
||||||
|
wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.wasm
|
||||||
|
wasm-port/build/wasm/tp/linuxcnc_tp.js
|
||||||
|
wasm-port/build/wasm/tp/linuxcnc_tp.wasm
|
||||||
|
wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js
|
||||||
|
wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm
|
||||||
|
```
|
||||||
|
|
||||||
|
验证命令结果:
|
||||||
|
|
||||||
|
```text
|
||||||
|
node tests/node/verify_xyzbc_trt_web_app.mjs -> xyzbc_trt_web_app_smoke=ok
|
||||||
|
SKIP_KINEMATICS_BUILD=1 wasm-port/tests/wasm/node/verify_kinematics_wasm.sh -> kinematics_wasm_node_smoke=ok
|
||||||
|
SKIP_TP_BUILD=1 wasm-port/tests/wasm/node/verify_tp_wasm.sh -> tp_wasm_node_smoke=ok
|
||||||
|
SKIP_TASK_HAL_BUILD=1 wasm-port/tests/wasm/node/verify_task_hal_wasm.sh -> linuxcnc_task_runtime_smoke=ok
|
||||||
|
```
|
||||||
|
|
||||||
|
补充 interpreter/remap 验收:
|
||||||
|
|
||||||
|
```text
|
||||||
|
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh -> interp_wasm_node_smoke=ok
|
||||||
|
wasm-port/tests/wasm/node/verify_interp_wasm.sh -> interp_wasm_node_smoke=ok
|
||||||
|
```
|
||||||
|
|
||||||
|
修复依据:`interp_g10_l11_wasm` 的实际 WASM 输出与 `wasm-port/vendor/linuxcnc/tests/interp/g10/g10-l11/expected` 一致,均为 `SET_G92_OFFSET x=-43.0622 y=-47.4282 z=-72`;原 Node 验收脚本中的 `x=-41.1962 y=-46.1962 z=-72` 为过期期望值。
|
||||||
|
|
||||||
## 页面证据
|
## 页面证据
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
# 06-决策记录
|
# 06-决策记录
|
||||||
|
|
||||||
|
## D-013:缺 WASM artifact 时 build 与 path compare 继续显式失败
|
||||||
|
|
||||||
|
日期:2026-07-02
|
||||||
|
|
||||||
|
决策:本轮补齐 Web/native evidence 字段和 compare 规则,但在缺少 `wasm-port/build/wasm` artifact 时,`npm run build`、Web preview path、Web task/HAL execution path 和 compare path 检查继续显式失败或 blocked,不通过占位样本伪造通过。
|
||||||
|
|
||||||
|
理由:
|
||||||
|
|
||||||
|
- `xyzbc-trt` 的 G-code、remap、kinematics、planner、task/HAL 语义必须来自 LinuxCNC-derived WASM 或真实 LinuxCNC evidence。
|
||||||
|
- 当前仓库缺少 `wasm-port/build/wasm/kinematics`、core、tp、task-hal artifact,浏览器 worker/runtime 不具备真实执行条件。
|
||||||
|
- compare JSON 的失败项已经精确指向 artifact 与 path 样本缺口,保留失败状态比静默降级更利于后续验收。
|
||||||
|
|
||||||
## D-012:DOCX 任务书整合为 working Markdown
|
## D-012:DOCX 任务书整合为 working Markdown
|
||||||
|
|
||||||
日期:2026-07-02
|
日期:2026-07-02
|
||||||
@@ -139,3 +151,30 @@
|
|||||||
- 用户明确要求以已编译成功并能执行 `xyzbc-trt` 的 `/home/mes123456/cnc_wams/linuxcnc` 为对标依据。
|
- 用户明确要求以已编译成功并能执行 `xyzbc-trt` 的 `/home/mes123456/cnc_wams/linuxcnc` 为对标依据。
|
||||||
- `/home/mes123456/cnc_wams/linuxcnc` 是干净 Git 源码仓库,便于把 native 行为、Web WASM 构建和后续代码追踪关联到同一源码树。
|
- `/home/mes123456/cnc_wams/linuxcnc` 是干净 Git 源码仓库,便于把 native 行为、Web WASM 构建和后续代码追踪关联到同一源码树。
|
||||||
- 该路径已完成 run-in-place 编译,`scripts/rip-environment`、`bin/axis`、`bin/xyzbc-trt-gui`、`rtlib/xyzbc-trt-kins.so` 已生成;因此后续第一步是用该路径重新生成 native evidence。
|
- 该路径已完成 run-in-place 编译,`scripts/rip-environment`、`bin/axis`、`bin/xyzbc-trt-gui`、`rtlib/xyzbc-trt-kins.so` 已生成;因此后续第一步是用该路径重新生成 native evidence。
|
||||||
|
|
||||||
|
## D-012:WASM artifact 已生成,后续 blocker 改为路径采集与 interpreter 回归
|
||||||
|
|
||||||
|
日期:2026-07-02
|
||||||
|
|
||||||
|
决策:`wasm-port/build/wasm` artifact 已通过 `emcc 6.0.2` 生成,Web evidence 不再把缺 artifact 作为 blocker;后续开发重点转向 native/Web path 采集闭环、Web task/HAL execution path collector,以及 `verify_interp_wasm.sh` 的 G10 L11 回归断言差异。
|
||||||
|
|
||||||
|
理由:
|
||||||
|
|
||||||
|
- core、kinematics、TP、task/HAL 所需 `.js/.wasm` 均已存在,`web-xyzbc-trt-evidence.json.wasm.missing=[]`。
|
||||||
|
- kinematics、TP、task/HAL 的 Node WASM smoke 已通过,说明主要 runtime artifact 可加载执行。
|
||||||
|
- Web preview path 已由 `linuxcnc_interp` WASM 生成样本,证明 Web evidence 已越过原先缺 artifact 的前置阻塞。
|
||||||
|
- compare 剩余失败项均指向 native path 样本和 Web execution path 样本缺失;这是采集能力缺口,不再是构建产物缺口。
|
||||||
|
- 历史状态:当时 `verify_interp_wasm.sh` 仍有 `interp_g10_l11_wasm` 断言失败,完整 interpreter/remap WASM 验收不能标记为完成。
|
||||||
|
|
||||||
|
## D-013:G10 L11 验收期望对齐 vendored LinuxCNC expected
|
||||||
|
|
||||||
|
日期:2026-07-02
|
||||||
|
|
||||||
|
决策:将 `verify_interp_wasm.mjs` 中 `interp_g10_l11_wasm` 的 `SET_G92_OFFSET` 期望值调整为 `x=-43.0622 y=-47.4282 z=-72`,与 `wasm-port/vendor/linuxcnc/tests/interp/g10/g10-l11/expected` 保持一致。
|
||||||
|
|
||||||
|
理由:
|
||||||
|
|
||||||
|
- 单独复现 `g10-l11` 后,WASM 实际 canonical 输出为 `SET_G92_OFFSET x=-43.0622 y=-47.4282 z=-72`。
|
||||||
|
- vendored LinuxCNC 当前 expected 文件同样记录 `SET_G92_OFFSET(-43.0622, -47.4282, -72.0000)`。
|
||||||
|
- 因此失败原因是 Node 验收脚本中的期望值过期,不是 WASM interpreter/remap 行为偏离 vendored LinuxCNC。
|
||||||
|
- 修正后,`SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh` 和完整 `wasm-port/tests/wasm/node/verify_interp_wasm.sh` 均通过,输出 `interp_wasm_node_smoke=ok`。
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
{
|
{
|
||||||
"apiName": "xyzbc-trt-native-web-evidence-comparison",
|
"apiName": "xyzbc-trt-native-web-evidence-comparison",
|
||||||
"status": "fail",
|
"status": "fail",
|
||||||
"comparedAt": "2026-07-02T07:41:51.598Z",
|
"comparedAt": "2026-07-02T12:48:11.244Z",
|
||||||
"nativePath": "/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/native-xyzbc-trt-evidence.json",
|
"nativePath": "/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/native-xyzbc-trt-evidence.json",
|
||||||
"webPath": "/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/web-xyzbc-trt-evidence.json",
|
"webPath": "/home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/working/evidence/web-xyzbc-trt-evidence.json",
|
||||||
"summary": {
|
"summary": {
|
||||||
"checkCount": 12,
|
"checkCount": 23,
|
||||||
"passCount": 11,
|
"passCount": 18,
|
||||||
"failCount": 1,
|
"failCount": 5,
|
||||||
"blockers": [
|
"blockers": [],
|
||||||
"missing-wasm-artifacts"
|
|
||||||
],
|
|
||||||
"nativeStatus": "ok",
|
"nativeStatus": "ok",
|
||||||
"webStatus": "blocked"
|
"webStatus": "ready-for-wasm-runtime"
|
||||||
},
|
},
|
||||||
"checks": [
|
"checks": [
|
||||||
{
|
{
|
||||||
@@ -134,18 +132,9 @@
|
|||||||
{
|
{
|
||||||
"category": "wasm",
|
"category": "wasm",
|
||||||
"requirement": "required WASM artifacts available",
|
"requirement": "required WASM artifacts available",
|
||||||
"status": "fail",
|
"status": "pass",
|
||||||
"evidence": {
|
"evidence": {
|
||||||
"missing": [
|
"missing": []
|
||||||
"wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.js",
|
|
||||||
"wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.wasm",
|
|
||||||
"wasm-port/build/wasm/core/linuxcnc_interp.js",
|
|
||||||
"wasm-port/build/wasm/core/linuxcnc_interp.wasm",
|
|
||||||
"wasm-port/build/wasm/tp/linuxcnc_tp.js",
|
|
||||||
"wasm-port/build/wasm/tp/linuxcnc_tp.wasm",
|
|
||||||
"wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js",
|
|
||||||
"wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -155,22 +144,525 @@
|
|||||||
"evidence": {
|
"evidence": {
|
||||||
"parameterFile": "xyzbc.var"
|
"parameterFile": "xyzbc.var"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "program",
|
||||||
|
"requirement": "web boat-xyzbc demo program staged",
|
||||||
|
"status": "pass",
|
||||||
|
"evidence": {
|
||||||
|
"demoPrograms": [
|
||||||
|
{
|
||||||
|
"filename": "xyzbc_switchkins.ngc",
|
||||||
|
"default": true,
|
||||||
|
"staged": true,
|
||||||
|
"sourceRel": "configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "boat-xyzbc.ngc",
|
||||||
|
"default": false,
|
||||||
|
"staged": true,
|
||||||
|
"sourceRel": "configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/boat-xyzbc.ngc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "ngcgui",
|
||||||
|
"requirement": "web Ngcgui/remap subroutines staged",
|
||||||
|
"status": "pass",
|
||||||
|
"evidence": {
|
||||||
|
"ngcguiSubroutines": [
|
||||||
|
{
|
||||||
|
"filename": "xyzbc_switchkins_sub.ngc",
|
||||||
|
"staged": true,
|
||||||
|
"sourceRel": "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzbc_switchkins_sub.ngc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "centering.ngc",
|
||||||
|
"staged": true,
|
||||||
|
"sourceRel": "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/centering.ngc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "helix_bc.ngc",
|
||||||
|
"staged": true,
|
||||||
|
"sourceRel": "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_bc.ngc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "postgui-hal",
|
||||||
|
"requirement": "web PyVCP to HALUI POSTGUI nets represented",
|
||||||
|
"status": "pass",
|
||||||
|
"evidence": {
|
||||||
|
"halNets": [
|
||||||
|
{
|
||||||
|
"signal": "kinstype-select",
|
||||||
|
"source": "motion.analog-out-03",
|
||||||
|
"target": "motion.switchkins-type",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "table-x",
|
||||||
|
"source": "joint.0.pos-fb",
|
||||||
|
"target": "xyzbc-trt-gui.table-x",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "saddle-y",
|
||||||
|
"source": "joint.1.pos-fb",
|
||||||
|
"target": "xyzbc-trt-gui.saddle-y",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "spindle-z",
|
||||||
|
"source": "joint.2.pos-fb",
|
||||||
|
"target": "xyzbc-trt-gui.spindle-z",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "tilt-b",
|
||||||
|
"source": "joint.3.pos-fb",
|
||||||
|
"target": "xyzbc-trt-gui.tilt-b",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "rotate-c",
|
||||||
|
"source": "joint.4.pos-fb",
|
||||||
|
"target": "xyzbc-trt-gui.rotate-c",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "tool-offset",
|
||||||
|
"source": "motion.tooloffset.z",
|
||||||
|
"target": "xyzbc-trt-kins.tool-offset",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "tool-offset",
|
||||||
|
"source": "xyzbc-trt-kins.tool-offset",
|
||||||
|
"target": "xyzbc-trt-gui.tool-offset",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "x-offset",
|
||||||
|
"source": "xyzbc-trt-kins.x-offset",
|
||||||
|
"target": "xyzbc-trt-gui.x-offset",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "z-offset",
|
||||||
|
"source": "xyzbc-trt-kins.z-offset",
|
||||||
|
"target": "xyzbc-trt-gui.z-offset",
|
||||||
|
"boundary": "ini-halcmd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "kinstype.is-0",
|
||||||
|
"source": "kinstype.is-0",
|
||||||
|
"target": "pyvcp.multilabel.0.legend0",
|
||||||
|
"boundary": "postgui-hal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "kinstype.is-1",
|
||||||
|
"source": "kinstype.is-1",
|
||||||
|
"target": "pyvcp.multilabel.0.legend1",
|
||||||
|
"boundary": "postgui-hal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "kinstype.is-2",
|
||||||
|
"source": "kinstype.is-2",
|
||||||
|
"target": "pyvcp.multilabel.0.legend2",
|
||||||
|
"boundary": "postgui-hal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "vismach-clear",
|
||||||
|
"source": "pyvcp.vismach-clear",
|
||||||
|
"target": "vismach.plotclear",
|
||||||
|
"boundary": "postgui-hal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "type0-button",
|
||||||
|
"source": "pyvcp.type0-button",
|
||||||
|
"target": "halui.mdi-command-00",
|
||||||
|
"command": "M429",
|
||||||
|
"boundary": "postgui-hal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "type1-button",
|
||||||
|
"source": "pyvcp.type1-button",
|
||||||
|
"target": "halui.mdi-command-01",
|
||||||
|
"command": "M428",
|
||||||
|
"boundary": "postgui-hal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signal": "type2-button",
|
||||||
|
"source": "pyvcp.type2-button",
|
||||||
|
"target": "halui.mdi-command-02",
|
||||||
|
"command": "M430",
|
||||||
|
"boundary": "postgui-hal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "kinematics",
|
||||||
|
"requirement": "web kinematics HAL pins represented",
|
||||||
|
"status": "pass",
|
||||||
|
"evidence": {
|
||||||
|
"kinematicsPins": {
|
||||||
|
"xOffset": -20,
|
||||||
|
"zOffset": -15,
|
||||||
|
"xRotPoint": 0,
|
||||||
|
"yRotPoint": 0,
|
||||||
|
"zRotPoint": 0,
|
||||||
|
"conventionalDirections": 0,
|
||||||
|
"toolOffsetSource": "motion.tooloffset.z",
|
||||||
|
"pins": [
|
||||||
|
"motion.switchkins-type",
|
||||||
|
"motion.analog-out-03",
|
||||||
|
"motion.tooloffset.z",
|
||||||
|
"xyzbc-trt-kins.tool-offset",
|
||||||
|
"xyzbc-trt-kins.x-offset",
|
||||||
|
"xyzbc-trt-kins.z-offset",
|
||||||
|
"xyzbc-trt-kins.x-rot-point",
|
||||||
|
"xyzbc-trt-kins.y-rot-point",
|
||||||
|
"xyzbc-trt-kins.z-rot-point",
|
||||||
|
"xyzbc-trt-kins.conventional-directions",
|
||||||
|
"halui.mdi-command-00",
|
||||||
|
"halui.mdi-command-01",
|
||||||
|
"halui.mdi-command-02"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "limits",
|
||||||
|
"requirement": "web TRAJ/AXIS/JOINT limits represented",
|
||||||
|
"status": "pass",
|
||||||
|
"evidence": {
|
||||||
|
"axisJointLimits": {
|
||||||
|
"coordinates": "XYZBC",
|
||||||
|
"linearUnits": "mm",
|
||||||
|
"angularUnits": "deg",
|
||||||
|
"jogAxes": [
|
||||||
|
"X",
|
||||||
|
"Y",
|
||||||
|
"Z",
|
||||||
|
"C"
|
||||||
|
],
|
||||||
|
"geometry": "XYZB",
|
||||||
|
"traj": {
|
||||||
|
"coordinates": "XYZBC",
|
||||||
|
"linearUnits": "mm",
|
||||||
|
"angularUnits": "deg",
|
||||||
|
"defaultLinearVelocity": 20,
|
||||||
|
"maxLinearVelocity": 35,
|
||||||
|
"defaultLinearAcceleration": 300,
|
||||||
|
"maxLinearAcceleration": 400
|
||||||
|
},
|
||||||
|
"axisLimits": {
|
||||||
|
"X": {
|
||||||
|
"min": -200,
|
||||||
|
"max": 200,
|
||||||
|
"maxVelocity": 20,
|
||||||
|
"maxAcceleration": 300
|
||||||
|
},
|
||||||
|
"Y": {
|
||||||
|
"min": -100,
|
||||||
|
"max": 100,
|
||||||
|
"maxVelocity": 20,
|
||||||
|
"maxAcceleration": 300
|
||||||
|
},
|
||||||
|
"Z": {
|
||||||
|
"min": -120,
|
||||||
|
"max": 120,
|
||||||
|
"maxVelocity": 20,
|
||||||
|
"maxAcceleration": 300
|
||||||
|
},
|
||||||
|
"B": {
|
||||||
|
"min": -36000,
|
||||||
|
"max": 36000,
|
||||||
|
"maxVelocity": 30,
|
||||||
|
"maxAcceleration": 300
|
||||||
|
},
|
||||||
|
"C": {
|
||||||
|
"min": -36000,
|
||||||
|
"max": 36000,
|
||||||
|
"maxVelocity": 30,
|
||||||
|
"maxAcceleration": 300
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jointCount": 5,
|
||||||
|
"jointConfig": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"axis": "X",
|
||||||
|
"type": "LINEAR",
|
||||||
|
"home": 0,
|
||||||
|
"min": -200,
|
||||||
|
"max": 200,
|
||||||
|
"maxVelocity": 20,
|
||||||
|
"maxAcceleration": 300,
|
||||||
|
"homeSearchVelocity": 0,
|
||||||
|
"homeSequence": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"axis": "Y",
|
||||||
|
"type": "LINEAR",
|
||||||
|
"home": 0,
|
||||||
|
"min": -100,
|
||||||
|
"max": 100,
|
||||||
|
"maxVelocity": 20,
|
||||||
|
"maxAcceleration": 300,
|
||||||
|
"homeSearchVelocity": 0,
|
||||||
|
"homeSequence": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"axis": "Z",
|
||||||
|
"type": "LINEAR",
|
||||||
|
"home": 0,
|
||||||
|
"min": -120,
|
||||||
|
"max": 120,
|
||||||
|
"maxVelocity": 20,
|
||||||
|
"maxAcceleration": 300,
|
||||||
|
"homeSearchVelocity": 0,
|
||||||
|
"homeSequence": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"axis": "B",
|
||||||
|
"type": "ANGULAR",
|
||||||
|
"home": 0,
|
||||||
|
"min": -100,
|
||||||
|
"max": 50,
|
||||||
|
"maxVelocity": 30,
|
||||||
|
"maxAcceleration": 300,
|
||||||
|
"homeSearchVelocity": 0,
|
||||||
|
"homeSequence": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"axis": "C",
|
||||||
|
"type": "ANGULAR",
|
||||||
|
"home": 0,
|
||||||
|
"min": -36000,
|
||||||
|
"max": 36000,
|
||||||
|
"maxVelocity": 30,
|
||||||
|
"maxAcceleration": 300,
|
||||||
|
"homeSearchVelocity": 0,
|
||||||
|
"homeSequence": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "path-preview",
|
||||||
|
"requirement": "native and web preview path sample period is 20ms",
|
||||||
|
"status": "fail",
|
||||||
|
"evidence": {
|
||||||
|
"nativeSamplePeriodMs": null,
|
||||||
|
"webSamplePeriodMs": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "path-preview",
|
||||||
|
"requirement": "native and web preview paths have samples",
|
||||||
|
"status": "fail",
|
||||||
|
"evidence": {
|
||||||
|
"nativeSampleCount": 0,
|
||||||
|
"webSampleCount": 3074,
|
||||||
|
"unavailable": [
|
||||||
|
"native: missing samples",
|
||||||
|
"sample period mismatch null/20"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "path-execution",
|
||||||
|
"requirement": "native and web execution path sample period is 20ms",
|
||||||
|
"status": "fail",
|
||||||
|
"evidence": {
|
||||||
|
"nativeSamplePeriodMs": null,
|
||||||
|
"webSamplePeriodMs": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "path-execution",
|
||||||
|
"requirement": "native and web execution paths have samples",
|
||||||
|
"status": "fail",
|
||||||
|
"evidence": {
|
||||||
|
"nativeSampleCount": 0,
|
||||||
|
"webSampleCount": 569,
|
||||||
|
"unavailable": [
|
||||||
|
"native: missing samples",
|
||||||
|
"sample period mismatch null/20"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "path-preview-execution-consistency",
|
||||||
|
"requirement": "native preview and execution paths are comparable",
|
||||||
|
"status": "fail",
|
||||||
|
"evidence": {
|
||||||
|
"nativePreviewSampleCount": 0,
|
||||||
|
"nativeExecutionSampleCount": 0,
|
||||||
|
"unavailable": [
|
||||||
|
"nativePreview: missing samples",
|
||||||
|
"nativeExecution: missing samples",
|
||||||
|
"sample period mismatch null/null"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "path-preview-execution-consistency",
|
||||||
|
"requirement": "web preview and execution paths are comparable",
|
||||||
|
"status": "pass",
|
||||||
|
"evidence": {
|
||||||
|
"webPreviewSampleCount": 3074,
|
||||||
|
"webExecutionSampleCount": 569,
|
||||||
|
"unavailable": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"pathComparison": {
|
||||||
|
"samplePeriodMs": 20,
|
||||||
|
"previewVsPreview": {
|
||||||
|
"status": "fail",
|
||||||
|
"comparable": false,
|
||||||
|
"periodsMatch": false,
|
||||||
|
"nativeSamplePeriodMs": null,
|
||||||
|
"webSamplePeriodMs": 20,
|
||||||
|
"leftSampleCount": 0,
|
||||||
|
"rightSampleCount": 3074,
|
||||||
|
"nativeSampleCount": 0,
|
||||||
|
"webSampleCount": 3074,
|
||||||
|
"unavailable": [
|
||||||
|
"native: missing samples",
|
||||||
|
"sample period mismatch null/20"
|
||||||
|
],
|
||||||
|
"maxTcpErrorMm": null,
|
||||||
|
"rmsTcpErrorMm": null,
|
||||||
|
"maxJointError": null,
|
||||||
|
"rmsJointError": null,
|
||||||
|
"maxToolAxisAngleDeg": null,
|
||||||
|
"sampleCountDelta": 3074,
|
||||||
|
"missingSamples": []
|
||||||
|
},
|
||||||
|
"executionVsExecution": {
|
||||||
|
"status": "fail",
|
||||||
|
"comparable": false,
|
||||||
|
"periodsMatch": false,
|
||||||
|
"nativeSamplePeriodMs": null,
|
||||||
|
"webSamplePeriodMs": 20,
|
||||||
|
"leftSampleCount": 0,
|
||||||
|
"rightSampleCount": 569,
|
||||||
|
"nativeSampleCount": 0,
|
||||||
|
"webSampleCount": 569,
|
||||||
|
"unavailable": [
|
||||||
|
"native: missing samples",
|
||||||
|
"sample period mismatch null/20"
|
||||||
|
],
|
||||||
|
"maxTcpErrorMm": null,
|
||||||
|
"rmsTcpErrorMm": null,
|
||||||
|
"maxJointError": null,
|
||||||
|
"rmsJointError": null,
|
||||||
|
"maxToolAxisAngleDeg": null,
|
||||||
|
"sampleCountDelta": 569,
|
||||||
|
"missingSamples": []
|
||||||
|
},
|
||||||
|
"previewVsExecutionNative": {
|
||||||
|
"status": "fail",
|
||||||
|
"comparable": false,
|
||||||
|
"periodsMatch": false,
|
||||||
|
"nativePreviewSamplePeriodMs": null,
|
||||||
|
"nativeExecutionSamplePeriodMs": null,
|
||||||
|
"leftSampleCount": 0,
|
||||||
|
"rightSampleCount": 0,
|
||||||
|
"unavailable": [
|
||||||
|
"nativePreview: missing samples",
|
||||||
|
"nativeExecution: missing samples",
|
||||||
|
"sample period mismatch null/null"
|
||||||
|
],
|
||||||
|
"maxTcpErrorMm": null,
|
||||||
|
"rmsTcpErrorMm": null,
|
||||||
|
"maxJointError": null,
|
||||||
|
"rmsJointError": null,
|
||||||
|
"maxToolAxisAngleDeg": null,
|
||||||
|
"sampleCountDelta": 0,
|
||||||
|
"missingSamples": []
|
||||||
|
},
|
||||||
|
"previewVsExecutionWeb": {
|
||||||
|
"status": "pass",
|
||||||
|
"comparable": true,
|
||||||
|
"periodsMatch": true,
|
||||||
|
"webPreviewSamplePeriodMs": 20,
|
||||||
|
"webExecutionSamplePeriodMs": 20,
|
||||||
|
"leftSampleCount": 3074,
|
||||||
|
"rightSampleCount": 569,
|
||||||
|
"unavailable": [],
|
||||||
|
"maxTcpErrorMm": 31.575968660550437,
|
||||||
|
"rmsTcpErrorMm": 14.55884964854259,
|
||||||
|
"maxJointError": 58.498220458848685,
|
||||||
|
"rmsJointError": 20.762740311288844,
|
||||||
|
"maxToolAxisAngleDeg": 19.999999999999993,
|
||||||
|
"sampleCountDelta": 2505,
|
||||||
|
"missingSamples": []
|
||||||
|
}
|
||||||
|
},
|
||||||
"requiredImprovements": [
|
"requiredImprovements": [
|
||||||
{
|
{
|
||||||
"category": "wasm",
|
"category": "path-preview",
|
||||||
"requirement": "required WASM artifacts available",
|
"requirement": "native and web preview path sample period is 20ms",
|
||||||
"evidence": {
|
"evidence": {
|
||||||
"missing": [
|
"nativeSamplePeriodMs": null,
|
||||||
"wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.js",
|
"webSamplePeriodMs": 20
|
||||||
"wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.wasm",
|
}
|
||||||
"wasm-port/build/wasm/core/linuxcnc_interp.js",
|
},
|
||||||
"wasm-port/build/wasm/core/linuxcnc_interp.wasm",
|
{
|
||||||
"wasm-port/build/wasm/tp/linuxcnc_tp.js",
|
"category": "path-preview",
|
||||||
"wasm-port/build/wasm/tp/linuxcnc_tp.wasm",
|
"requirement": "native and web preview paths have samples",
|
||||||
"wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js",
|
"evidence": {
|
||||||
"wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm"
|
"nativeSampleCount": 0,
|
||||||
|
"webSampleCount": 3074,
|
||||||
|
"unavailable": [
|
||||||
|
"native: missing samples",
|
||||||
|
"sample period mismatch null/20"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "path-execution",
|
||||||
|
"requirement": "native and web execution path sample period is 20ms",
|
||||||
|
"evidence": {
|
||||||
|
"nativeSamplePeriodMs": null,
|
||||||
|
"webSamplePeriodMs": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "path-execution",
|
||||||
|
"requirement": "native and web execution paths have samples",
|
||||||
|
"evidence": {
|
||||||
|
"nativeSampleCount": 0,
|
||||||
|
"webSampleCount": 569,
|
||||||
|
"unavailable": [
|
||||||
|
"native: missing samples",
|
||||||
|
"sample period mismatch null/20"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"category": "path-preview-execution-consistency",
|
||||||
|
"requirement": "native preview and execution paths are comparable",
|
||||||
|
"evidence": {
|
||||||
|
"nativePreviewSampleCount": 0,
|
||||||
|
"nativeExecutionSampleCount": 0,
|
||||||
|
"unavailable": [
|
||||||
|
"nativePreview: missing samples",
|
||||||
|
"nativeExecution: missing samples",
|
||||||
|
"sample period mismatch null/null"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user