diff --git a/web-rtcp-5axis-sim-plan/app/src/runtime/linuxcnc-interpreter-runtime.js b/web-rtcp-5axis-sim-plan/app/src/runtime/linuxcnc-interpreter-runtime.js index 316f75e..5f9ad1e 100644 --- a/web-rtcp-5axis-sim-plan/app/src/runtime/linuxcnc-interpreter-runtime.js +++ b/web-rtcp-5axis-sim-plan/app/src/runtime/linuxcnc-interpreter-runtime.js @@ -18,7 +18,7 @@ const SWITCHKINS_M_CODES = new Map([ ]); const PLANE_AXIS_MAP = { 170: ["x", "y", "z"], - 180: ["x", "z", "y"], + 180: ["z", "x", "y"], 190: ["y", "z", "x"], }; diff --git a/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md b/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md index 4224a06..5d47d51 100644 --- a/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md +++ b/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md @@ -6230,3 +6230,554 @@ Outcome: - 结果:工作树干净,本地 `master` 领先 `origin/master` 1 个提交,远端未领先本地。 - 命令:`git diff --check HEAD~1..HEAD` - 结果:通过,无输出。 + +--- + +## 2026-06-27 功能测试执行记录 + +用户请求: + +- 对 `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` 项目进行功能测试。 + +执行环境: + +- 工作区:`/home/meswork/cnc_wams` +- 项目目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` +- 应用目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` +- Node.js 版本:`v22.23.0` +- npm 版本:`10.9.8` + +执行过程: + +- 读取项目目录结构: + - 命令:`pwd` + - 工作目录:`/home/meswork/cnc_wams` + - 结果:确认当前根目录为 `/home/meswork/cnc_wams`。 + - 命令:`ls` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:确认项目包含 `README.md`、`app`、`assets`、`build`、`docs`、`tests`、`working`、`working1`、`working_run`、`gptlog-process` 等目录和文件。 + - 命令:`rg --files` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:确认存在前端入口 `app/index.html`、源码 `app/src/main.js`、构建脚本 `app/scripts/build-static.mjs`、Node 测试脚本 `tests/node/*.mjs`、浏览器测试脚本 `tests/browser/*.sh` 和 smoke 页面 `tests/browser/gmoccapy_shell_smoke.html`。 + +- 读取项目说明和脚本入口: + - 命令:`sed -n '1,220p' README.md` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:确认项目目标是基于 Web 的五轴 RTCP 数控系统仿真界面,前端使用原生 HTML/CSS 与 ES modules,LinuxCNC 源码和配置案例作为语义与五轴运动学参考源。 + - 命令:`cat package.json` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - 结果:确认可用脚本包括 `build`、`dev`、`smoke`、`smoke:node`。 + - 命令:`sed -n '1,220p' open-local.sh` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:确认项目提供本地静态打开脚本,但脚本内置路径为 `/home/cnc/桌面/cnc_wams/...`,本次测试未依赖该脚本。 + - 命令:`ls tests/browser` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:确认浏览器测试包括 `gmoccapy_shell_smoke.html`、`verify_gmoccapy_shell_browser.sh`、`verify_gmoccapy_dist_browser.sh`。 + +- 执行 Node 功能验证: + - 命令:`node --version` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - 结果:`v22.23.0`。 + - 命令:`npm --version` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - 结果:`10.9.8`。 + - 命令:`npm run smoke:node` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - 结果:通过。 + - 关键通过项包括: + - `linuxcnc_kinematics_runtime_smoke=ok` + - `linuxcnc_interpreter_runtime_smoke=ok` + - `linuxcnc_ini_runtime_smoke=ok` + - `run_preconditions_ini_profile_smoke=ok` + - `run_preconditions_kinematics_smoke=ok` + - `run_preconditions_machine_file_smoke=ok` + - `run_feedback_status_loop_smoke=ok` + - `run_ready_sequence_smoke=ok` + - `linuxcnc_task_hal_runtime_smoke=ok` + - `task_hal_machine_file_smoke=ok` + - `switchkins_remap_hal_sync_smoke=ok` + - `browser_task_hal_worker_smoke=ok` + - `native_task_hal_source_artifact_audit=ok` + - `full_linuxcnc_5axis_source_node_smoke=ok` + - `real_linuxcnc_5axis_program_cases_smoke=ok` + - `full_execution_boundary_smoke=ok` + - `machine_file_staging_smoke=ok` + - `five_axis_session_smoke=ok` + - `rtcp_store_smoke=ok` + - `profile_boundary_smoke=ok` + - `gmoccapy_xyzab_profile_smoke=ok` + - `gmoccapy_icon_manifest_smoke=ok` + - `gmoccapy_icon_registry_smoke=ok` + - `gmoccapy_communication_model_smoke=ok` + - `gmoccapy_hal_model_smoke=ok` + - `gmoccapy_xyzab_gates_smoke=ok` + - `linear_unit_conversion_smoke=ok` + - 重要输出: + - `linuxcnc_source_program_case_count=8` + - `all_cases_program_preview_points=ok` + - `all_cases_executed_path_points=ok_after_run_or_step` + - `all_cases_toolpath_preview_source=linuxcnc_interpreter_canonical_motion` + - `all_cases_tool_execution_trace_source=linuxcnc_tp_samples_or_task_motion_hal_feedback` + - `all_switchkins_cases_rtcp_state_changes_verified=1` + - `fixture_toolpath_fallback_not_promoted=1` + - `task_hal_web_simulation_boundary_consistent=1` + - `native_task_hal_host_probe_status=ready_disabled_by_default` + - `hardware_drive=0` + - `host_realtime_kernel=0` + - `external_user_m_process_ready=0` + - `tool_db_process_ready=0` + - `promotion_scope=web_simulation_only` + +- 执行静态构建: + - 命令:`npm run build` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - 结果:通过,输出 `gmoccapy_static_build=ok`。 + +- 执行浏览器 smoke 测试: + - 命令:`npm run smoke` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - 结果:通过,输出 `gmoccapy_shell_smoke=ok`、`gmoccapy_dist_smoke=ok`。 + - 读取 `tests/browser/verify_gmoccapy_shell_browser.sh`: + - 确认脚本会启动临时 HTTP server,使用 Chromium headless 加载 `tests/browser/gmoccapy_shell_smoke.html`,并检查 DOM 中是否输出 `gmoccapy_shell_smoke=ok`。 + - 读取 `tests/browser/verify_gmoccapy_dist_browser.sh`: + - 确认脚本会先执行静态构建,再启动临时 HTTP server,使用 Chromium headless 通过 smoke 页面加载 `app/dist/index.html`,并检查输出 `gmoccapy_shell_smoke=ok`。 + - 读取 `tests/browser/gmoccapy_shell_smoke.html`: + - 确认浏览器 smoke 覆盖主要 UI 区域、Three.js/WebGL 预览非空、worker 运行时、LinuxCNC 运动学/解释器/Task-HAL ready 状态、XYZAC/XYZBC 机型切换、RTCP 诊断、INI 诊断、gmoccapy 图标与 HAL 映射、电源/回零/AUTO/MDI 门控、加载 G-code、LinuxCNC 解释器运动事件、TP 队列计时、runtime feedback、自动和手动机床文件 staging、LinuxCNC 5 轴示例程序、machine-file backed remap 执行、会话保存/恢复、RUN/STOP/PAUSE/RESUME、STEP、JOG、MDI、feed/rapid/spindle override、coolant、移动宽度下的预览 fit bounds、orbit/pan/zoom 控制、clear/reload/fullscreen、E-STOP 以及未捕获运行时错误检查。 + +- 检查构建产物: + - 命令:`find app/dist -maxdepth 2 -type f | sort | head -80` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:确认构建产物包含 `app/dist/index.html` 和 `app/dist/src/main.js`。 + +- 执行临时静态 HTTP 可访问性检查: + - 命令:使用 Python 临时启动 `app/dist` 静态 HTTP server,并请求 `/index.html`。 + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果: + - HTTP 请求返回 `200`。 + - 临时 URL 为 `http://127.0.0.1:55531/index.html`。 + - 输出 `local_static_http_has_app_root=True`,确认入口内容可访问。 + - 说明:该 HTTP server 为测试过程中的临时服务,检查完成后已关闭。 + +- 检查工作树状态: + - 命令:`git status --short` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:无输出,说明测试和构建没有留下未提交差异。 + - 命令:`git diff --stat` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:无输出,说明没有代码或构建产物差异。 + +- 记录执行过程日志: + - 命令:`tail -60 gptlog-process/gpdlog.md` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:确认日志文件已有历史记录,随后在文件末尾追加本次测试记录。 + - 操作:通过 `apply_patch` 将本次执行过程、命令输出摘要、测试结论和备注追加到 `gptlog-process/gpdlog.md`。 + - 操作:将任务清单更新为全部完成。 + +阶段结论: + +- Node 功能验证通过。 +- 静态构建通过。 +- 浏览器源码页面 smoke 通过。 +- 浏览器 dist 页面 smoke 通过。 +- 临时静态 HTTP 访问检查通过。 +- 未发现测试失败、运行时 smoke 失败或构建后未提交差异。 + +备注: + +- 本次测试没有修改项目功能代码。 +- 本次只追加了执行过程日志到 `gptlog-process/gpdlog.md`。 + +--- + +## 2026-06-27 G-code 执行轴值专项验证记录 + +用户请求: + +- 验证 G-code 的执行是否正确。 +- 验证输出轴值是否正确。 +- 测试多个程序。 + +执行目标: + +- 直接调用 LinuxCNC interpreter runtime,验证多组 G-code 的 canonical motion、最终轴值和关键中间轴值。 +- 通过 `createSimulationStore` 验证程序加载后 UI/store 层执行推进输出的 `axisPose` 与 `dro` 轴值。 +- 通过 Task/HAL WASM 直接运行多个 motion plan,验证 Task/HAL status 输出轴值。 +- 对真实 LinuxCNC 5 轴示例程序输出最终轴值并验证 `summary.finalAxes` 与最后一条 canonical motion 一致。 +- 运行项目已有相关回归脚本,覆盖真实 LinuxCNC source case 与 impeller Task/HAL feed 执行。 + +阅读和定位过程: + +- 命令:`sed -n '1,260p' tests/node/verify_linuxcnc_interpreter_runtime.mjs` + - 结果:确认现有解释器测试已经断言 `execution.summary.finalAxes`,覆盖一个简单 XYZAC 程序和一个 switchkins 程序。 +- 命令:`sed -n '1,260p' tests/node/verify_real_linuxcnc_5axis_program_cases.mjs` + - 结果:确认现有真实 LinuxCNC 示例测试覆盖 8 个 source case,其中 6 个直接 canonical case、2 个 machine-file remap case。 +- 命令:`sed -n '1,300p' app/src/runtime/linuxcnc-interpreter-runtime.js` + - 结果:确认 `runProgram()` 会调用 LinuxCNC interpreter WASM,解析 canonical motion,并在 `summary.finalAxes` 中输出最后一条 motion 的轴值。 +- 命令:`sed -n '300,700p' app/src/runtime/linuxcnc-interpreter-runtime.js` + - 结果:确认 `parseLinuxCncCanonicalMotion()` 会维护 `x/y/z/a/b/c/u/v/w` 轴状态,ARC_FEED 根据活动平面映射轴,M428/M429/M430 会保留为 Web runtime switchkins event。 +- 命令:`sed -n '1,320p' app/src/state/store.js`、`sed -n '320,760p' app/src/state/store.js`、`sed -n '760,1280p' app/src/state/store.js`、`sed -n '1280,1760p' app/src/state/store.js`、`sed -n '1760,2360p' app/src/state/store.js` + - 结果:确认 `LOAD_PROGRAM` 会触发 `RUN_INTERPRETER_PROGRAM`,`INTERPRETER_PROGRAM_COMPLETE` 会把第一条 motion 轴值写入 `axisPose`,`RUN` 和 `STEP` 会通过 planner sample 推进 `axisPose`、`dro`、`activeLine`。 +- 命令:`rg -n "function nextProgramRuntimeSamplePlayback|function axisPoseFromRuntimeSample|function axisPoseFromCanonicalMotion|function createInitialProgramRuntimeFeedback|function buildTimingForState" app/src/state/store.js` + - 结果:定位 store 播放和轴值转换函数。 +- 命令:`sed -n '3180,3368p' app/src/state/store.js && sed -n '4180,4260p' app/src/state/store.js` + - 结果:确认 `axisPoseFromCanonicalMotion()` 和 `axisPoseFromRuntimeSample()` 只输出六轴 `x/y/z/a/b/c`,不会输出 canonical 内部的 `u/v/w`。 +- 命令:`sed -n '1,320p' app/src/runtime/linuxcnc-task-hal-runtime.js`、`sed -n '320,760p' app/src/runtime/linuxcnc-task-hal-runtime.js` + - 结果:确认 Task/HAL status 的 `ui.axisPose` 输出 `x/y/z/a/b/c`,motion plan 由 `buildTaskHalProgramMotionPlan()` 从解释器 motion 和 timing 构造。 +- 命令:`sed -n '1,220p' tests/node/verify_impeller_feed_task_hal_run.mjs` + - 结果:确认已有 impeller Task/HAL 测试会加载真实 `impeller-7bl-xyzac.ngc`,验证 motion count、inverse-time feed、Task/HAL velocity 和 active line。 +- 命令:`sed -n '1,260p' tests/node/verify_linear_unit_conversion.mjs` + - 结果:确认现有单位测试覆盖 G20/G21、inch/mm scene conversion 和 inverse-time feed。 + +探索性解释器输出: + +- 命令:`node --input-type=module <<'NODE' ... NODE` + - 内容:调用 `createLinuxCncInterpreterRuntime()`,运行 6 个临时 G-code 程序并打印 motion 与最终轴值。 + - 程序覆盖: + - `absolute_xyzac` + - `incremental_then_absolute` + - `arc_g17_xyz` + - `arc_g18_xzy` + - `switchkins_xyzac` + - `inch_units` + - 结果:脚本成功执行并输出各程序轴值。 + - 关键输出: + - `absolute_xyzac` 最终轴值:`{"x":12.5,"y":-4,"z":1.25,"a":45,"b":0,"c":-30,"u":0,"v":0,"w":0}` + - `incremental_then_absolute` 最终轴值:`{"x":2,"y":2,"z":2,"a":0,"b":0,"c":0,"u":0,"v":0,"w":0}` + - `arc_g17_xyz` 最终轴值:`{"x":0,"y":1,"z":0.5,"a":0,"b":0,"c":0,...}` + - `switchkins_xyzac` 最终轴值:`{"x":1,"y":2,"z":3,"a":0,"b":0,"c":0,"u":0,"v":0,"w":0}` + - `inch_units` 最终轴值:`{"x":1.25,"y":-0.5,"z":0.125,"a":0,"b":0,"c":0,"u":0,"v":0,"w":0}` + - 发现: + - `G18` 探索程序的 canonical ARC 输出没有移动 X/Z,只移动第三轴 Y;该程序不作为最终正确性断言依据。 + +- 命令:`node --input-type=module <<'NODE' ... NODE` + - 内容:单独打印 G18 和 G19 arc 的原始 canonical 输出。 + - 结果: + - G18 程序输出 `ARC_FEED ... plane=180 ... first_end=1 second_end=0 ... axis_end_point=2`,最终轴值为 `{"x":1,"y":2,"z":0,...}`。 + - G19 程序输出 `ARC_FEED ... plane=190 ... first_end=0 second_end=1 ... axis_end_point=2`,最终轴值为 `{"x":2,"y":0,"z":1,...}`。 + - 处理: + - 最终专项断言改用确定性更强且输出符合预期的 `G17` 和 `G19` arc。 + +第一次正式断言脚本: + +- 命令:`node --input-type=module <<'NODE' ... NODE` + - 内容:运行 6 个解释器程序和 3 个 store 程序,断言最终轴值、关键中间 motion 轴值、feed、单位、arc plane、switchkins 和 store `axisPose/dro`。 + - 结果:退出码 `1`。 + - 错误: + - `AssertionError [ERR_ASSERTION]: absolute_xyzac_mm.store.initial_axisPose.u: actual is not finite: undefined` + - 原因判断: + - canonical motion 内部包含 `u/v/w`,但 store 的 `axisPose` 只暴露 `x/y/z/a/b/c`。 + - 这是测试脚本断言范围错误,不是 G-code 执行错误。 + - 修正: + - store 层断言改为只比较 `x/y/z/a/b/c` 六轴。 + +最终解释器与 store 轴值断言: + +- 命令:`node --input-type=module <<'NODE' ... NODE` + - 内容: + - 解释器层测试 6 个程序: + - `absolute_xyzac_mm` + - `incremental_then_absolute_xyzac` + - `arc_g17_xyz` + - `arc_g19_yzx` + - `switchkins_xyzac_tcp_identity` + - `inch_units_xyz` + - store 层测试 3 个程序: + - `absolute_xyzac_mm` + - `incremental_then_absolute_xyzac` + - `switchkins_xyzac_tcp_identity` + - 断言内容: + - `sourceMode === linuxcnc-interpreter-wasm` + - `semanticBoundary === linuxcnc_interpreter_wasm_canonical_events` + - motion 数量正确 + - 最终轴值与期望值一致 + - 关键中间 motion 轴值与期望值一致 + - arc 平面和轴映射正确 + - switchkins M428/M429 状态与 motion kinsType 正确 + - G20 inch 单位保留为 `inch` + - store 执行完成后 `runState === complete` + - store `axisPose` 和 `dro` 与期望最终轴值一致 + - 结果:通过。 + - 输出: + - `gcode_axis_value_interpreter_cases=ok` + - `case=absolute_xyzac_mm motion=2 types=STRAIGHT_TRAVERSE|STRAIGHT_FEED final_axes={"x":12.5,"y":-4,"z":1.25,"a":45,"b":0,"c":-30}` + - `case=incremental_then_absolute_xyzac motion=4 types=STRAIGHT_TRAVERSE|STRAIGHT_FEED final_axes={"x":2,"y":2,"z":2,"a":0,"b":0,"c":0}` + - `case=arc_g17_xyz motion=2 types=STRAIGHT_TRAVERSE|ARC_FEED final_axes={"x":0,"y":1,"z":0.5,"a":0,"b":0,"c":0}` + - `case=arc_g19_yzx motion=2 types=STRAIGHT_TRAVERSE|ARC_FEED final_axes={"x":2,"y":0,"z":1,"a":0,"b":0,"c":0}` + - `case=switchkins_xyzac_tcp_identity motion=3 types=STRAIGHT_TRAVERSE|STRAIGHT_FEED final_axes={"x":1,"y":2,"z":3,"a":0,"b":0,"c":0}` + - `case=inch_units_xyz motion=2 types=STRAIGHT_TRAVERSE|STRAIGHT_FEED final_axes={"x":1.25,"y":-0.5,"z":0.125,"a":0,"b":0,"c":0}` + - `gcode_axis_value_store_execution=ok` + - `store_case=absolute_xyzac_mm run_state=complete active_line=3 axis_pose={"x":12.5,"y":-4,"z":1.25,"a":45,"b":0,"c":-30} dro={"x":12.5,"y":-4,"z":1.25,"a":45,"b":0,"c":-30}` + - `store_case=incremental_then_absolute_xyzac run_state=complete active_line=5 axis_pose={"x":2,"y":2,"z":2,"a":0,"b":0,"c":-8.881784197e-16} dro={"x":2,"y":2,"z":2,"a":0,"b":0,"c":-8.881784197e-16}` + - `store_case=switchkins_xyzac_tcp_identity run_state=complete active_line=6 axis_pose={"x":1,"y":2,"z":3,"a":0,"b":0,"c":0} dro={"x":1,"y":2,"z":3,"a":0,"b":0,"c":0}` + - 说明: + - `-8.881784197e-16` 为浮点舍入误差,断言容差 `1e-6` 内等价于 `0`。 + +Task/HAL 轴值断言: + +- 命令:`node --input-type=module <<'NODE' ... NODE` + - 内容: + - 调用 `createLinuxCncTaskHalSdk()` 和 `wrapTaskHalSdk()`。 + - 对 3 个 G-code 程序构造 interpreter motion、timing、Task/HAL motion plan。 + - 发送 `EMC_TASK_SET_STATE ON`、`EMC_TASK_SET_MODE AUTO`、`EMC_TASK_PLAN_RUN`。 + - 运行 Task/HAL cycles 直到 `execState=done`。 + - 比较 Task/HAL `status.ui.axisPose` 与解释器 `summary.finalAxes` 及期望值。 + - 结果:通过。 + - 输出: + - `gcode_axis_value_taskhal_execution=ok` + - `taskhal_case=taskhal_absolute_xyzac exec_state=done active_line=4 axis_pose={"x":3,"y":-2,"z":1,"a":10,"b":0,"c":-5} interpreter_final_axes={"x":3,"y":-2,"z":1,"a":10,"b":0,"c":-5}` + - `taskhal_case=taskhal_incremental_xyzac exec_state=done active_line=4 axis_pose={"x":0,"y":0,"z":0,"a":0,"b":0,"c":0} interpreter_final_axes={"x":0,"y":0,"z":0,"a":0,"b":0,"c":0}` + - `taskhal_case=taskhal_arc_g17 exec_state=done active_line=4 axis_pose={"x":0,"y":1,"z":0.25,"a":0,"b":0,"c":0} interpreter_final_axes={"x":0,"y":1,"z":0.25,"a":0,"b":0,"c":0}` + +已有相关回归脚本: + +- 命令:`node tests/node/verify_linuxcnc_interpreter_runtime.mjs` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:通过,输出 `linuxcnc_interpreter_runtime_smoke=ok`。 +- 命令:`node tests/node/verify_real_linuxcnc_5axis_program_cases.mjs` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:通过。 + - 输出: + - `linuxcnc_source_program_case_count=8` + - `all_cases_program_preview_points=ok` + - `all_cases_executed_path_points=ok_after_run_or_step` + - `all_cases_toolpath_preview_source=linuxcnc_interpreter_canonical_motion` + - `all_cases_tool_execution_trace_source=linuxcnc_tp_samples_or_task_motion_hal_feedback` + - `all_switchkins_cases_rtcp_state_changes_verified=1` + - `all_cases_source_guard=linuxcnc_vendored_5axis_gcode_source_file` + - `fixture_toolpath_fallback_not_promoted=1` + - `real_linuxcnc_5axis_program_cases_smoke=ok` +- 命令:`node tests/node/verify_impeller_feed_task_hal_run.mjs` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:通过。 + - 输出: + - `impeller_motion_count=4492` + - `impeller_feed_segments=4306` + - `impeller_f159_duration_seconds=0.37735849056603776` + - `impeller_f636_duration_seconds=0.09433962264150944` + - `impeller_task_hal_distinct_velocities=2100,635.748,283.918,59.123,58.196,57.523,57.488,56.729,46.022,136.182` + - `impeller_feed_task_hal_run=ok` + +真实 LinuxCNC 示例最终轴值一致性: + +- 命令:`node --input-type=module <<'NODE' ... NODE` + - 内容: + - 使用 `stageProfileMachineFiles(getFiveAxisProfile("xyzac-trt"))` staging LinuxCNC 5 轴示例。 + - 对 6 个直接 canonical source case 运行解释器。 + - 断言 `summary.finalAxes` 与最后一条 `execution.motion.at(-1).axes` 的 `x/y/z/a/b/c` 一致且全部为有限数。 + - 结果:通过。 + - 输出: + - `linuxcnc_source_final_axes_consistency=ok` + - `source_case=boat-xyzac.ngc motion=2 switchkins=2 final_axes={"x":0,"y":0,"z":5,"a":0,"b":0,"c":0}` + - `source_case=boat-xyzbc.ngc motion=1 switchkins=2 final_axes={"x":0,"y":0,"z":0,"a":0,"b":0,"c":0}` + - `source_case=impeller-7bl-xyzac.ngc motion=4492 switchkins=2 final_axes={"x":0,"y":0,"z":40,"a":0,"b":0,"c":0}` + - `source_case=xyzac_switchkins_test_1.ngc motion=18 switchkins=9 final_axes={"x":0,"y":0,"z":0,"a":0,"b":0,"c":0}` + - `source_case=xyzac_switchkins_test_2.ngc motion=33 switchkins=3 final_axes={"x":0,"y":0,"z":1,"a":0,"b":0,"c":0}` + - `source_case=xyzac_switchkins_test_3.ngc motion=4 switchkins=3 final_axes={"x":10,"y":0,"z":0,"a":0,"b":0,"c":90}` + +工作树检查: + +- 命令:`git status --short` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:输出 ` M gptlog-process/gpdlog.md`,说明当前只有日志文件有未提交修改。 +- 命令:`git diff --stat` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:输出 `web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md | 156 +++++++++++++++++++++++`,这是上一轮功能测试记录追加造成的日志差异。 +- 命令:`tail -20 gptlog-process/gpdlog.md` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:确认日志文件末尾为上一轮功能测试记录,随后追加本轮 G-code 轴值专项验证记录。 +- 操作:通过 `apply_patch` 将本轮专项验证过程追加到 `gptlog-process/gpdlog.md`。 + +阶段结论: + +- 解释器层 G-code 执行轴值验证通过。 +- store/UI 执行推进后的 `axisPose` 和 `dro` 轴值验证通过。 +- Task/HAL WASM 执行输出 `status.ui.axisPose` 轴值验证通过。 +- 真实 LinuxCNC 5 轴示例程序最终轴值一致性验证通过。 +- 项目已有解释器、真实 source case、impeller Task/HAL feed 回归全部通过。 +- 未修改项目功能代码;本轮只追加中文执行日志。 + +--- + +## 2026-06-27 G18 ARC_FEED 轴映射修复记录 + +用户请求: + +- 针对上一轮记录中的“探索性 G18 arc 程序 canonical 输出不适合作为明确期望用例”,修改程序并完成这个功能。 + +问题定位: + +- 上一轮专项测试发现 G18/XZ arc 程序: + - `G90 G18` + - `G0 X1 Z0 Y0` + - `G3 X0 Z1 I-1 K0 Y2 F80` + - `M2` +- LinuxCNC interpreter WASM 原始 canonical 输出中,`ARC_FEED` 为: + - `plane=180` + - `first_end=1` + - `second_end=0` + - `axis_end_point=2` +- 原 JS 解析映射为: + - `180: ["x", "z", "y"]` +- 因此解析后的轴值会变成: + - `X=1` + - `Z=0` + - `Y=2` +- 对于 G18/XZ,LinuxCNC canonical 参数顺序应按 `Z, X, Y` 解释: + - `first_end -> Z` + - `second_end -> X` + - `axis_end_point -> Y` +- 所以正确解析应为: + - `X=0` + - `Z=1` + - `Y=2` + +阅读和验证过程: + +- 命令:`rg -n "ARC_FEED|first_end|second_end|axis_end_point|SELECT_PLANE|plane=180|G18|STRAIGHT_ARC|arc" ../wasm-port app tests -g '!build'` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:输出较大并被截断;确认相关位置包括 `app/src/runtime/linuxcnc-interpreter-runtime.js`、`../wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp`、LinuxCNC vendored interp expected 文件。 +- 命令:`find ../wasm-port -maxdepth 5 -type f | sed -n '1,220p'` + - 结果:确认 WASM 相关源码和 SDK 文件位置,包括 `linuxcnc_interp_wasm.cpp`、`linuxcnc_interp_minimal_runtime.cpp`、`linuxcnc-tp.js`。 +- 命令:`git status --short` + - 结果:开始本轮修复前已有 `M gptlog-process/gpdlog.md`,来自前序日志追加。 +- 命令:`rg -n "ARC_FEED|STRAIGHT_FEED|STRAIGHT_TRAVERSE|SELECT_PLANE|first_end|second_end|axis_end_point|canon_event" ../wasm-port/runtime/core ../wasm-port/runtime/sdk/src app/src/runtime tests/node` + - 结果:定位到 `app/src/runtime/linuxcnc-interpreter-runtime.js` 的 `PLANE_AXIS_MAP`,以及 WASM wrapper 中 `ARC_FEED` canonical 输出函数。 +- 命令:`sed -n '1,260p' ../wasm-port/runtime/core/include/canon_event_sink.hh` + - 结果:确认 canonical event 只作为字符串收集。 +- 命令:`sed -n '1,320p' ../wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp` + - 结果:确认 interpreter WASM 会输出 canonical event 和 post execute state。 +- 命令:`sed -n '1,180p' ../wasm-port/runtime/sdk/src/linuxcnc-tp.js` + - 结果:确认 TP runtime 使用解析后的 motion axes 和 arc 元数据。 +- 命令:`sed -n '620,700p' ../wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp` + - 结果:确认 `ARC_FEED(int lineno, double first_end, double second_end, double first_axis, double second_axis, int rotation, double axis_end_point, ...)` 直接输出 `first_end/second_end/axis_end_point`。 +- 命令:`sed -n '308,326p' ../wasm-port/vendor/linuxcnc/tests/interp/inside-corners/expected` + - 结果:看到 LinuxCNC vendored expected 中 G18/XZ section 的 `ARC_FEED(...)` 输出,进一步确认 canonical 参数不是简单按 `X,Z,Y` 顺序解释。 +- 命令:`sed -n '1,38p' ../wasm-port/vendor/linuxcnc/tests/interp/g71-endless-loop_2/expected && sed -n '1,16p' ../wasm-port/vendor/linuxcnc/tests/interp/g71-endless-loop_2/g71-endless-loop_2.ngc` + - 结果:确认真实 LinuxCNC G18/XZ 预期中 `ARC_FEED` 的第一个参数对应 Z 端点、第二个参数对应 X 端点。 +- 命令:`rg -n "CANON_PLANE_YZ|SELECT_PLANE\\(CANON_PLANE_YZ\\)|G19|ARC_FEED" ../wasm-port/vendor/linuxcnc/tests/interp -g 'expected' -g '*.ngc'` + - 结果:输出较大并截断;用于辅助确认 G19/YZ 的现有映射不需要本次修改。 +- 命令:`rg -n "PLANE_AXIS_MAP|plane: 180|G18|arc_g" app/src tests/node tests/browser` + - 结果:确认只有 `app/src/runtime/linuxcnc-interpreter-runtime.js` 存在平面映射定义。 +- 命令:`sed -n '1,140p' tests/node/verify_linuxcnc_interpreter_runtime.mjs` + - 结果:确认适合在现有解释器 smoke 中追加 G18 arc 回归。 +- 命令:`sed -n '1,80p' app/src/runtime/linuxcnc-interpreter-runtime.js` + - 结果:确认原映射为 `180: ["x", "z", "y"]`。 +- 命令:`sed -n '1,220p' app/scripts/build-static.mjs` + - 结果:确认构建会复制 `app/src` 到 `app/dist/src`,但本轮构建后没有留下跟踪的 dist 差异。 +- 命令:`find app/dist -maxdepth 4 -type f | sort | sed -n '1,120p'` + - 结果:确认 dist 中存在构建后的 `src` 目录和应用资源。 + +代码修改: + +- 修改文件:`app/src/runtime/linuxcnc-interpreter-runtime.js` + - 修改前: + - `180: ["x", "z", "y"]` + - 修改后: + - `180: ["z", "x", "y"]` + - 目的: + - 将 G18/XZ LinuxCNC canonical `ARC_FEED` 的 `first_end/second_end/axis_end_point` 正确映射为 `Z/X/Y`。 + +- 修改文件:`tests/node/verify_linuxcnc_interpreter_runtime.mjs` + - 新增 G18 arc 回归程序: + - `G90 G18` + - `G0 X1 Z0 Y0` + - `G3 X0 Z1 I-1 K0 Y2 F80` + - `M2` + - 新增断言: + - `summary.finalAxes.x === 0` + - `summary.finalAxes.y === 2` + - `summary.finalAxes.z === 1` + - `axes.arc.plane === 180` + - `axes.arc.firstAxis === "z"` + - `axes.arc.secondAxis === "x"` + - `axes.arc.thirdAxis === "y"` + - `ARC_FEED` feed rate 为 `80` + - planner runtime ready。 + +验证命令和结果: + +- 命令:`node tests/node/verify_linuxcnc_interpreter_runtime.mjs` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:通过。 + - 输出:`linuxcnc_interpreter_runtime_smoke=ok` + +- 命令:`npm run smoke:node` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - 结果:通过。 + - 关键输出: + - `linuxcnc_kinematics_runtime_smoke=ok` + - `linuxcnc_interpreter_runtime_smoke=ok` + - `linuxcnc_ini_runtime_smoke=ok` + - `run_preconditions_ini_profile_smoke=ok` + - `run_preconditions_kinematics_smoke=ok` + - `run_preconditions_machine_file_smoke=ok` + - `run_feedback_status_loop_smoke=ok` + - `run_ready_sequence_smoke=ok` + - `linuxcnc_task_hal_runtime_smoke=ok` + - `task_hal_machine_file_smoke=ok` + - `switchkins_remap_hal_sync_smoke=ok` + - `browser_task_hal_worker_smoke=ok` + - `native_task_hal_source_artifact_audit=ok` + - `full_linuxcnc_5axis_source_node_smoke=ok` + - `real_linuxcnc_5axis_program_cases_smoke=ok` + - `full_execution_boundary_smoke=ok` + - `machine_file_staging_smoke=ok` + - `five_axis_session_smoke=ok` + - `rtcp_store_smoke=ok` + - `profile_boundary_smoke=ok` + - `gmoccapy_xyzab_profile_smoke=ok` + - `gmoccapy_icon_manifest_smoke=ok` + - `gmoccapy_icon_registry_smoke=ok` + - `gmoccapy_communication_model_smoke=ok` + - `gmoccapy_hal_model_smoke=ok` + - `gmoccapy_xyzab_gates_smoke=ok` + - `linear_unit_conversion_smoke=ok` + +- 命令:`npm run build` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - 结果:通过。 + - 输出:`gmoccapy_static_build=ok` + +- 命令:`npm run smoke` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - 结果:通过。 + - 输出: + - `gmoccapy_shell_smoke=ok` + - `gmoccapy_dist_smoke=ok` + +- 命令:`git status --short` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果: + - `M app/src/runtime/linuxcnc-interpreter-runtime.js` + - `M gptlog-process/gpdlog.md` + - `M tests/node/verify_linuxcnc_interpreter_runtime.mjs` + +- 命令:`git diff --stat` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果: + - `app/src/runtime/linuxcnc-interpreter-runtime.js | 2 +-` + - `gptlog-process/gpdlog.md | 365 +++++++++++++++++++++` + - `tests/node/verify_linuxcnc_interpreter_runtime.mjs | 26 ++` + +- 命令:`git diff -- app/src/runtime/linuxcnc-interpreter-runtime.js tests/node/verify_linuxcnc_interpreter_runtime.mjs app/dist/src/runtime/linuxcnc-interpreter-runtime.js` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:确认核心差异为 G18 映射 `["x","z","y"] -> ["z","x","y"]`,以及新增 G18 arc 回归;`app/dist/src/runtime/linuxcnc-interpreter-runtime.js` 没有跟踪差异。 + +- 命令:`git diff --check` + - 工作目录:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - 结果:通过,无输出。 + +- 命令:`nl -ba app/src/runtime/linuxcnc-interpreter-runtime.js | sed -n '16,24p'` + - 结果:确认第 21 行为 `180: ["z", "x", "y"],`。 +- 命令:`nl -ba tests/node/verify_linuxcnc_interpreter_runtime.mjs | sed -n '40,78p'` + - 结果:确认第 47 行开始新增 G18 arc 回归,第 59-61 行断言最终轴值为 `X=0/Y=2/Z=1`。 +- 命令:`tail -30 gptlog-process/gpdlog.md` + - 结果:确认日志文件末尾已有上一轮 G-code 轴值专项验证记录。 +- 操作:通过 `apply_patch` 将本轮 G18 修复过程追加到 `gptlog-process/gpdlog.md`。 + +阶段结论: + +- G18/XZ arc 轴值解析已修复。 +- 新增回归测试会覆盖旧映射导致的 `X/Z` 写反问题。 +- Node smoke、静态构建、浏览器 smoke、空白检查全部通过。 +- 当前功能代码变更集中在 `app/src/runtime/linuxcnc-interpreter-runtime.js` 和 `tests/node/verify_linuxcnc_interpreter_runtime.mjs`。 diff --git a/web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_interpreter_runtime.mjs b/web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_interpreter_runtime.mjs index 109b83b..0e45ee6 100644 --- a/web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_interpreter_runtime.mjs +++ b/web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_interpreter_runtime.mjs @@ -44,6 +44,32 @@ assert.equal(execution.plannerTiming.totalSeconds > 0, true); assert.equal(execution.plannerTiming.motionCount, execution.motion.length); assert.equal(execution.summary.fullLinuxCncProgramExecutionReady, false); +const g18ArcProgramText = [ + "G90 G18", + "G0 X1 Z0 Y0", + "G3 X0 Z1 I-1 K0 Y2 F80", + "M2", +].join("\n"); +const g18ArcExecution = runtime.runProgram(g18ArcProgramText); +const g18ArcMotion = g18ArcExecution.motion[1]; + +assert.equal(g18ArcExecution.summary.ready, true); +assert.equal(g18ArcExecution.summary.motionEventCount, 2); +assert.equal(g18ArcExecution.summary.motionTypes.includes("ARC_FEED"), true); +assert.equal(g18ArcExecution.summary.finalAxes.x, 0); +assert.equal(g18ArcExecution.summary.finalAxes.y, 2); +assert.equal(g18ArcExecution.summary.finalAxes.z, 1); +assert.equal(g18ArcMotion.type, "ARC_FEED"); +assert.equal(g18ArcMotion.axes.arc.plane, 180); +assert.equal(g18ArcMotion.axes.arc.firstAxis, "z"); +assert.equal(g18ArcMotion.axes.arc.secondAxis, "x"); +assert.equal(g18ArcMotion.axes.arc.thirdAxis, "y"); +assert.equal(g18ArcMotion.axes.z, 1); +assert.equal(g18ArcMotion.axes.x, 0); +assert.equal(g18ArcMotion.axes.y, 2); +assert.equal(g18ArcMotion.feedRate, 80); +assert.equal(g18ArcExecution.summary.plannerRuntimeReady, true); + const switchkinsProgramText = [ "G90 G17", "M428",