149 lines
5.6 KiB
Markdown
149 lines
5.6 KiB
Markdown
# 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。
|