diff --git a/text10.txt b/text10.txt index f6ae877..70f9523 100644 --- a/text10.txt +++ b/text10.txt @@ -546,3 +546,68 @@ host_wasm_opfs_browser_smokes=ok `Restore And Load Session`:先校验 snapshot,再把 OPFS INI/parameter/tool-table 加载到 interpreter WASM FS,并保持 G-code path 可运行。范围仍限定 OPFS/session/UI 编排,不 解析或改写任何 CNC 语义。 + +六、2026-06-14 继续执行记录:INI panel restore-and-load session workflow + +本轮继续推进 browser/UI workflow 的实质能力,把 snapshot restore 和 machine-session +load 串成一个可直接执行的入口。 + +完成内容: + +- 在 `wasm-port/runtime/ui/ini-panel/app.js` 抽出 + `restoreSessionSnapshotToEditor()`,统一执行 checked snapshot load、OPFS machine + file restore、编辑器回填和 snapshot 文件清单字段回填; +- 抽出 `loadMachineSessionIntoWasm()`,统一通过既有 + `loadMachineSessionFromOpfs(...)` 把 OPFS INI、parameter、tool-table 加载到 + interpreter WASM FS; +- 保留既有 `Restore Session Snapshot` 与 `Load Machine Session` 按钮行为,并让它们 + 复用上述 workflow helper; +- 新增 `Restore And Load Session` 控件: + - 先通过 `loadMachineSessionSnapshot(...)` 校验 snapshot; + - 再恢复 OPFS machine files 到编辑器; + - 再把 INI、parameter、tool-table 加载到 interpreter WASM FS; + - 保留 snapshot G-code path,供后续 `Run G-code` 使用; +- 在 browser INI panel smoke 中改为通过 `Restore And Load Session` 完成 session + 恢复加载,再继续执行 G-code run,验证 snapshot path、OPFS file mapping、 + LinuxCNC parameter/tool-table load result 和 canonical-event flow; +- 未改变 LinuxCNC INI parser、interpreter、parameter restore/save、tool-table load/save + 或 G-code 执行语义; +- 未改变 OPFS path model、generic snapshot envelope、file-service、machine-session + bridge 或 LinuxCNC-owned runtime semantics。 + +验证已通过: + +```bash +git diff --check +SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh +wasm-port/tests/opfs/node/verify_file_service.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 +browser_ini_opfs_smoke=ok +opfs_file_service_node_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 +``` + +下一步建议: + +继续沿 UI workflow 做实质推进。优先把 `Run G-code` 的输入从固定 +`GCODE_FILENAME` 收敛为“优先使用已恢复 snapshot 中的 G-code OPFS path,否则回退默认 +G-code”,并在 browser smoke 中验证 restore-and-load 后运行的程序来自 snapshot 文件清单。 +仍只做 OPFS/session/UI 编排,不解析或改写 G-code 语义。 diff --git a/wasm-port/runtime/ui/ini-panel/app.js b/wasm-port/runtime/ui/ini-panel/app.js index b0c033f..5b7883d 100644 --- a/wasm-port/runtime/ui/ini-panel/app.js +++ b/wasm-port/runtime/ui/ini-panel/app.js @@ -310,6 +310,37 @@ function syncEditorToWasmFs() { requireIniSdk().writeTextFile(WASM_FILE, editor.value); } +async function restoreSessionSnapshotToEditor() { + const snapshot = await loadMachineSessionSnapshot( + SESSION_SNAPSHOT_ID, + MACHINE_ID, + { filename: SESSION_SNAPSHOT_FILENAME }, + ); + const files = await loadMachineTextFiles(MACHINE_ID); + editor.value = files.ini; + setField("sessionIni", snapshot.payload.files.ini); + setField("sessionParameters", snapshot.payload.files.parameters); + setField("sessionToolTable", snapshot.payload.files.toolTable); + setField("sessionGcode", snapshot.payload.files.gcode); + setField("sessionSnapshot", `${snapshot.sessionId}/${SESSION_SNAPSHOT_FILENAME}`); + return snapshot; +} + +async function loadMachineSessionIntoWasm() { + loadedSession = await loadMachineSessionFromOpfs( + requireInterpSdk(), + MACHINE_ID, + { + ...SESSION_WASM_FILES, + iniSdk: requireIniSdk(), + }, + ); + setField("sessionIni", loadedSession.ini.wasmPath); + setField("sessionParameters", loadedSession.parameters.wasmPath); + setField("sessionToolTable", loadedSession.toolTable.wasmPath); + return loadedSession; +} + async function loadSample() { setLog(`Fetching ${SAMPLE_PATH}`); const response = await fetch(SAMPLE_PATH); @@ -463,18 +494,7 @@ document.getElementById("save-session-snapshot").addEventListener("click", async document.getElementById("restore-session-snapshot").addEventListener("click", async () => { try { - const snapshot = await loadMachineSessionSnapshot( - SESSION_SNAPSHOT_ID, - MACHINE_ID, - { filename: SESSION_SNAPSHOT_FILENAME }, - ); - const files = await loadMachineTextFiles(MACHINE_ID); - editor.value = files.ini; - setField("sessionIni", snapshot.payload.files.ini); - setField("sessionParameters", snapshot.payload.files.parameters); - setField("sessionToolTable", snapshot.payload.files.toolTable); - setField("sessionGcode", snapshot.payload.files.gcode); - setField("sessionSnapshot", `${snapshot.sessionId}/${SESSION_SNAPSHOT_FILENAME}`); + const snapshot = await restoreSessionSnapshotToEditor(); setLog( [ "Restored machine session snapshot from OPFS.", @@ -492,17 +512,7 @@ document.getElementById("restore-session-snapshot").addEventListener("click", as document.getElementById("load-session").addEventListener("click", async () => { try { - loadedSession = await loadMachineSessionFromOpfs( - requireInterpSdk(), - MACHINE_ID, - { - ...SESSION_WASM_FILES, - iniSdk: requireIniSdk(), - }, - ); - setField("sessionIni", loadedSession.ini.wasmPath); - setField("sessionParameters", loadedSession.parameters.wasmPath); - setField("sessionToolTable", loadedSession.toolTable.wasmPath); + await loadMachineSessionIntoWasm(); setLog( [ "Loaded machine session into the interpreter WASM filesystem.", @@ -518,6 +528,28 @@ document.getElementById("load-session").addEventListener("click", async () => { } }); +document.getElementById("restore-load-session").addEventListener("click", async () => { + try { + const snapshot = await restoreSessionSnapshotToEditor(); + await loadMachineSessionIntoWasm(); + setField("sessionGcode", snapshot.payload.files.gcode); + setLog( + [ + "Restored and loaded machine session snapshot.", + `Snapshot: ${snapshot.sessionId}/${SESSION_SNAPSHOT_FILENAME}`, + `INI: ${loadedSession.ini.opfsPath} -> ${loadedSession.ini.wasmPath}`, + `Parameter file: ${loadedSession.parameters.opfsPath} -> ${loadedSession.parameters.wasmPath}`, + `Parameters: ${loadedSession.parameters.result.trim()}`, + `Tool table file: ${loadedSession.toolTable.opfsPath} -> ${loadedSession.toolTable.wasmPath}`, + `Tool table: ${loadedSession.toolTable.result.trim()}`, + `G-code: ${snapshot.payload.files.gcode ?? "-"}`, + ].join("\n"), + ); + } catch (error) { + setLog(`Machine session snapshot restore/load failed: ${error.message}`, true); + } +}); + document.getElementById("run-gcode").addEventListener("click", async () => { try { if (!loadedSession) { diff --git a/wasm-port/runtime/ui/ini-panel/index.html b/wasm-port/runtime/ui/ini-panel/index.html index 3db7f97..4bd5441 100644 --- a/wasm-port/runtime/ui/ini-panel/index.html +++ b/wasm-port/runtime/ui/ini-panel/index.html @@ -230,6 +230,7 @@ + diff --git a/wasm-port/tests/browser/ini_panel_smoke.html b/wasm-port/tests/browser/ini_panel_smoke.html index a0a8da7..b2d7234 100644 --- a/wasm-port/tests/browser/ini_panel_smoke.html +++ b/wasm-port/tests/browser/ini_panel_smoke.html @@ -413,10 +413,10 @@ TOOL_TABLE = browser-tool.tbl if (!uiDocument.getElementById("ini-editor").value.includes("MACHINE = browser-smoke")) { throw new Error("UI restored session snapshot did not reload INI text"); } - uiDocument.getElementById("load-session").click(); + uiDocument.getElementById("restore-load-session").click(); await waitFor( - () => uiDocument.getElementById("log")?.textContent.includes("Loaded machine session"), - "machine session load", + () => uiDocument.getElementById("log")?.textContent.includes("Restored and loaded machine session"), + "restore and load machine session", ); assertEqual( uiDocument.getElementById("field-session-ini").textContent, @@ -435,9 +435,10 @@ TOOL_TABLE = browser-tool.tbl ); const uiLog = uiDocument.getElementById("log").textContent; if (!uiLog.includes("restore_parameters=0") || !uiLog.includes("tooldata_load=0")) { - throw new Error(`UI session log missing LinuxCNC load result: ${uiLog}`); + throw new Error(`UI restore/load session log missing LinuxCNC load result: ${uiLog}`); } if ( + !uiLog.includes("Snapshot: ui-machine-session/ui-machine-session.json") || !uiLog.includes( "Parameter file: linuxcnc/machines/xyzab-tdr/linuxcnc.var -> /work/session-linuxcnc.var", ) || @@ -445,7 +446,7 @@ TOOL_TABLE = browser-tool.tbl "Tool table file: linuxcnc/machines/xyzab-tdr/tool.tbl -> /work/session-tool.tbl", ) ) { - throw new Error(`UI session log missing OPFS default file mapping: ${uiLog}`); + throw new Error(`UI restore/load session log missing OPFS default file mapping: ${uiLog}`); } uiDocument.getElementById("run-gcode").click(); await waitFor(