Move panel temporary state to worker OPFS

This commit is contained in:
2026-09-03 14:03:32 +08:00
parent 0f6691a9fc
commit 2b1e249baf
22 changed files with 688 additions and 42 deletions

View File

@@ -27,6 +27,8 @@
- 禁止用本地按钮状态覆盖 WASM 的 task、IO、HAL、tool、probe、home/limit 或 planner 状态。
- 禁止把按钮“可点击”、静态截图或单次页面加载当作实现证据;必须有 worker/WASM command result 和后续 snapshot。
- 不具备对应 LinuxCNC/WASM ABI 的功能必须保持禁用,并明确记录 `blocked_by_abi`,不得用 JS 模拟补齐。
- 页面临时上下文和断电 checkpoint 可以使用 Worker-only OPFS 临时 JSON 保存,但 OPFS 只负责持久化,不得成为 CNC 业务 authority;恢复时必须重新交给 LinuxCNC WASM 校验和执行。
- React 不得直接访问 OPFS、`localStorage` 或 WASM 指针;所有页面缓存读写必须通过 Worker 消息完成,并带 schema、context、generation 和 hash。
## 验收证据

View File

@@ -270,3 +270,5 @@ flowchart LR
~~~
当前浏览器审计要求:WASM authority 标记正确、browser_errors=0、所有按钮有 command result 或明确 gate/ABI 原因;面板 verified 不等于完整 TP oracle verified。
页面临时上下文使用 Worker-only OPFS 双槽位 JSON;它不进入 CNC protocol sequence,也不保存运动 authority。断电 checkpoint 由 Worker 写入 OPFS,下一次 LOAD_WORKSPACE 时再提交给 LinuxCNC WASM 校验和恢复。

View File

