推进 OPFS 机器会话快照保存入口

This commit is contained in:
2026-06-14 15:03:33 +08:00
parent 2bff22e0d9
commit a829984300
4 changed files with 147 additions and 0 deletions

View File

@@ -70,6 +70,22 @@ remap 等语义必须来自 LinuxCNC upstream 或 vendored LinuxCNC C/C++/配置
native、Node、browser gate 都按顺序通过;
- browser 不能依赖目录枚举。
4. 加快实质性推进铁律。
核心移植已稳定后,暂停性能测试和纯 test glue 微整理,后续默认优先推进能被用户或
上层 API 直接使用的实质性能力:
- 优先级从高到低为OPFS/session persistence、SDK/API surface、browser/UI workflow、
sim config coverage promotion、host/runtime boundary proof、文档化可执行工作流
- 每批应尽量产出一个可调用 helper、可复用 API、可验证用户流程、可执行 gate、
可提升覆盖面的 runtime boundary proof或者一份直接指导下一步实现的设计/清单;
- 允许继续做测试胶水,但只能作为支撑实质能力的验证,不再把“局部常量化/断言整理”
作为主要目标;
- 禁止启动性能测试,除非已有明确功能路径和用户流程需要性能基线;
- 禁止为了“推进”而扩大 CNC 语义边界G-code、tool、parameter、planner 等语义仍必须
由 LinuxCNC-owned runtime 提供;
- 每批仍必须小步、可验证、可回滚,并按 `text10.txt` 记录结论和下一步建议。
三、当前完成情况摘要
1. 当前工作树状态。
@@ -336,3 +352,66 @@ host_wasm_opfs_browser_smokes=ok
继续小批量 browser test glue guard。优先扫描 machine text store path assertion groups
或剩余 OPFS readback path groups 是否还能进一步局部常量化或复用 existing helpers
只改测试胶水不改变输入、OPFS path、WASM path 字符串或保存/读取行为。
三、2026-06-14 继续执行记录OPFS machine session snapshot save helper
本轮先将“加快实质性推进”规则写入 `text10.txt` 的当前项目纪律铁律中,并按该规则
继续推进 OPFS/session persistence 的实际 API 入口,而不是继续纯 test glue 微整理。
完成内容:
- 在“当前项目纪律”中新增“加快实质性推进铁律”;
- 在 `wasm-port/runtime/opfs/snapshot-store.js` 新增
`saveMachineSessionSnapshot(sessionId, machineId, options)`
- 该 helper 组合既有 `createMachineSessionSnapshotPayload(...)` 与
`saveSessionSnapshot(...)`,让上层能用一个入口生成并保存机器会话快照;
- 支持沿用既有 `storage`、snapshot `filename`、`createdAt`、`metadata` 以及
`iniFilename`、`parameterFilename`、`toolTableFilename`、`gcodeFilename`、
`iniOpfsPath`、`parameterOpfsPath`、`toolTableOpfsPath`、`gcodeOpfsPath`
选项;
- 在 Node OPFS smoke 中验证组合 helper 的自定义 snapshot 文件名、G-code path、
metadata 与 load round trip
- 在 browser INI panel smoke 中验证浏览器运行时可直接调用组合 helper并能加载回
parameter/tool-table/G-code path 与 metadata
- 未改变 session snapshot envelope 格式、版本、path model、file-service 行为或既有
save/load API
- 未改变 OPFS bridge behavior、SDK planner、interpreter、INI、tool、parameter 或
LinuxCNC-owned runtime semantics。
验证已通过:
```bash
git diff --check
wasm-port/tests/opfs/node/verify_file_service.sh
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
wasm-port/tools/verify_vendor_sync.sh
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_interp_browser.sh
wasm-port/tests/host/verify_host_smokes.sh
```
关键输出:
```text
opfs_file_service_node_smoke=ok
browser_ini_opfs_smoke=ok
vendor sync up to date
standalone CNC semantics guard complete
interp_wasm_node_smoke=ok
sim_configs_wasm_node_inventory_executed=28
sim_configs_wasm_node_inventory_passed=28
sim_configs_wasm_node_inventory_skipped=131
sim_configs_wasm_node_inventory_unexpected_fail=0
browser_interp_smoke=ok
host_wasm_opfs_browser_smokes=ok
```
下一步建议:
继续沿 OPFS/session persistence 的实质能力推进。优先新增
`loadMachineSessionSnapshot(sessionId, machineId, options)`,在读取 generic snapshot
后校验 `payload.machineId` 与机器文件 path 结构,并补 Node/browser smoke如果该入口
已足够清晰,再把 save/load machine session snapshot 接到 INI panel 的实际会话保存/恢复
workflow。仍只处理 OPFS/filesystem/browser boundary不引入或重写任何 CNC 语义。

