修复 G18 圆弧轴映射

This commit is contained in:
2026-06-27 01:31:49 -04:00
parent 0e1e9d61a8
commit 45b0f9bcf8
3 changed files with 578 additions and 1 deletions

View File

@@ -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"],
};

View File

@@ -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 modulesLinuxCNC 源码和配置案例作为语义与五轴运动学参考源。
- 命令:`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/XZLinuxCNC 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`

View File

@@ -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",