@@ -0,0 +1,148 @@
# AXIS Web/WASM OPFS 页面临时缓存设计
## 目标
页面刷新或 Worker 重启后,恢复用户需要的页面上下文,同时不把页面缓存误认为 LinuxCNC 运行时权威。所有 G-code、Task、TP、运动学、轴值、HAL、IO、工具、探针和错误状态仍由 LinuxCNC WASM checkpoint/recovery 恢复;OPFS 临时 JSON 只保存页面上下文和可重新提交给 WASM 的程序草稿。
## 分层
~~~mermaid
flowchart LR
UI[React 页面状态<br/>页签 / 相机 / 图层 / 编辑器] -->|UI_STATE_SAVE| WC[ReactWorkerClient]
WC -->|Worker message| W[Dedicated Worker]
W --> OPFS[OPFS 双槽位临时 JSON]
OPFS -->|UI_STATE_LOAD| W
W --> WC
WC --> UI
P[POWER OFF response] --> W
W --> CP[WASM power-off checkpoint]
CP --> OPFS
OPFS -->|LOAD_WORKSPACE| W
W --> WASM[LinuxCNC WASM restore]
WASM --> R[Task/TP/pose/IO snapshot]
R --> UI
~~~
## 缓存范围
允许写入 UI 临时 JSON 的字段:
- selectedAxis、leftTab、rightTab;
- view、zoom、rotateMode;
- coordType、displayType、metric;
- preview layers;
- casePath、program.path、program.source;
- cache schema version、context_id、generation、saved_at_ms、state_hash。
禁止由 UI JSON 直接恢复并驱动运动的字段:
- task state/mode/program status;
- TP queue、active segment、velocity、acceleration、jerk;
- XYZAC pose、J0..J4 joints、RTCP/FK/IK state;
- HAL、spindle、coolant、tool、probe、home/limit、fault;
- operation ledger、event cursor 和已提交副作用。
这些字段必须来自 WASM checkpoint;缓存的 program.source 重新恢复时也必须再次发送 PROGRAM_LOAD,由 WASM interpreter 解析。
## 文件布局
```text
OPFS root/
linuxcnc-web-wasm/
ui-state/
xyzac-trt/
state-a.json
state-b.json
manifest.json
xyzac-trt-power-off/
state-a.json
state-b.json
manifest.json
```
`state-a.json` 和 `state-b.json` 轮换写入。写新槽位成功并 flush 后才更新 manifest;任一写入中断时,旧槽位仍可读取。读取时验证两个槽位,选择 generation 最大且 hash 正确的记录。
## JSON 记录
```json
{
"schema": "linuxcnc-ui-temp-json.v1",
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
"key": "xyzac-trt",
"generation": 12,
"saved_at_ms": 1770000000000,
"state": {
"version": 1,
"leftTab": "manual",
"rightTab": "preview",
"view": "p",
"zoom": 1,
"program": { "path": "browser/demo.ngc", "source": "..." }
},
"state_hash": "fnv1a64:...",
"record_hash": "fnv1a64:..."
}
```
约束:单记录最大 1 MiB,程序缓存最大 900 KiB,缓存 TTL 为 7 天;超限、过期、JSON 损坏、schema/context/hash 不匹配时返回 empty/blocked,不能回退到旧 UI 假状态。
## 生命周期
~~~mermaid
sequenceDiagram
participant A as App.tsx
participant C as WorkerClient
participant W as Worker
participant O as OPFS
participant X as WASM
A->>C: ensureSession
C->>W: HELLO / SESSION_ATTACH
A->>C: UI_STATE_LOAD
C->>W: custom Worker cache request
W->>O: scan slots + verify hash/TTL
O-->>W: newest valid UI state
W-->>C: UI_STATE_RESULT
C-->>A: restore tabs/camera/editor draft
A->>W: LOAD_WORKSPACE
W->>O: load power-off checkpoint if present
W->>X: restore WASM task/TP/program/IO
X-->>W: authoritative snapshot
W-->>A: state snapshot
A->>C: debounced UI_STATE_SAVE
C->>W: save state JSON
W->>O: write inactive slot, flush, update manifest
~~~
高频 SIM_STEP 不写 OPFS。页面状态变化使用 500ms 防抖;程序编辑器输入连续变化只保存最后一个稳定版本。POWER OFF 的 WASM checkpoint 由 Worker 在收到成功回执后写入专用 key;下一次 LOAD_WORKSPACE 由 Worker 自动读取并交给 WASM restore。
## 安全和一致性
1. React 不访问 navigator.storage、WASM 指针或 OPFS 文件。
2. Worker 是 OPFS 唯一写者;所有消息串行处理,避免控制命令和缓存写入竞争。
3. UI_STATE_RESULT 不进入 CNC state reducer,不改变 protocol sequence、task tick 或运动状态。
4. UI cache 的 program.source 只能作为输入草稿;加载和预览仍必须由 WASM PROGRAM_LOAD 生成。
5. OPFS 不可用时,页面显示可诊断的 blocked/empty 状态,不使用 localStorage 或静默内存替代。
6. 清理只删除 UI 临时槽位;不会删除 WASM checkpoint、事件日志或程序运行状态。
## 代码和测试
| 能力 | 文件 |
| --- | --- |
| 双槽位 JSON、TTL、hash、OPFS API | web/worker/opfs-ui-state.mjs |
| Worker-only 路由、断电 checkpoint | web/worker/opfs-worker-bridge.mjs |
| React UI 状态读写调用 | web/react/src/worker-client.ts、web/react/src/App.tsx |
| 浏览器 Worker 入口 | web/react/src/sim-worker-entry.ts、web/worker/sim-worker.mjs |
| 单元回归 | web/worker/test_opfs_ui_state.mjs |
| 原生 OPFS 能力探针 | web/workspace/opfs-runtime.mjs、web/app/browser-opfs-probe.html |
验证命令:
```text
node web/worker/test_opfs_ui_state.mjs
npm run typecheck --prefix web/react
npm run build --prefix web/react
```
真实浏览器还需验证 OPFS quota、Worker reload、坏槽位、过期记录和离线启动;这些属于独立的浏览器恢复 oracle,不等同于 UI cache 单元测试通过。
已完成的线上浏览器样例:`scripts/test_xyzac_opfs_browser.mjs` 在 `20260903-linuxcnc-wasm-opfs-r30` 上验证视角、DRO 页签、A 轴选择刷新后恢复;实际 OPFS 文件为 `state-b.json`/`manifest.json`,`localStorage` 为空,浏览器错误为 0。quota、崩溃和离线 oracle 仍独立保留为 pending。