Files
linuxcncWebWASM/doc-rtcp/xyzac-panel-button-parity-test-report.md
wangdequan bade560372 Align AXIS control flow and fix pause and program rendering
Document source UI gates, explicit native live-plot clearing, and remaining parity boundaries. Fix pause/resume transport continuity, repeated stepping, coordinate previews, and spindle override state. Optimize large program snapshots and rendering, and cover the control flows with native Tcl, WASM, and browser checks.

Validation: 22 native tests and source/browser checks pass. Worker/App regression: 113 of 123 pass; 10 pre-existing failures remain documented with no new failures.
2026-09-08 10:46:27 +08:00

720 lines
46 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# XYZAC TRT 面板按钮对标测试报告
## 2026-09-08 10:33 恢复原生清图方式(北京时间,当前结论)
按最新明确要求“按原生的方法如何清除的,web 也同样方式清除”,先更新
流程文件“原生清图方式(10:14)”,再修改实现。本节取代下方历史版本中
自动换程序清图的说明,不再保留该扩展行为。
源码路径:`axis.tcl` 工具栏 Clear Live Plot / Ctrl-K →
`axis.py::clear_live_plot` → `LivePlotter.clear` → `positionlogger.clear`。
`open_file_guts`、`reload_file` 创建/替换程序预览,但不调用这一清图路径。
当前 Web 行为:
| 操作 | 加工轨迹 Live Plot | 程序预览 |
| --- | --- | --- |
| Open、同文件 Reload、编辑后 Reload、加载 Chips | 保留 | 替换 |
| Run、Pause、Resume、Stop、正常结束 | 保留 | 保留 |
| 工具栏 Clear Live Plot / Ctrl+K | 清除,运行时继续累积后续运动 | 保留 |
实现仅移除五个 Web 加载入口的自动清图标志,以及 Worker PROGRAM_LOAD
中的自动清图分支。旧调用即使附带退役的 `clear_live_plot_on_replace` 参数,
也不会额外清图。显式 VISUAL_CLEAR、暂停/恢复通信修复和 Chips 性能优化保留。
验证结果:
- Worker 验证换文件、同文件、编辑后、解析失败与 gate 拒绝均不改变
live epoch;显式 Clear 才推进 epoch,且 task/motion/program 哈希不变。
- 浏览器 Open 和 Reload 保留轨迹;工具栏 Clear 清为 0、静态预览保留。
- Ctrl+K 将 41 个实际运动轨迹点清为 0,12 段静态预览完整保留。
- 默认程序与 Chips 的重复暂停/清图/倍率/继续、暂停中 Step 后继续和
移动端清图/继续均通过;185 条桌面有序命令、4 项预期 task 拒绝,无通信错误。
- 源码契约、TypeScript/生产构建通过;桌面/移动 canvas 检查通过。
- 全量 Worker/App:123 项,113 通过、10 项原有失败;与上一轮失败集合
相同,没有新增失败。既有完整 AXIS 能力边界不因此改变。
原始记录:`/tmp/axis-native-clear-browser.json`、
`/tmp/axis-native-clear-pause.json`、`/tmp/axis-native-clear-regression.log`。
本地预览:`http://127.0.0.1:5175/`,刷新后使用新版本;未进行远程部署。
## 2026-09-08 09:57 暂停/恢复一致性修复(北京时间,当前结论)
遵循“先核对源码、更新流程文档,再修改代码”的顺序。本次修复仅涉及已连接
通信层对 PAUSED 的处理,以及 React 对 task 拒绝的采样处理;既有轨迹、
G-code、TP 步长、进给、程序切换和 UI 控件集合继续使用原实现。
### 已复现的原因
| 命令 | 发送 seq | 旧通信层 last_seq | 结果 |
| --- | --- | --- | --- |
| PROGRAM_PAUSE | 9 | 9 | 成功暂停 |
| VISUAL_CLEAR | 10 | 9 | protocol.invalid_state |
| PROGRAM_RESUME | 11 | 9 | protocol.sequence_gap |
| PROGRAM_ABORT | 12 | 9 | protocol.sequence_gap |
旧 `sim_protocol.c` 在 PAUSED 使用另一份命令白名单,拒绝了 Tcl 仍允许的
Clear、Step、主轴倍率等操作,并且不消费 seq。随后继续和停止都无法恢复
通信连续性。该问题独立于 TP 是否能恢复,也不能通过改写 task 状态解决。
源依据是 `axis.py::task_pause/task_resume/task_pauseresume` 和
`emctaskmain.cc` 的 PAUSED 命令分支、`interpResumeState` 保存/恢复:暂停
保留队列和恢复状态,NML 通道继续接受命令,再由 task 判断执行是否合法。
### 修复及保持不变的行为
- READY/PAUSED 均接收顺序正确的合法通信帧;Worker task gate 继续负责拒绝
非法机床操作,拒绝事件按该 seq 入账,后续 Resume/Stop 不再断档。
- schema、握手、operation_id、真正乱序和 tick 回退校验继续生效;不放宽
CNC gate、不丢弃错误、不自动重发副作用、不用 Reload/清 TP 实现恢复。
- React 遇到 `axis_gate` 拒绝时保留 canonical 状态。若 task 仍运行则继续
原有单请求采样;真正 FAULT/OFF/ESTOP、恢复或其他错误继续停止采样。
- 原生协议单测加入 `-UNDEBUG`,确保 Release 构建确实执行断言;补充暂停中
通信接收和真正坏帧拒绝。旧 MDI 通信拒绝断言改由真实 Worker task 拒绝
用例覆盖,未删除机床拒绝验证。
### 验证结果
| 检查 | 结果 |
| --- | --- |
| 原生构建 / CTest | 22/22 通过,协议断言已启用 |
| `test_xyzac_pause_resume_consistency.mjs` | 153 条真实 WASM 命令,通信 seq 与事件 cursor 一致;重复暂停、清图/倍率、非法命令、单步后继续、WAITING 完成、MDI 暂停/继续、最后停止均通过 |
| `test_xyzac_pause_resume_browser.mjs` | 默认程序与 Chips 各三轮暂停/清图/倍率/继续;重复命令拒绝不终止后续运动;暂停中单步后继续、移动端暂停/清图/继续/停止通过;桌面段 194 条有序命令、4 个预期 task 拒绝,无通信错误 |
| 既有按钮浏览器检查 | 20/20 组通过,浏览器错误 0 |
| 既有轨迹生命周期 | 换新文件清图、同源重复执行保留、Pause/Resume/Stop 保留、显式 Clear 通过 |
| 源码公式/清单 | Tcl 7488 条断言、源码 hash 和 82 个控件 ID 通过 |
| TypeScript / Vite | 通过;保留既有 bundle 大小提示 |
| 全量 Worker/App | **123 项,113 通过,10 项旧失败**;与上一轮失败集合相同,新增失败 0 |
剩余旧失败仍为三轴 adapter/完成状态/工具 IO 和配置数量问题,详见下方
历史记录;不能把本次专项通过写成完整 LinuxCNC 发布或全仓测试通过。
原始记录:`/tmp/axis-pause-resume-build.log`、
`/tmp/axis-pause-resume-browser.json`、`/tmp/axis-pause-resume-regression.log`、
`/tmp/axis-pause-buttons-regression.json`、`/tmp/axis-pause-plot-regression.json`。
桌面/移动截图:`/tmp/axis-pause-resume-desktop.png`、
`/tmp/axis-pause-resume-mobile.png`。
本地生产预览为 `http://127.0.0.1:5175/`。刷新页面才会启动新 WASM Worker;
本次未远程部署,也不会在旧页面已损坏的通信序号上静默重放程序。
## 2026-09-08 换程序清图与 Chips 性能(当前结论)
本次按“核对 AXIS 源码 → 先完善流程文件 → 修改实现 → 复现验收”执行。
对应流程见 `linuxcnc-axis-web-control-panel-source-logic.md` 的“换程序轨迹与
长程序性能”及更新后的“Toolpath display lifecycle”。
### 清图语义
`axis.py::open_file_guts` 替换静态 AxisCanon,不调用 LivePlotter.clear;
原生保留历史加工轨迹。因此这不是原生 Open 的缺陷。本次按用户明确要求,
Web 文件替换发送 `clear_live_plot_on_replace:true`,在新文件/新文本解析
成功后,通过与 VISUAL_CLEAR 共用的 `clearLivePlot()` 递增可视 epoch,
一次发布新预览与新 epoch。这是已记录的 `WEB_EXTENSION`,不冒充原生行为。
默认 PROGRAM_LOAD 保留轨迹;同文件同文本 Reload、Run/Pause/Resume/Stop
保留轨迹。取消、门控拒绝或解析失败不自动清图。加载期间也不再用 null
快照造成假 epoch=0;画布只按真正的 Worker epoch 清除历史和待播放样本。
### 性能原因与测量
修改前 CPU profile 中,82.9% 样本位于 JS `fnv1a64`,主要来自每次 program
snapshot 对 200509 字节源程序重复计算 hash。此外,全部 canonical 线段
都有 samples,旧绘图分支将其各建成独立 Three.js Line,导致约 4684 次
静态 draw call,而非代码注释中声称的批量绘图。
修正为源文本 hash 按不可变文本缓存,保留 WASM 动态状态的实时读取;
同一 servo 批次只读取一次不变的 program paused 状态;相邻 canonical
顶点合并成 rapid/feed 两个 LineSegments;Live Plot 使用增量更新的 GPU
缓冲,不再每几帧复制整段历史。未修改 G-code、F 值、倍率、TP 或 tick 步长。
同一主机、相同输入、两次 Chips 加载,200 task ticks(2 秒仿真)测试:
| 指标 | 修改前 | 修改后 |
| --- | --- | --- |
| 第一次 Chips 加载 | 18.904 s | 1.822 s |
| 再次 Chips 加载 | 18.394 s | 1.688 s |
| 推进 2 秒仿真(第一次) | 10.376 s CPU/墙钟 | 0.325 s CPU/墙钟 |
| 推进 2 秒仿真(再次) | 10.268 s | 0.296 s |
| 200 次长程序 snapshot | 755 ms | 40 ms |
| 5-tick 请求 p95(第一次) | 287 ms | 13.4 ms |
两个尝试的 source hash、preview hash、preview 段数、末端位置、速度、队列、
进给倍率、执行行号在修改前后逐项完全一致;清图 epoch 是唯一预期的可视
生命周期差异。以上是计算吞吐提升,不是放大机床速度。浏览器仍按 1 倍
仿真时间推进,最多一个采样请求在途。
### 完整浏览器场景
在 `http://127.0.0.1:5175/` 运行实际配置的第一份 `xyzac_switchkins.ngc`
直到完成,然后点击加载 `linuxcnc/nc_files/3D_Chips.ngc`,再运行/暂停/
恢复/停止,结果:
| 项目 | 实际结果 |
| --- | --- |
| 第一份程序完成时 Live Plot | 4349 点 |
| 第二份加载完成后旧 Live Plot | 0 点,新 epoch=2 |
| Chips 浏览器加载 | 2624 ms |
| 静态预览 | 4683 段,2 次 draw call;旧程序结束在 Z10,首个 G0 Z10 是正确消去的零位移 |
| 运行 5.020 s 墙钟 | 推进 5.050 s 仿真,比例 1.006 |
| Worker 请求 p95 | 18.2 ms |
| 暂停/停止响应 | 149 / 151 ms |
| 浏览器错误 | 0 |
另外验证同源 Reload 保留轨迹、显式 Clear 只清 Live Plot、不清静态预览;
默认 Worker PROGRAM_LOAD 的原生保留路径及成功/失败/拒绝 Web 替换均通过。
移动端长程序编辑区的自动高度曾扩展到数万像素,已改为固定 grid 高度和
内部滚动。390/768/1440 宽度下页面高度为 2520/2427/1559,编辑器可滚动
81957 像素但只渲染 30/30/37 行;移动端运行比 0.999,错误 0。桌面/移动端
canvas 非空,Chips 实体刀路正常显示,截图已检查。
### 复现与回归
- `scripts/benchmark_xyzac_program_replacement.mjs`:实际 WASM 前后基准。
- `scripts/test_xyzac_second_program_browser.mjs`:完整首程序完成后换 Chips。
- `scripts/test_xyzac_chips_layout_browser.mjs`:三种尺寸、长编辑器滚动及移动运行。
- `scripts/test_xyzac_live_path_lifecycle_browser.mjs`:暂停/恢复/停止、换文件、重复运行、显式清图。
- 源码契约、82 个控件 ID、TypeScript 和生产构建通过。
- 针对性 Worker/React 回归 9/9;全量 Worker/App **122 项,112 通过、10 项原有失败**,与上轮失败集合相同。
原始输出:`/tmp/axis-chips-before.json`、`/tmp/axis-chips-after.json`、
`/tmp/axis-chips-browser.json`、`/tmp/axis-chips-layout.json`、
`/tmp/axis-chips-lifecycle-browser.log`、`/tmp/axis-chips-full-regression.log`。
截图:`/tmp/axis-chips-replacement-1440.png`、`/tmp/axis-chips-replacement-390.png`。
本次仅修正上述换程序和性能问题,保持既有完整 AXIS/所有配置的能力边界,
未进行远程部署;线上页面不会因本地代码修改自动更新。
## 2026-09-08 第二次源码复核(当前结论)
按要求先复核并修正流程文件,再修改 Worker/WASM 和 Web。当前流程文件的
“源码复核修正”和“尚未取得完整等价证据”两节是本次审核入口;早期的
SOURCE_ADAPTER 标签不得用来掩盖其中列出的功能缺口。
### 源码证据
新增 `scripts/test_axis_source_ui_oracle.mjs`,直接执行锁定的
`axis.tcl::update_state`,对 **576 种状态组合、13 条控件公式作 7488 条断言**,
结果通过。源码 Python/Tcl hash 检查和 **82 个控件 ID** 清单检查也通过。
该测试不运行桌面 AXIS,更不替代完整 motion 数值 oracle;它验证原始 Tcl
公式与 Web 的 `ui_gates` 投影一致。
### 本次修正
| 项目 | 源码定义与实现结果 |
| --- | --- |
| UI 与命令门控 | `ui_gates` 对标 Tcl,`gates` 对标命令接收。Run/Step 不再用未回零状态改变原生 UI 公式;MDI UI 可用时,handler 仍可按 manual_ok 拒绝并保留输入 |
| OFF 状态 | `STATE_ESTOP_RESET` 在 AXIS 显示为 OFF;不再根据 last_error 猜测机器状态,也不再将 FAULT 标签直接投影成 ESTOP |
| Step | 初次从 0 行进入暂停;后续可连续解析;已有暂停运动通过 C/WASM 的 TP 执行 ID 边界单步。原生 C 测试验证两条排队运动分两次完成,不能一次排空 |
| WAITING | 程序读完但运动未结束可暂停;恢复原来的完成/等待状态,不重新开始解释;MDI 运动期间正确投影 WAITING 并保持互斥 |
| 模式恢复 | ON+IDLE 的 AUTO 边沿及文件加载后按选中标签发送 SET_MODE;在消息处理时排队,避免延迟 effect 覆盖后续 Run |
| Home Axis | switchkins 始终为 KINEMATICS_BOTH。世界轴字母回零正确拒绝;Worker 独立支持 `HOME {joint:0..4}` 的原生数字 joint 入口,未伪装为世界轴按钮 |
| 主轴覆盖率 | 独立保留 requested_rpm/direction;覆盖率 0 后恢复原转速与方向;React 不再按输出 RPM 的正负推断按钮方向 |
| 手动/MDI 控件组 | 轴单选框及 MDI 历史列表加入各自的源控件组门控;c/i/I 切换步长;Escape 可调用原生 task_stop,中止手动或 MDI 运动 |
| 预览 | Rotate 只切换鼠标 Drag/Rotate;取消自动旋转。limits 读取 C/WASM XYZ 软限位;offsets 读取 G5x;velocity/extents 现在确实改变对应显示。按 glnav.py 修正五个正交视图、Z2 的平面旋转及短程序过大的标记 |
### 验收结果
| 检查 | 结果 |
| --- | --- |
| 原始 Tcl 公式对照 | 7488/7488 通过 |
| 原生 CTest | 22/22 通过;包含双段 TP 单步和方向限位 |
| 实际 WASM 控制链 | `test_axis_source_execution.mjs` 的 UI/命令门控、Step、WAITING/MDI、回零类型、主轴零覆盖率均通过 |
| 生产构建浏览器按钮 | 20/20 组通过 |
| 生产构建事件/显示流程 | 12/12 组通过;包含重复 Step、模式恢复、旋转不自转、真实图层、1440×1000 和 390×844 画布像素检查 |
| 控件清单 | 82 个已登记 ID/家族;初始 DOM 80 个控件,About/历史为动态控件 |
| 前端投影测试 | 4/4 通过 |
| TypeScript / Vite | 通过;既有 bundle 大小提示仍存在 |
| 全部 Worker/App 测试 | **122 项,112 通过,10 失败** |
剩余 10 项均为上轮已在修改前源码复现的旧三轴/配置问题:
`test_axis3_selected_case`、`test_axis3_tutorial_motion`、`test_axis3_tutorial_program`、
`test_axis3_tutorial_sequence`、`test_axis3_tutorial_tool_io`、`test_axis3_visible_completion`、
`test_panel_gate_matrix`、`test_panel_snapshot_projection`、`test_sim_case_matrix_s8_06`、
`test_wasm_canonical_state`(均为 `web/worker/*.mjs`)。没有把这些失败计为通过。
`test_xyzac_manual_control` 已使用真实 joint 编号回零、等待 MDI 队列排空后再
加载程序;原测试的 Z180 超过选定 INI 的 Z 最大值 120,正常路径改为 Z18,
并继续保留越界拒绝和活动 MDI 拒绝断言。ESTOP_RESET 断言改为检查 source
state;界面显示 OFF。首次 Step 及暂停恢复测试已按源 task 分支更新。
日志:`/tmp/axis-source-round2-final-regression.log`、
`/tmp/axis-source-round2-camera-final.log`、`/tmp/axis-source-round2-browser-buttons2.log`。
截图:`/tmp/axis-source-flow-1440.png`、`/tmp/axis-source-flow-390.png`。
本地生产预览:`http://127.0.0.1:5175/`,未执行远程部署。
### 仍不属于完整对标
完整 joint/world 模式、Run 边界警告对话框、独立角速度和物理单位滑块、
原生 task/NML 指令级 Step 细分、全部 G92/旋转/刀具补偿图层、optional stop、
block delete、rapid override、Tool Touch Off 等仍有明确缺口。TP 执行 ID 在
伺服采样后变化,单步边界允许原生单周期内的微小推进,未伪造精确端点。
新 IO 状态布局由 `sim_io_size` 读取;旧布局 checkpoint 会明确拒绝,不能
把旧二进制状态直接套入新结构。
因此本轮结论是**指定控制路径经源码验证并修正,仍有公开的源功能缺口**;
不是“全部 LinuxCNC/AXIS 功能已经完全等价”。`release_status: STOP_RELEASE`。
## 2026-09-08 源流程文件驱动验收
本次按“核对 AXIS 源码 → 完善流程文件 → 修改 WASM/Worker → 修改 Web → 验收”执行。
当前规范为 [`linuxcnc-axis-web-control-panel-source-logic.md`](../docs/linuxcnc-axis-web-control-panel-source-logic.md),
覆盖 **82 个控件 ID/动态家族**,并同步了 DOT 与 PNG 流程图。
本节是当前结论;后面的旧版本记录仅用于历史追溯,不能替代本节或当前流程文件。
状态:`VERIFIED_WITH_SCOPE_GAPS`;发布边界继续为 `STOP_RELEASE`。
本地生产构建预览:`http://127.0.0.1:5175/`。
### 按流程修正
| 流程 | 当前行为与验证 |
| --- | --- |
| 文件打开/重载 | 统一 Worker 加载门控;运行、暂停、活动 TP 队列和读取中的互斥;异步读取后再次检查门控 |
| 按住点动 | 一次 `JOG_START`,C/WASM 选择方向限位;浏览器只发 `SIM_STEP`;释放/取消/移出/失焦/隐藏页只发送一次 `JOG_STOP` |
| 增量点动 | 松开后继续采样直到 WASM 队列排空;线性轴按显示单位转换,A/C 保持角度 |
| MDI | trim、清空输入、相邻历史去重、1000 条历史上限、单击选取、双击一次执行、Up/Down 循环选择 |
| 主轴/冷却/Stop | 区分原生命令接收条件与 Tcl 控件启用条件;主轴指令自行切 MANUAL,F9/F10 先停转分支保留;冷却复选框只在 ON+IDLE 可用,Stop 按钮只在 ON+非 IDLE 可用 |
| 覆盖率 | Feed/Jog/Max/Spindle 设置在 OFF/ESTOP 仍可提交;快照回显数值,设置本身不得使主轴启动 |
| 对刀 | G10 不改变机器位置;X/Y/Z 单位转换与 A/C 角度分开;G54..G59.3 偏置保留到 Reload,重新生成预览并更新 relative 显示 |
| 程序完成 | M2/EOF 后等待最后 TP 段排空;验收同时检查队列排空前 Run 禁用、排空后任务 STOPPED |
| 快捷键/弹窗 | F1 在文本输入中仍有效;大小写和 Ctrl/Alt/Meta 不误映射机器命令;About 焦点、Escape 和菜单关闭按流程处理 |
| 显示 | 修复菜单弹层裁切、文件栏按钮被挤压和窄屏数值裁切;减少画布调试文本遮挡 |
### 实际结果
| 检查 | 结果 |
| --- | --- |
| `scripts/build-wasm.sh` | WASM 构建及内置样例通过;native CTest **22/22** 通过 |
| 源码契约/控件清单 | Python/Tcl 源文件 SHA-256 与锁定值一致;**82** 个控件 ID 已登记 |
| `test_xyzac_panel_buttons_browser.mjs` | 最新生产构建 **20/20** 组通过,浏览器错误 0;含菜单、案例、急停和断电恢复 |
| `test_axis_panel_source_flow_browser.mjs` | **11/11** 组通过;实际统计 Worker 命令,验证五轴双向、快速释放、失焦、键盘按住、MDI 历史、对刀与相对坐标 |
| 浏览器控件清单 | 初始 80 个控件加动态 About,历史家族另在专项生成并验证;无未登记或非法重复 ID |
| 桌面/移动端 | 1440×1000、390×844;画布非空、视图改变像素、资源加载正常、无页面横向溢出 |
| `web/react/src/test_*.mjs` | **4/4** 投影测试通过 |
| TypeScript/Vite | typecheck/build 通过;保留既有大 bundle 提示 |
| 全部 `web/worker/test_*.mjs` + `web/app/test_*.mjs` | **121 项,110 通过,11 失败**;不能标记全仓通过 |
原始输出:`/tmp/axis-panel-core-build-final.log`、
`/tmp/axis-panel-browser-production.log`、`/tmp/axis-panel-source-flow-acceptance.log`、
`/tmp/axis-panel-worker-acceptance.log`。
截图:`/tmp/axis-source-flow-1440.png`、`/tmp/axis-source-flow-390.png`。
### 修改前复现与保留失败
使用主仓库提交 `845075f4a37baca145ecec572d93d8aade17ba32` 的独立源码目录
`/tmp/axis-source-baseline.mK3EZO` 重建原始 WASM,复用相同 LinuxCNC 资源,
对 13 个相关用例复测:1 通过、12 失败。
其中 `test_xyzac_hal_tool_alarm.mjs` 的 MDI 空闲态主轴拒绝已修正;
最终剩余 11 项均属于该基线失败集合,**新增失败数为 0**。
复现输出:`/tmp/axis-panel-baseline-failures.log`。
| 保留用例(均位于 `web/worker/`) | 失败原因 |
| --- | --- |
| `test_axis3_selected_case.mjs`、`test_axis3_tutorial_motion.mjs`、`test_axis3_tutorial_sequence.mjs`、`test_panel_gate_matrix.mjs`、`test_panel_snapshot_projection.mjs`、`test_wasm_canonical_state.mjs` | 旧三轴 motion adapter 缺少 `wasmMotion.jog` |
| `test_axis3_tutorial_program.mjs`、`test_axis3_visible_completion.mjs` | 旧三轴任务完成状态仍为 RUNNING |
| `test_axis3_tutorial_tool_io.mjs` | 旧三轴工具/IO 路径被门控拒绝 |
| `test_sim_case_matrix_s8_06.mjs` | 资源矩阵实际数量 236 与旧断言 235 不一致 |
| `test_xyzac_manual_control.mjs` | 旧用例要求在 MDI 运动队列未排空时接受 `PROGRAM_LOAD`,与当前文档的加载互斥相冲突;保留拒绝行为 |
`test_xyzac_machine_sequence.mjs` 已按源码补充 M2 后的有限队列排空检查,
继续断言 TP 队列非空时 Run 禁用、最终 task STOPPED、queue=0;输出记录实际
`drain_ticks`,不再把固定 8000 个采样等同于完成。
原先关于“Spindle 必须预先 MANUAL”及“OFF/ESTOP 禁止覆盖率”的断言已按
AXIS handler/Tcl 原公式修正,并增加实际 WASM 状态断言。
### 明确边界
Rapid Override、独立 Angular Jog Speed、Override Limits、Block Delete、
Optional Stop、完整 Tool Touch Off/工具偏置 ABI,以及桌面工具编辑器/Quit
仍按流程记录为 `SOURCE_BLOCKED` 或 host boundary。禁用功能不计入已实现
源码对标;当前证据也不代表完整原生 free/teleop 减速、全部坐标旋转/G92/
刀具补偿、所有 LinuxCNC 配置或完整 TP 数值 oracle 已闭合。
复现本次验收:
```bash
bash scripts/build-wasm.sh
node scripts/test_axis_control_contract.mjs
node scripts/test_axis_control_inventory.mjs
node --experimental-strip-types web/worker/test_xyzac_source_widget_gates.mjs
node --experimental-strip-types web/worker/test_xyzac_touch_off_source_flow.mjs
npm run build --prefix web/react
# 本地预览运行后,向这两个脚本传入 PLAYWRIGHT_MODULE 和 XYZAC_PANEL_URL
node scripts/test_xyzac_panel_buttons_browser.mjs
node scripts/test_axis_panel_source_flow_browser.mjs
node --experimental-strip-types --test --test-concurrency=4 web/worker/test_*.mjs web/app/test_*.mjs
```
## 历史记录(不作为当前验收结论)
## 结论
本报告针对当前选定案例
`linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini`,
按 LinuxCNC AXIS 的命令、门控、状态和错误语义执行了一轮完整面板验收。
`selected_case_status: VERIFIED_WITH_SCOPE_GAPS`
`release_status: STOP_RELEASE`
这表示已执行并通过选定 XYZAC 案例的 WASM/Worker 控制闭环,但不宣称整个 LinuxCNC 或所有 AXIS 配置已经数值完全对标。
本项目铁律:所有 G-code、运动、门控、I/O、HAL、刀具和状态业务不得由 JavaScript 模拟;React/Three 只做用户输入与显示投影。
## 对标输入
| 作用 | LinuxCNC 原始输入 | Web/WASM 输入 |
| --- | --- | --- |
| AXIS 菜单、快捷键、状态轮询 | `linuxcnc/src/emc/usr_intf/axis/scripts/axis.py`、`linuxcnc/share/axis/tcl/axis.tcl` | `web/react/src/App.tsx`、`web/app/axis-contract.mjs` |
| 程序/暂停/恢复/停止 | `axis.py:task_run/task_step/task_pause/task_resume/task_stop` | `PROGRAM_RUN/STEP/PAUSE/RESUME/ABORT` -> `SimTask` + LinuxCNC source TP |
| JOG/HOME | `axis.py:jog_on/jog_off/home_all_joints/home_joint` | `JOG_START/JOG_STOP/HOME` -> `WasmLinuxcncTp/WasmHomeLimit` |
| MDI/Touch Off | `axis.py:send_mdi_command/touch_off_system/touch_off_tool` | `MDI_EXECUTE` -> `linuxcnc-wasm-sim_program` |
| Feed/rapid/spindle override | `axis.py:set_feedrate/set_rapidrate/set_spindlerate` | `SET_OVERRIDE` -> WASM TP/IO |
| 主轴、刹车、冷却、刀具、探针 | `axis.py:spindle/brake/mist/flood/change_tool` | `SPINDLE_COMMAND/SPINDLE_BRAKE/COOLANT_COMMAND/TOOL_*/PROBE_CONFIG` -> WASM IO |
| 预览、实时轨迹、清屏 | `linuxcnc/lib/python/rs274/glcanon.py` | `XyzacToolPreview`,只投影 WASM preview/motion trace |
| XYZAC M428/M429/M430 | `switchkins_postgui.hal`、`remap_subs/428remap.ngc` 等 | `KINEMATICS_SELECT` -> WASM kinematics/switchkins |
源码输入哈希:
```text
axis.py 328c038a1e8fefd952e53c8e398e3fa98b04602351c44318edd1b044e8f63ffa
glcanon.py 0dfb20f1fdc05289894f3aa638357d98e1063fbd53329c723c777399ec7b33b7
xyzac-trt.ini f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811
source slices c9f0b4dc686717b37565f85fb0ab8cc8ded9cfa0028cc0aaaccdbcf808c4c524
```
AXIS 源控件清单:菜单 80、工具栏 20、快捷键 10,共 110 条,来自 `web/workspace/xyzac-trt-source-slices.mjs`。
## 权威状态边界
选定 `xyzac-trt` 配置时,Worker 强制建立 LinuxCNC WASM TP,不接受缺省 planner 降级。前端只负责输入和显示投影,业务值、门控、运动和设备状态均来自以下 WASM 边界:
| 数据/动作 | 唯一来源 | 前端使用方式 |
| --- | --- | --- |
| G-code 解析、当前行、活动段、预览 | `WasmProgramRuntime` (`linuxcnc-wasm-sim_program`) | 显示源代码和路径,不在 React 重解析 |
| XYZAC 位姿、速度、队列、前瞻 | `WasmLinuxcncTp` (`linuxcnc-source-tp`) | DRO、实时刀具、执行状态 |
| 五关节命令值 | WASM TP 当前 pose + `sim_kinematics_switch_inverse_mode` | 关节/DRO 投影;无 JS 插值 |
| JOG 目标、速度和限位 | `sim_linuxcnc_tp_jog`、WASM TP axis limits/jog scale | Worker 只传递轴、方向、步长 |
| HOME、逐关节 homed、limit | `WasmHomeLimit` (`homed_mask`) | Home 按钮和轴状态门控 |
| ESTOP、POWER、主轴、探针、HAL | `WasmXyzacIo` / `sim_io` / `sim_hal` | 设备状态、报警和命令结果 |
| Task/程序门控 | `SimTask` `sim_task_command_gate` | 所有按钮 `disabled` 和错误原因 |
| Feed/Jog/Max/Spindle override | WASM TP/IO getter/setter | 受控 slider,值由快照回读 |
| 视图缩放、旋转、平移、tab | React/Three 本地 UI | 仅显示交互,不参与运动计算 |
实时参数区显示:主轴实际转速 `speed_rpm`、主轴指令转速 `command_rpm`、G-code `S` 值、TP 实际进给 `velocity_mm_min`、G-code `F` 值、TP 速度/上限、F/S override、队列深度、活动段和当前执行行。所有值来自 WASM program/TP/IO 快照,参数区的 `data-xyzac-parameter-source` 为 `linuxcnc-wasm`;未取得快照时显示 `-`。
程序预览替换规则:`PROGRAM_LOAD` 开始时先清空旧的 canonical preview;如果 WASM 解析或子程序加载失败,错误响应中的路径仍为空,不会把旧程序刀路重新投影出来。新的 WASM preview 成功后才替换为新程序路径。
在 WASM 快照尚未到达前,界面显示 `WASM PENDING` 并禁用运动操作;不会使用 JS 默认轴值、任务状态或 override 值。浏览器定时器只发送 `SIM_STEP` 采样请求,不执行 G-code、轨迹、运动学或设备状态计算。React/Three 中的定时器、相机、网格、刀具网格和文本格式化均为显示层,不产生或修改任何 CNC 状态。
## 按钮/控件矩阵
下表中的“通过”包含正常路径和刻意触发的 LinuxCNC 门控拒绝;拒绝不是失败,必须返回可解释的 gate/error key。
| 面板区域 | Web 控件 | LinuxCNC 命令/源函数 | WASM 权威输出 | 结果 |
| --- | --- | --- | --- | --- |
| 会话 | HELLO、SESSION_ATTACH | AXIS 启动/状态轮询 | `sim_protocol`、`SimTask` | 通过 |
| Machine | Emergency Stop、Release E-stop | `General_Halt`、`emcTaskAbort` | WASM IO + Task ESTOP/RESET | 通过 |
| Machine | Power On/Off | `commands.power`、`emcTaskSetState` | `sim_io_set_power` | 通过 |
| Machine | Reset | `commands.reset` | WASM Task/IO reset | 通过 |
| Machine | HOME ALL | `home_all_joints` | `WasmHomeLimit` 五关节回零 | 通过 |
| Machine | Home X/Y/Z/A/C | `home_joint` | `WasmHomeLimit.homed_mask` 逐关节回零 | 通过 |
| Machine | Run Startup Sequence | release -> power -> home -> run | Worker 固定顺序 | 通过 |
| Program | Load | `open_file_guts` | WASM interpreter preview | 通过 |
| Program | Run | `task_run` / `AUTO_RUN` | WASM program + source TP | 通过 |
| Program | Pause / Resume | `task_pause` / `task_resume` | WASM interpreter + TP pause/resume | 通过 |
| Program | Stop | `task_stop` / `c.abort` | WASM TP abort,队列清零 | 通过 |
| Program | Restart | `reload_file` / first line | WASM program reload | 通过 |
| Program | Step / Seek | `task_step` / `set_first_line` | WASM interpreter | 通过 |
| JOG | X、Y、Z、A、C +/-、Stop | `jog_plus/jog_minus/jog_on/jog_off` | WASM source TP jog velocity/queue | 通过 |
| JOG | 越界/非有限速度 | LinuxCNC motion limit/ferror | WASM 错误 key | 通过拒绝 |
| MDI | Go、历史命令 | `send_mdi_command` | WASM interpreter/canonical | 通过 |
| MDI | 运行中 MDI | `manual_ok` | `mdi_while_program_active` | 通过拒绝 |
| Override | Feed Override 50%/200% | `set_feedrate` | WASM TP feed override | 通过 |
| Override | Feed > 200% | `set_feedrate` range | `motion_override_range` | 通过拒绝 |
| Spindle | CW/CCW/Stop | `spindle_forward_toggle/spindle` | WASM spindle command | 通过 |
| Spindle | +/- | `SPINDLE_INCREASE/DECREASE` | WASM signed delta | 通过 |
| Spindle | Brake | `EMC_SPINDLE_BRAKE_ENGAGE/RELEASE` | WASM IO brake state | 通过 |
| Coolant | Mist/Flood | `mist_toggle/flood_toggle` | WASM device IO | 通过 |
| Tool | Prepare、Prepared、Change、Changed | iocontrol/tool handler | WASM IO tool state | 通过 |
| Probe | Arm、Disarm、Trip | probe canonical/HAL | WASM IO + HAL | 通过 |
| Switchkins | Identity/M429、TCP: XYZAC/M428、User K/M430 | `switchkins_postgui.hal` + remap files | WASM kinematics `active_type` | 通过 |
| Switchkins | IDENTITY <-> TCP: XYZAC <-> USERK 任意切换 | `KINEMATICS_SELECT` / `M429` / `M428` / `M430` | 当前 WASM TP pose + WASM inverse/forward | 通过 |
| Preview | Preview、DRO、路径/实时/限位/速度/偏置/范围 | `glcanon.py` draw layers | WASM pose/joints/toolpath | 通过投影 |
| Preview | 缩放、平移、旋转、原点坐标系 | AXIS view handlers | React/Three display-only state | 通过显示 |
| Diagnostics | 状态、门控、错误、恢复 | AXIS status/error channel | Worker snapshot/ledger | 通过 |
## 本轮自动化结果
聚合用例:`web/worker/test_xyzac_panel_buttons.mjs`
```json
{
"schema": "xyzac-panel-button-parity.v1",
"source": "linuxcnc-wasm-worker",
"verified_paths": 97,
"expected_error_paths": 8,
"blocked_by_abi_paths": 4,
"selected_case_release_status": "STOP_RELEASE"
}
```
97 条通过路径包括:
- Machine:HELLO、SESSION_ATTACH、LOAD_WORKSPACE、ESTOP、ESTOP_RESET、POWER、RESET、HOME ALL、启动序列;
- Machine:五个轴的独立 HOME、`homed_mask` 聚合和部分回零门控;
- Program:LOAD、RUN、SIM_STEP、PAUSE、SNAPSHOT、RESUME、ABORT/STOP、RESTART、STEP、SEEK、按行恢复;
- JOG:XYZAC 五轴启动、固定 tick 采样和停止;
- MDI:正常 G-code、运行中门控和非法 G-code 错误回显;
- Feed Override:50%、200% 和 201% 拒绝;
- 主轴:CW、signed increase/decrease、Stop、Brake engage/release;
- Coolant、Tool prepare/change ack、Probe arm/disarm、ESTOP recovery;
- M428/M429/M430、VISUAL_CLEAR 和诊断快照。
- Switchkins 直接按钮在非零姿态下 `0 -> 1 -> 2 -> 0 -> 1 -> 2 -> 0` 循环切换;每一步的 kinematics pose 与 TP pose 逐轴一致。
8 条错误路径是刻意验证的拒绝:OFF 状态 Run/HOME/JOG/MDI/M428、Feed 201%、非法 MDI、运行中 MDI。它们均返回 LinuxCNC/WASM 可读原因,没有把拒绝当成成功。
3D_Chips 长程序补充验证:
```text
source_length: 200509
line_count: 4711
preview_segments: 4684
fixed_tick_samples: 3000
execution_line: 337
execution_trace_entries: 335
motion_authority: wasm
motion_delta_snapshots: 0
ledger_audit_window: 32
running_queue_gaps: 0 (excluding the initial startup tick)
```
## 长距离连续性判定
`3D_Chips.ngc` 第 17 行设置 `G64 P.1`,后续是大量连续短段。对标 LinuxCNC source TP 时,正常表现是 TP 在当前段运行期间持续保留前瞻队列;G64 不应在普通同模式段之间出现人为的零速度空队列 tick。此前 Worker 在等待当前段完全结束后才入队下一段,造成截图中可见的短暂停顿,这不是源 LinuxCNC 的合理结果。
已修复为:同一运动学模式的下一段可在当前 TC 活动时进入 source TP;M428/M429/M430 模式变化仍由 WASM Task/TP 同步边界约束;队列容量由 `sim_linuxcnc_tp_queue_capacity` 读取,按真实 `TP_DEFAULT_QUEUE_SIZE` 限流。1000 tick 连续性回归中,除启动第 1 tick 外 `program_status=running && queue_depth=0` 的次数为 `0`;第 82 行附近仍保持 31 段前瞻队列。
## 当前未闭合项
以下项目仍明确记录为 `blocked_by_abi`,没有伪造通过:
1. `Override Limits`:原始 AXIS `toggle_override_limits` 有独立 motion command;选定 XYZAC WASM 配置目前把 limits 作为配置所有权,尚无等价 command ABI。
2. `Block delete` 和 `Optional pause`:当前 AXIS 图标已禁用,尚未写入 WASM interpreter modal/control state;不保留本地假状态。
3. `Tool Touch Off`:当前按钮发送固定 `G43`;原始 AXIS 先取得用户值,再发送 `G10 L10/L11` 和 `G43`,交互输入流程尚未闭合。
4. `Edit tool data`、`Quit`:依赖桌面进程/外部编辑器,属于浏览器 `HOST_BOUNDARY`,不在仿真 Web 范围内。
## 复现命令
```bash
scripts/build-wasm.sh
npm run build --prefix web/react
node --experimental-strip-types web/worker/test_xyzac_panel_buttons.mjs
node --experimental-strip-types web/worker/test_xyzac_trt_case.mjs
node --experimental-strip-types web/worker/test_xyzac_button_constraints.mjs
node --experimental-strip-types web/worker/test_xyzac_workflow.mjs
node --experimental-strip-types web/worker/test_xyzac_manual_control.mjs
node --experimental-strip-types web/worker/test_xyzac_feed_override.mjs
node --experimental-strip-types web/worker/test_xyzac_trt_io_authority.mjs
node --experimental-strip-types web/worker/test_xyzac_3d_chips.mjs
bash -n run-5axis-rtcp.sh
```
本轮结果:WASM 构建通过,原生 CTest 22/22 通过,XYZAC 聚合按钮测试通过,3D_Chips 固定 tick 长程序测试通过。Jog Speed、Max Velocity、Spindle Override 已改为 WASM TP/IO 状态和命令;前端运行态不再使用 React 默认值。发布站点 UI 运行/暂停/恢复/停止也已在 Chromium 仿真环境执行;Safari 和真实物理断电不属于本项目范围。
本轮连续性修复后,`3D_Chips.ngc` 3000 tick 执行到第 337 行,`running_queue_gaps=0`;这证明长距离运行中的中间停顿已不再由 Worker 队列饥饿制造。
暂停/恢复修复:暂停期间若有一个 `SIM_STEP` 已在浏览器与 Worker 之间传输,WASM protocol 现在接受该帧为稳定无动作轮询,不会消耗错误序号;随后 `PROGRAM_RESUME` 正常恢复 source TP。
发布站点 r11 暂停/恢复验证:
```text
url: https://49.232.250.35:58010/
release: 20260902-linuxcnc-wasm-pause-resume-r11
resource_sha256: index-EXbw4yiL.js 6759738df285bc5e2f5c3fb84043dc09fb82292bbc1c3c1011a558c8654c96ab
sim-worker-entry-DjXIMZ0D.js 190515af943d70e93037e88a5cf63b3b8e9cda675a8338cfc1fb4bb954bfd685
linuxcnc-sim-core-D3Qw0e1g.wasm f1b1961279cf8ff7ef2e3fcd0068b3ba79de57abe1e56d5c32b16d17568b9536
before_pause: line=62, tick=140, queue_depth=1, velocity=28.2842712475
paused: line=62, tick=145, queue_depth=1, velocity=28.2842712475
paused_after_1.5s: tick=145 (stable)
resumed: line=62, tick=160, queue_depth=1, velocity=4.96408252063
after_resume: line=68, tick=360, queue_depth=2, velocity=32.8295260060
PROGRAM_RESUME: acknowledged; browser_errors: 0
screenshot: /tmp/published-pause-resume-final-r11c.png
screenshot_sha256: 9d1660fd043dd0628ff2989a3e139a3239acd6386d141657a5834c9d2f1bc5a7
```
发布站点 r13 实时参数验证:
```text
url: https://49.232.250.35:58010/
release: 20260902-linuxcnc-wasm-live-parameters-r13
parameter_source: linuxcnc-wasm
idle: Spindle 0.0 rpm / Feed actual 0.0 mm/min / TP velocity 0.000 mm/s / Planner 0 queued
running: Feed actual 540.0 mm/min / TP velocity 9.000 mm/s / Planner 1 queued / Segment 13 / Line 65
fields: spindle actual+command+program S, feed actual+program F, TP velocity+limit,
F/S override, queue depth, active segment, execution line
browser_errors: 0
screenshot: /tmp/published-live-parameters-r13.png
screenshot_sha256: 4e25750136d9b5a8ed633a15091128a94fc9bbec9af857c71d9bf5dd01270fb6
```
发布站点 r14(程序主轴同步 + 实时参数)验证:
```text
url: https://49.232.250.35:58010/
release: 20260902-linuxcnc-wasm-live-parameters-r14
resource_sha256: index-CDC5yZJC.js c3a6aa009e091d5fcb2098c7254313d987229abc1e4765052edb15b465ca0fc4
sim-worker-entry-BQKHHWpv.js e72d80d9ae0fd679d92d3efbad4dba1eef55f9ce7b54bcd06e4a0d644c3cfb1a
linuxcnc-sim-core-fuIvKetC.wasm ad4e17ecbf73afbad0d9f069ab20ba3c66b6cc4d75ecdb2b3182673490ee916d
3D_Chips.ngc running: spindle_actual=210 rpm, spindle_command=1600 rpm, program_S=1600 rpm,
feed_actual=1200 mm/min, program_F=2250000 mm/min,
tp_velocity=20 mm/s, queue_depth=22, active_segment=21, line=38
browser_errors: 0
screenshot: /tmp/published-live-parameters-r14.png
screenshot_sha256: 7f5307c913d8bc4e60c2a31c29aacc9afd1b0d91fac4a4f22e8a64fc2ea37a1e
```
发布站点 r15(程序预览原子替换)验证:
```text
url: https://49.232.250.35:58010/
release: 20260902-linuxcnc-wasm-program-replace-r15
initial preview: 29 segments
invalid PROGRAM_LOAD: preview 0 segments, error shown, old path not retained
valid PROGRAM_LOAD: preview 1 new segment
browser_errors: 0
screenshot: /tmp/published-program-replace-r15.png
screenshot_sha256: c3e0a5b992381e7b6cb26f98a30338935df5239c875755727fc678911452ac24
resource_sha256: index-DUB3ldSh.js 4d71a2eb0b162608d7a47b63274cf274a57b1c2f2620b69af139b12c2ba22cce
sim-worker-entry-yfjiq6MN.js a0cf75522ddc9c6712126de86937ec89f24574bf0e0dad3dbddc6d807fb352b7
linuxcnc-sim-core-fuIvKetC.wasm ad4e17ecbf73afbad0d9f069ab20ba3c66b6cc4d75ecdb2b3182673490ee916d
```
发布站点 r10 连续性验证:
```text
url: https://49.232.250.35:58010/
release: 20260902-linuxcnc-wasm-continuous-r10
resource_sha256: index-Byst0JhC.js 26d3f21f496c8223218761ba641dab8b4f36fd18892f260c7bf5b5cfa83cafec
sim-worker-entry-BTVfbu6g.js adf2ac5621ba388408f31ceab4bbcbfa6ba2923577d8eb69aa79ea54f3be0134
linuxcnc-sim-core-B2786u4k.wasm 3163f5ff63c5f3e1d2c07d1d948fce328bf3ad919eee188e432f7ba57468fc71
3D_Chips.ngc: loaded -> POWER -> HOME ALL -> RUNNING
sampled_running_frames: 33
queue_gaps: 0
first_running: current_line=62, queue_depth=3, velocity=20, active_id=16
last_running: current_line=69, queue_depth=2, velocity=16.6666666667, active_id=17
browser_errors: 0
screenshot: /tmp/published-r10-continuous.png
screenshot_sha256: c93fd23e648e49bfa9359af245372f03f3fccee123497909f3dcc005f5a59749
```
发布站点 r5 浏览器冒烟:
```text
url: https://49.232.250.35:58010/
result: verified
startup -> pause -> resume -> stop: verified
pause_tick: 140 (position held while PAUSED)
resume_tick: 160 -> continued running
stop: IDLE / queue cleared
JOG X/Y/Z/A/C, spindle CW/CCW/+/-/brake, coolant, tool/probe,
Feed Override, M428/M429/M430, VISUAL_CLEAR, Preview/DRO/MDI/Diagnostics
and view controls: clicked with no browser errors
output_sha256: bdf705473e6cda94d721aa23329257adf4a68b705add7650da5cc47012ab70b0
```
发布站点 r6(历史 WASM authority 版本)浏览器验收:
```text
url: https://49.232.250.35:58010/
release: 20260902-linuxcnc-wasm-authority-r6
resource_sha256: index-DEinWp01.js 79f5e933909fc631186db9b5091b6239f30c1ade612bb908a4ab9fcf79965343
sim-worker-entry-7q53Ew6x.js e5c239104d6676f242ea45cb3ddaf78a379aaaecaca5ff26031dee1ba9496e7c
linuxcnc-sim-core-CJilJ2YZ.wasm 13e2746899b0cac5cf91189c00a6174160202f8e632b53c5701e9664335c97cf
initial_dom: data-axis-authority=linuxcnc-wasm-worker, data-axis-state-source=linuxcnc-wasm,
data-axis-motion-authority=wasm, data-axis-task-authority=wasm_task_canonical
3D_Chips.ngc: loaded from published workspace, preview 100%, source TP running current line 22
browser_errors: 0
screenshots: /tmp/published-r6-authority.png (e58205228a04b386c7b7514bf03c71df7b53f66f2d9ebe2292931b36ef0edb55)
/tmp/published-r6-running.png (7fc474d0cb31a0fdd4afb198061e9812c753684e3d2963cc004ec4483c5c4990)
```
当前发布资源为 r10;r6 资源 hash 仅作为历史记录保留。
发布站点 r7(IDENTITY/TCP: XYZAC/USERK 切换版本)浏览器验收:
```text
url: https://49.232.250.35:58010/
release: 20260902-linuxcnc-wasm-switchkins-r7
startup: WASM authority ready, initial machine OFF
power -> HOME ALL -> IDENTITY -> TCP: XYZAC -> USERK -> IDENTITY -> TCP: XYZAC -> USERK: verified
active modes: 0 -> 1 -> 2 -> 0 -> 1 -> 2
browser_errors: 0
screenshot: /tmp/published-switchkins-r7.png
screenshot_sha256: 920b85ad7fd6451dfb134255ffdcc4f03a7aa615ebf486c63cde8582e17af195
```
## 证据哈希
```text
panel_button_test_output 9bddcd8b0219925e00598e081dc7a4ed0f4868e8822a21f0ccd08d8e9d355d45
button_constraints_output 5b6f4ce1dec03764dcb234793ff96fc2f04d15ab954620a44dbb1210ec64a5a5
feed_authority_output deab4a51ee0e56d4f8134efba4b7a97a2b88f37d9165cf730207f71886df8526
3d_chips_test_output c97caef2ea5cf3841e1238ec443d0eebadad8cb0048af829111c06e99c9fcdd7
3d_chips_no_gap_output c97b5fbb43a6e81a8ee15272bf71aa75ec0ba79be28a34f30b4221d50251c77d
wasm_io_source 1cb066d185dd2c5bbf435e022c2518483bd44962e5a0303248757b2da51740f7
wasm_io_c_source 09bce1430d6f71702ca0b3bf29ac50db377a6f232ce0d1a9b2c1eaac7ea2162e
panel_button_test_source 80782fc1f85f88beb48ec3dfaf638a93f2edb912e694840199dbff504477fe74
switchkins_direct_test_output 24e6cb8ec1195a97f71294b49217db0cbc0d7c3ecfa4bc8aa244ba3921c816a7
panel_r10_output 9bddcd8b0219925e00598e081dc7a4ed0f4868e8822a21f0ccd08d8e9d355d45
pause_resume_browser_screenshot 9d1660fd043dd0628ff2989a3e139a3239acd6386d141657a5834c9d2f1bc5a7
live_parameters_browser_screenshot 7f5307c913d8bc4e60c2a31c29aacc9afd1b0d91fac4a4f22e8a64fc2ea37a1e
program_replace_browser_screenshot c3e0a5b992381e7b6cb26f98a30338935df5239c875755727fc678911452ac24
```
报告对应提交前工作树保留了既有 coverage 大文件和 LinuxCNC 子模块改动;本轮提交包含 WASM TP/IO/HOME authority、无 JS JOG 运动计算、前端 pending 投影、长程序快照性能修正、聚合测试和本报告。
## AXIS 源码逐项映射与发布站点审计
本轮按 `linuxcnc/src/emc/usr_intf/axis/scripts/axis.py` 对照了 `manual_ok()`、`ensure_mode()`、`open_file_guts()`、`task_run/task_pause/task_resume/task_stop/task_step`、`jog_on/jog_off_actual`、`mist_toggle/flood_toggle`、`spindle_*`、`change_tool` 和 MDI 入口。映射约束与禁止事项见 [`LinuxCNC AXIS Web 铁律`](../docs/linuxcnc-axis-web-iron-law.md)。
发现并修复的实际偏差:
- 手动/MDI 页签现在通过 WASM `SET_MODE` 同步 LinuxCNC task mode;之前仅切 React 页签会使 JOG、主轴和 MDI 被错误地显示为不可用。
- React 现在保留 `panel_snapshot.io` 的 WASM canonical envelope,正确读取 `state.ready/estop` 和设备状态。
- `JOG_STOP` 对 LinuxCNC source TP 只停止当前 jog 并保持 TP runnable;不再伪造 `AUTO_PAUSE`,避免后续 MDI/主轴操作得到 `linuxcnc_tp_paused` 调度错误。
- MDI 在前一次 JOG_STOP 后明确恢复 WASM TP runnable,再由 WASM 解释器/队列执行 MDI block。
- 工具栏 Reset 和 Reopen defaults 均走 WASM gate;Reopen 执行 `LOAD_WORKSPACE + PROGRAM_LOAD`,不再只修改本地编辑器字符串。
发布地址 `https://49.232.250.35:58010/` 的 Chromium 审计命令:
```text
PLAYWRIGHT_MODULE=... node scripts/test_xyzac_panel_buttons_browser.mjs
```
审计覆盖:WASM startup、Open G-code file、`3D_Chips.ngc`、Load case、POWER ON、HOME ALL、RESET、IDENTITY/TCP:XYZAC/USERK、视图和 Clear plot、MANUAL/MDI、XYZAC 五轴 JOG、主轴正反转/停止/刹车、Mist/Flood、Tool/Probe、Jog/Feed/Spindle/Max velocity overrides、MDI、Run/Pause/Resume/Abort/Step、Run Startup Sequence、编辑器 Load/Reopen 和 ABI-disabled 控件。发布版本 `20260902-linuxcnc-wasm-panel-audit-r27` 最终结果为 20 组 verified、83 个按钮、37 个按 LinuxCNC gate 禁用、`browser_errors=0`;WASM worker authority 标记为 `linuxcnc-wasm-worker` / `wasm_task_canonical`,截图 `/tmp/xyzac-panel-buttons-browser.png`,SHA-256 `dbf6d196cec3fe765a64d5a1d194b7ebb5681a30555f238e084c9ad6deac6032`。
源代码对标结论:AXIS 的可执行操作均通过对应 WASM command/result 和后续 snapshot;`Override Limits`、Block delete/Optional pause、完整 Tool Touch Off/G10 交互以及桌面 tool editor/Quit 仍明确记录为 `blocked_by_abi` 或浏览器宿主边界,不能用 JavaScript 模拟。整体 `release_status` 继续保持 `STOP_RELEASE`,因为本报告不替代完整 TP 数值 oracle。
## OPFS 页面临时缓存
页面上下文和断电 checkpoint 由 Dedicated Worker 的 OPFS 临时 JSON 服务保存,React 不直接访问 OPFS 或 `localStorage`。`UI_STATE_SAVE/LOAD/CLEAR` 是 Worker storage service,不进入 LinuxCNC WASM CNC protocol sequence;缓存中的程序文本恢复时必须再次经过 WASM `PROGRAM_LOAD`。
```text
node web/worker/test_opfs_ui_state.mjs
node web/worker/test_opfs_worker_bridge.mjs
status: verified
double_slot / generation / TTL / hash / corruption fallback: verified
power_off_checkpoint OPFS round-trip: verified
UI cache does not consume WASM sequence: verified
```
详细字段、文件布局、原子写入和恢复边界见 [`AXIS Web/WASM OPFS 页面临时缓存设计`](../docs/linuxcnc-axis-web-wasm-opfs-ui-cache.md)。
发布版本 `20260903-linuxcnc-wasm-opfs-r30` 的真实 Chromium OPFS 往返:改变 Preview 视角为 `z`、切换 DRO、选择 A 轴,等待 1.8 秒防抖保存后刷新页面;刷新后 `view=z`、DRO 仍激活、A 轴仍选中,OPFS 中实际存在 `state-b.json` 和 `manifest.json`,两次 `localStorage` key 数均为 `0`,`browser_errors=0`。截图 `/tmp/xyzac-opfs-browser.png`。
## 实时轨迹程序替换验证
发布版本 `20260903-linuxcnc-wasm-live-replace-r32` 执行了真实浏览器流程:第一程序运行完成并产生 5 个 live trace 点,随后加载第二个程序。第二程序加载完成后 WASM preview 为 1 段、live trace 为 0,旧程序实时轨迹不再保留,`browser_errors=0`。截图 `/tmp/xyzac-live-trace-replacement.png`,SHA-256 `4bdf642d3e51cffae8afadb881422d58408d3f095aa795566d045a35d2c05482`。