View File

@@ -89,6 +89,12 @@ export async function saveSessionSnapshot(sessionId, payload, options = {}) {
return snapshot;
}
export async function saveMachineSessionSnapshot(sessionId, machineId, options = {}) {
assertPlainObject(options, "machine session snapshot save options");
const payload = createMachineSessionSnapshotPayload(machineId, options);
return saveSessionSnapshot(sessionId, payload, options);
}
export async function loadSessionSnapshot(sessionId, options = {}) {
const path = sessionSnapshotPath(sessionId, options.filename);
const text = await loadTextFile(path, options.storage);

View File

@@ -17,6 +17,7 @@
import {
createMachineSessionSnapshotPayload,
loadSessionSnapshot,
saveMachineSessionSnapshot,
saveSessionSnapshot,
} from "../../runtime/opfs/snapshot-store.js";
import {
@@ -204,6 +205,42 @@ TOOL_TABLE = browser-tool.tbl
customSnapshot.metadata.source,
"custom snapshot metadata",
);
const machineSessionSnapshot = await saveMachineSessionSnapshot(
"browser-machine-session",
"browser-smoke",
{
filename: "browser-machine-snapshot.json",
parameterFilename: "browser-linuxcnc.var",
toolTableFilename: "browser-tool.tbl",
gcodeFilename: "browser-smoke.ngc",
createdAt: "2026-06-08T00:00:00.000Z",
metadata: { source: "browser-machine-session" },
},
);
const loadedMachineSessionSnapshot = await loadSessionSnapshot(
"browser-machine-session",
{ filename: "browser-machine-snapshot.json" },
);
assertEqual(
machineSessionSnapshot.payload.files.parameters,
"linuxcnc/machines/browser-smoke/browser-linuxcnc.var",
"machine session snapshot parameter file",
);
assertEqual(
machineSessionSnapshot.payload.files.toolTable,
"linuxcnc/machines/browser-smoke/browser-tool.tbl",
"machine session snapshot tool table",
);
assertEqual(
loadedMachineSessionSnapshot.payload.files.gcode,
"linuxcnc/gcode/browser-smoke.ngc",
"machine session snapshot G-code",
);
assertEqual(
loadedMachineSessionSnapshot.metadata.source,
"browser-machine-session",
"machine session snapshot metadata",
);
await assertRejects(
"browser snapshot invalid filename save",
() => saveSessionSnapshot("browser-session", {}, {

View File

@@ -19,6 +19,7 @@ import {
createMachineSessionSnapshotPayload,
createSessionSnapshot,
loadSessionSnapshot,
saveMachineSessionSnapshot,
saveSessionSnapshot,
validateSessionSnapshot,
} from "../../../runtime/opfs/snapshot-store.js";
@@ -325,6 +326,30 @@ assert.deepEqual(
}),
customSnapshot,
);
const machineSessionSnapshot = await saveMachineSessionSnapshot(
"machine-session-1",
"xyzab-tdr",
{
storage,
filename: "machine-session-snapshot.json",
gcodeFilename: "fixture.ngc",
createdAt: "2026-06-08T00:00:00.000Z",
metadata: { source: "node-machine-session" },
},
);
assertSessionSnapshotStoragePath(
"machine-session-1",
sessionSnapshotPath("machine-session-1", "machine-session-snapshot.json"),
"machine-session-snapshot.json",
);
assert.deepEqual(machineSessionSnapshot.payload, snapshotPayload);
assert.deepEqual(
await loadSessionSnapshot("machine-session-1", {
storage,
filename: "machine-session-snapshot.json",
}),
machineSessionSnapshot,
);
await saveMachineTextFiles("xyzab-tdr", {
ini: "[EMC]\nMACHINE = xyzab-tdr\n",