参考所有分组:完善OPFS参数备份桥接

结论:按 LinuxCNC rs274ngc_pre.cc save_parameters()/restore_parameters() 语义,补齐浏览器 OPFS 参数文件 .bak 持久化;未扩展 smoke 解析行为。

验证:./test-native.sh;./test-linuxcnc-source-link.sh;./test-web-wasm-node-smoke.sh;./test-web-wasm-browser-smoke.sh。
This commit is contained in:
cnc
2026-06-03 10:58:05 +08:00
parent 028825cd9c
commit 6cf7c73392
3 changed files with 86 additions and 27 deletions

View File

@@ -703,10 +703,19 @@ export async function createWasmSimulator(moduleOptions = {}) {
throw new Error("OPFS workspace is not available for parameter file persistence");
}
cleanupLinuxCncParameterFiles(module);
await opfs.loadParameterFile(parameterPath);
const previousParameterFile = await opfs.loadParameterFile(parameterPath);
try {
const events = parseText(program, dialect, options, { keepLinuxCncParameterFiles: true });
await opfs.persistParameterFile(parameterPath);
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
// links the previous parameter file to filename + ".bak" before
// replacing filename. Browser filesystems may not expose that link
// through the WASM mirror, so preserve the loaded OPFS file here.
if (previousParameterFile === null) {
await opfs.removeFile(`${parameterPath}.bak`);
} else if (!(await opfs.exists(`${parameterPath}.bak`))) {
await opfs.writeFile(`${parameterPath}.bak`, previousParameterFile);
}
return events;
} finally {
cleanupLinuxCncParameterFiles(module);

View File

@@ -28,6 +28,20 @@ export async function runBrowserAppOpfsSections(context) {
const savedParameterFile = new TextDecoder().decode(await appSimulator.fs.opfs.readFile("parameters/rs274ngc.var"));
expectIncludes(savedParameterFile, "5161\t1.000000",
"app frame did not persist LinuxCNC parameter state into OPFS");
await appSimulator.fs.opfs.removeFile("parameters/rs274ngc.var.bak");
parseAppFrameProgram(appFrame, "G21 G90\nG0 X2\nG28.1\nM30\n");
await waitFor(
() => appFrame.contentDocument?.querySelector("#axisX")?.value === "2.000",
"app frame did not parse second program for parameter backup coverage",
);
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
// preserves the previous parameter file as filename + ".bak".
const updatedParameterFile = new TextDecoder().decode(await appSimulator.fs.opfs.readFile("parameters/rs274ngc.var"));
expectIncludes(updatedParameterFile, "5161\t2.000000",
"app frame did not replace LinuxCNC parameter state after second parse");
const savedParameterBackup = new TextDecoder().decode(await appSimulator.fs.opfs.readFile("parameters/rs274ngc.var.bak"));
expectIncludes(savedParameterBackup, "5161\t1.000000",
"app frame did not persist LinuxCNC parameter backup into OPFS");
} finally {
await disposeAppFrame(appFrame);
}
@@ -38,7 +52,7 @@ export async function runBrowserAppOpfsSections(context) {
await waitForAppControls(restoredAppFrame, "restored app frame did not load CNC controls");
await waitFor(() => {
const restoredInput = restoredAppFrame.contentDocument?.querySelector("#programInput");
return restoredInput?.value === "G21 G90\nG0 X1\nG28.1\nM30\n";
return restoredInput?.value === "G21 G90\nG0 X2\nG28.1\nM30\n";
}, "app frame did not restore current program from OPFS after reload");
await waitForAppAlarm(
restoredAppFrame,
@@ -48,7 +62,7 @@ export async function runBrowserAppOpfsSections(context) {
await getAppFrameSimulator(restoredAppFrame, "restored app frame did not create OPFS simulator workspace");
parseAppFrameProgram(
restoredAppFrame,
"G21 G90\nF100\nO10 if [#5161 EQ 1]\nG1 X12\nO10 endif\nM30\n",
"G21 G90\nF100\nO10 if [#5161 EQ 2]\nG1 X12\nO10 endif\nM30\n",
);
await waitForAppAlarm(
restoredAppFrame,

View File

@@ -9,6 +9,7 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
expectText,
expectWasmFilesMissing,
near,
readOpfsText,
removeWasmPathIfExists,
runSection,
withSmokeSimulator,
@@ -21,9 +22,7 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
"linuxcnc",
{ backend: "linuxcnc-rs274" },
);
const opfsParameterFile = new TextDecoder().decode(
await simulator.fs.opfs.readFile("parameters/rs274ngc.var"),
);
const opfsParameterFile = await readOpfsText(simulator, "parameters/rs274ngc.var");
expectIncludes(opfsParameterFile, "5161\t1.000000", "LinuxCNC parameter file was not persisted into OPFS");
});
@@ -42,6 +41,20 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
});
});
await runSection("opfs default parameter backup", async () => {
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
// preserves the previous default parameter file as filename + ".bak".
await simulator.parseWithParameterFile(
"G21 G90\nM30\n",
"parameters/rs274ngc.var",
"linuxcnc",
{ backend: "linuxcnc-rs274" },
);
const defaultParameterBackup = await readOpfsText(simulator, "parameters/rs274ngc.var.bak");
expectIncludes(defaultParameterBackup, "5161\t1.000000",
"LinuxCNC default OPFS parameter backup did not preserve the previous file");
});
await runSection("opfs parameter temporary cleanup", async () => {
await simulator.fs.opfs.writeFile("parameters/rs274ngc.var.new", "stale temporary parameter file\n");
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() writes
@@ -73,24 +86,49 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
simulator.fs.module.FS.writeFile("rs274ngc.var", "5161\t2.000000\n");
simulator.fs.module.FS.writeFile("rs274ngc.var.bak", "5161\t1.000000\n");
await simulator.fs.opfs.persistParameterFile("parameters/backup-bridge.var");
const opfsParameterBackup = new TextDecoder().decode(
await simulator.fs.opfs.readFile("parameters/backup-bridge.var.bak"),
);
const opfsParameterBackup = await readOpfsText(simulator, "parameters/backup-bridge.var.bak");
expectIncludes(opfsParameterBackup, "5161\t1.000000",
"LinuxCNC parameter backup file was not bridged into OPFS");
});
await runSection("opfs parameter backup after existing save", async () => {
await simulator.fs.opfs.writeFile("parameters/existing-save.var", "5161\t3.000000\n");
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
// unlinks filename + ".bak", links the current parameter file to
// filename + ".bak", then renames filename + ".new" over filename.
await simulator.parseWithParameterFile(
"G21 G90\nG0 X4\nG28.1\nM30\n",
"parameters/existing-save.var",
"linuxcnc",
{ backend: "linuxcnc-rs274" },
);
const existingSaveParameterFile = await readOpfsText(simulator, "parameters/existing-save.var");
expectIncludes(existingSaveParameterFile, "5161\t4.000000",
"LinuxCNC existing OPFS parameter file was not replaced after save");
const existingSaveParameterBackup = await readOpfsText(simulator, "parameters/existing-save.var.bak");
expectIncludes(existingSaveParameterBackup, "5161\t3.000000",
"LinuxCNC existing OPFS parameter backup did not preserve the previous file");
await expectMissingOpfsFile(
() => simulator.fs.opfs.readFile("parameters/existing-save.var.new"),
"LinuxCNC existing OPFS parameter temporary file remained after save",
);
});
await runSection("opfs parameter stale backup cleanup", async () => {
await simulator.fs.opfs.removeFile("parameters/stale-backup-cleanup.var");
await simulator.fs.opfs.removeFile("parameters/stale-backup-cleanup.var.new");
await simulator.fs.opfs.removeFile("parameters/stale-backup-cleanup.var.bak");
await simulator.fs.opfs.writeFile("parameters/stale-backup-cleanup.var.bak", "stale backup without main file\n");
removeWasmPathIfExists(simulator, "rs274ngc.var.bak");
removeWasmPathIfExists(simulator, "/rs274ngc.var.bak");
await simulator.parseWithParameterFile(
"G21 G90\nM30\n",
"parameters/rs274ngc.var",
"parameters/stale-backup-cleanup.var",
"linuxcnc",
{ backend: "linuxcnc-rs274" },
);
await expectMissingOpfsFile(
() => simulator.fs.opfs.readFile("parameters/rs274ngc.var.bak"),
() => simulator.fs.opfs.readFile("parameters/stale-backup-cleanup.var.bak"),
"stale LinuxCNC parameter backup file remained in OPFS",
);
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters()
@@ -111,6 +149,9 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
});
await runSection("opfs missing parameter file creation", async () => {
await simulator.fs.opfs.removeFile("parameters/missing-main.var");
await simulator.fs.opfs.removeFile("parameters/missing-main.var.new");
await simulator.fs.opfs.removeFile("parameters/missing-main.var.bak");
await simulator.fs.opfs.writeFile("parameters/missing-main.var.bak", "stale backup without main file\n");
await simulator.fs.opfs.writeFile("parameters/missing-main.var.new", "stale temporary without main file\n");
// LinuxCNC source basis: rs274ngc_pre.cc save_parameters() unlinks
@@ -124,13 +165,15 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
"linuxcnc",
{ backend: "linuxcnc-rs274" },
);
const missingMainParameterFile = new TextDecoder().decode(
await simulator.fs.opfs.readFile("parameters/missing-main.var"),
);
const missingMainParameterFile = await readOpfsText(simulator, "parameters/missing-main.var");
expectIncludes(missingMainParameterFile, "5220\t1.000000",
"LinuxCNC missing parameter file was not created through OPFS");
expectIncludes(missingMainParameterFile, "5161\t0.000000",
"LinuxCNC missing parameter file did not include required G28 parameter");
await expectMissingOpfsFile(
() => simulator.fs.opfs.readFile("parameters/missing-main.var.bak"),
"stale LinuxCNC missing-parameter backup remained in OPFS after first creation",
);
const missingMainRestoreEvents = await simulator.parseWithParameterFile(
"G21 G90\nF100\nO10 if [#5220 EQ 1]\nG1 X6\nO10 endif\n",
"parameters/missing-main.var",
@@ -139,10 +182,9 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
);
expectEvent(missingMainRestoreEvents, (event) => event.type === "linear-feed" && near(event.end.x, 6),
"LinuxCNC-created missing OPFS parameter file was not restored on the next parse");
await expectMissingOpfsFile(
() => simulator.fs.opfs.readFile("parameters/missing-main.var.bak"),
"stale LinuxCNC missing-parameter backup remained in OPFS",
);
const missingMainParameterBackup = await readOpfsText(simulator, "parameters/missing-main.var.bak");
expectIncludes(missingMainParameterBackup, "5220\t1.000000",
"LinuxCNC-created OPFS parameter file was not backed up on the next successful save");
expectMissingWasmPath(
() => simulator.fs.module.FS.readFile("rs274ngc.var.bak"),
"stale LinuxCNC missing-parameter backup remained in WASM FS",
@@ -169,9 +211,7 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
"Parameter file out of order",
"LinuxCNC accepted an out-of-order OPFS parameter file",
);
const outOfOrderParameterFile = new TextDecoder().decode(
await simulator.fs.opfs.readFile("parameters/out-of-order.var"),
);
const outOfOrderParameterFile = await readOpfsText(simulator, "parameters/out-of-order.var");
expectText(outOfOrderParameterFile, "5162\t1.000000\n5161\t1.000000\n",
"LinuxCNC persisted an out-of-order OPFS parameter file after restore failure");
expectMissingWasmPath(
@@ -189,9 +229,7 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
"Parameter number out of range",
"LinuxCNC accepted an out-of-range OPFS parameter file",
);
const outOfRangeParameterFile = new TextDecoder().decode(
await simulator.fs.opfs.readFile("parameters/out-of-range.var"),
);
const outOfRangeParameterFile = await readOpfsText(simulator, "parameters/out-of-range.var");
expectText(outOfRangeParameterFile, "0\t1.000000\n",
"LinuxCNC persisted an out-of-range OPFS parameter file after restore failure");
expectMissingWasmPath(
@@ -209,9 +247,7 @@ export async function runBrowserOpfsParameterSections(context, simulator) {
"",
"LinuxCNC accepted a failing parse that should not persist parameters",
);
const failedParseParameterFile = new TextDecoder().decode(
await simulator.fs.opfs.readFile("parameters/failed-parse.var"),
);
const failedParseParameterFile = await readOpfsText(simulator, "parameters/failed-parse.var");
if (failedParseParameterFile.includes("5161\t1.000000")) {
throw new Error("LinuxCNC persisted failed parse parameter updates into OPFS");
}