接续L4-TOOL-DB持久化闭环
结论:真实 Node/native DB_PROGRAM transcript 与 flat-file 已接入 Tool DB OPFS store 证据链,浏览器执行仍保持锁定等待 Python/WASM Worker。
This commit is contained in:
@@ -569,6 +569,10 @@ private module paths:
|
|||||||
LinuxCNC-owned tool DB process port contract. Contract-only mode does not run
|
LinuxCNC-owned tool DB process port contract. Contract-only mode does not run
|
||||||
`DB_PROGRAM`, does not parse `.tbl` as a fallback, and keeps execution and
|
`DB_PROGRAM`, does not parse `.tbl` as a fallback, and keeps execution and
|
||||||
promotion disabled.
|
promotion disabled.
|
||||||
|
- `runToolDbProcessPortPersistenceSession()` for running a supplied tool DB
|
||||||
|
process port, persisting its transcript and DB flat file through the OPFS tool
|
||||||
|
DB store, and returning diagnostics. The helper is browser-safe glue; runtime
|
||||||
|
semantics still come from the supplied port adapter.
|
||||||
- `createLinuxCncToolDbNodeRuntimeAdapter()` in
|
- `createLinuxCncToolDbNodeRuntimeAdapter()` in
|
||||||
`runtime/sdk/src/tool-db-node-runtime-adapter.js` for Node/native verification
|
`runtime/sdk/src/tool-db-node-runtime-adapter.js` for Node/native verification
|
||||||
of the real LinuxCNC `DB_PROGRAM` child process. This adapter is intentionally
|
of the real LinuxCNC `DB_PROGRAM` child process. This adapter is intentionally
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ export {
|
|||||||
createToolDbTransactionPlan,
|
createToolDbTransactionPlan,
|
||||||
validateToolDbTranscript,
|
validateToolDbTranscript,
|
||||||
} from "./tool-db-process-port.js";
|
} from "./tool-db-process-port.js";
|
||||||
|
export {
|
||||||
|
runToolDbProcessPortPersistenceSession,
|
||||||
|
} from "./tool-db-runtime-session.js";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
defaultMachinePaths,
|
defaultMachinePaths,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
import { createWriteStream } from "node:fs";
|
||||||
import { mkdir, rm } from "node:fs/promises";
|
import { mkdir, readFile, rm } from "node:fs/promises";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { spawn } from "node:child_process";
|
import { spawn } from "node:child_process";
|
||||||
@@ -156,5 +156,9 @@ export function createLinuxCncToolDbNodeRuntimeAdapter({
|
|||||||
stdoutFile?.end();
|
stdoutFile?.end();
|
||||||
stderrFile?.end();
|
stderrFile?.end();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async readDbFileText() {
|
||||||
|
return readFile(dbSavefile, "utf8");
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
78
wasm-port/runtime/sdk/src/tool-db-runtime-session.js
Normal file
78
wasm-port/runtime/sdk/src/tool-db-runtime-session.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import {
|
||||||
|
loadToolDbFile,
|
||||||
|
loadToolDbTranscript,
|
||||||
|
saveToolDbFile,
|
||||||
|
saveToolDbTranscript,
|
||||||
|
} from "../../opfs/tool-db-store.js";
|
||||||
|
import {
|
||||||
|
createToolDbProcessDiagnostics,
|
||||||
|
createToolDbTransactionPlan,
|
||||||
|
validateToolDbTranscript,
|
||||||
|
} from "./tool-db-process-port.js";
|
||||||
|
|
||||||
|
function requireString(value, label) {
|
||||||
|
if (typeof value !== "string" || value.length === 0) {
|
||||||
|
throw new Error(`${label} must be a non-empty string.`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runToolDbProcessPortPersistenceSession({
|
||||||
|
port,
|
||||||
|
machineId,
|
||||||
|
runId = "run-1",
|
||||||
|
dbProgramPath = "./db_nonran.py",
|
||||||
|
dbFileText,
|
||||||
|
storage,
|
||||||
|
plan = createToolDbTransactionPlan(),
|
||||||
|
startupToolCount = 10,
|
||||||
|
mutationCount = 3,
|
||||||
|
readDbFileText = null,
|
||||||
|
} = {}) {
|
||||||
|
if (!port?.start || !port?.runTransactionPlan || !port?.exportTranscript || !port?.close) {
|
||||||
|
throw new Error("port must be a ToolDbProcessPort-like object.");
|
||||||
|
}
|
||||||
|
requireString(machineId, "machineId");
|
||||||
|
requireString(runId, "runId");
|
||||||
|
requireString(dbProgramPath, "dbProgramPath");
|
||||||
|
|
||||||
|
const startResult = await port.start();
|
||||||
|
const planResult = await port.runTransactionPlan(plan);
|
||||||
|
const transcript = port.exportTranscript();
|
||||||
|
const validation = validateToolDbTranscript(transcript);
|
||||||
|
const resolvedDbFileText = readDbFileText ? await readDbFileText() : (dbFileText ?? "");
|
||||||
|
await port.close();
|
||||||
|
|
||||||
|
const savedDb = await saveToolDbFile(machineId, resolvedDbFileText, { storage });
|
||||||
|
const savedTranscript = await saveToolDbTranscript(machineId, runId, transcript, { storage });
|
||||||
|
const loadedDb = await loadToolDbFile(machineId, { storage });
|
||||||
|
const loadedTranscript = await loadToolDbTranscript(machineId, runId, { storage });
|
||||||
|
const diagnostics = createToolDbProcessDiagnostics({
|
||||||
|
dbProgramPath,
|
||||||
|
opfsDbPath: savedDb.displayPath,
|
||||||
|
transcript,
|
||||||
|
dbFileText: loadedDb.text,
|
||||||
|
startupToolCount,
|
||||||
|
mutationCount,
|
||||||
|
runtimeMode: startResult.runtimeMode,
|
||||||
|
runtimeExecutionReady: startResult.runtimeExecutionReady === true && planResult.runtimeExecutionReady === true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
apiName: "linuxcnc-tool-db-process-runtime-persistence-session",
|
||||||
|
ready: validation.ready &&
|
||||||
|
diagnostics.runtimeExecutionReady === true &&
|
||||||
|
diagnostics.opfsPersistenceReady === true &&
|
||||||
|
diagnostics.protocolTranscriptReady === true,
|
||||||
|
startResult,
|
||||||
|
planResult,
|
||||||
|
validation,
|
||||||
|
savedDb,
|
||||||
|
savedTranscript,
|
||||||
|
loadedDb,
|
||||||
|
loadedTranscript,
|
||||||
|
diagnostics,
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -93,6 +93,7 @@ import {
|
|||||||
createLinuxCncToolDbProcessPort,
|
createLinuxCncToolDbProcessPort,
|
||||||
createToolDbProcessDiagnostics,
|
createToolDbProcessDiagnostics,
|
||||||
createToolDbTransactionPlan,
|
createToolDbTransactionPlan,
|
||||||
|
runToolDbProcessPortPersistenceSession,
|
||||||
createVirtualHalBridgeActionPlan,
|
createVirtualHalBridgeActionPlan,
|
||||||
createVirtualHalBridgeReadiness,
|
createVirtualHalBridgeReadiness,
|
||||||
createVirtualHalCommandScriptFixtureReport,
|
createVirtualHalCommandScriptFixtureReport,
|
||||||
@@ -524,6 +525,7 @@ const requiredExports = [
|
|||||||
["createLinuxCncToolDbProcessPort", createLinuxCncToolDbProcessPort],
|
["createLinuxCncToolDbProcessPort", createLinuxCncToolDbProcessPort],
|
||||||
["createToolDbProcessDiagnostics", createToolDbProcessDiagnostics],
|
["createToolDbProcessDiagnostics", createToolDbProcessDiagnostics],
|
||||||
["createToolDbTransactionPlan", createToolDbTransactionPlan],
|
["createToolDbTransactionPlan", createToolDbTransactionPlan],
|
||||||
|
["runToolDbProcessPortPersistenceSession", runToolDbProcessPortPersistenceSession],
|
||||||
["validateToolDbTranscript", validateToolDbTranscript],
|
["validateToolDbTranscript", validateToolDbTranscript],
|
||||||
["saveToolDbFile", saveToolDbFile],
|
["saveToolDbFile", saveToolDbFile],
|
||||||
["loadToolDbFile", loadToolDbFile],
|
["loadToolDbFile", loadToolDbFile],
|
||||||
|
|||||||
@@ -4,12 +4,64 @@ import { resolve } from "node:path";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
createLinuxCncToolDbProcessPort,
|
createLinuxCncToolDbProcessPort,
|
||||||
createToolDbProcessDiagnostics,
|
|
||||||
validateToolDbTranscript,
|
validateToolDbTranscript,
|
||||||
} from "../../../runtime/sdk/src/tool-db-process-port.js";
|
} from "../../../runtime/sdk/src/tool-db-process-port.js";
|
||||||
import {
|
import {
|
||||||
createLinuxCncToolDbNodeRuntimeAdapter,
|
createLinuxCncToolDbNodeRuntimeAdapter,
|
||||||
} from "../../../runtime/sdk/src/tool-db-node-runtime-adapter.js";
|
} from "../../../runtime/sdk/src/tool-db-node-runtime-adapter.js";
|
||||||
|
import {
|
||||||
|
runToolDbProcessPortPersistenceSession,
|
||||||
|
} from "../../../runtime/sdk/src/tool-db-runtime-session.js";
|
||||||
|
|
||||||
|
class MockFileHandle {
|
||||||
|
constructor(name) {
|
||||||
|
this.name = name;
|
||||||
|
this.textValue = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async createWritable() {
|
||||||
|
return {
|
||||||
|
write: async (text) => {
|
||||||
|
this.textValue = String(text);
|
||||||
|
},
|
||||||
|
close: async () => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFile() {
|
||||||
|
return {
|
||||||
|
text: async () => this.textValue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockDirectoryHandle {
|
||||||
|
constructor(name = "") {
|
||||||
|
this.name = name;
|
||||||
|
this.dirs = new Map();
|
||||||
|
this.files = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDirectoryHandle(name, options = {}) {
|
||||||
|
if (!this.dirs.has(name)) {
|
||||||
|
if (!options.create) {
|
||||||
|
throw new Error(`missing directory: ${name}`);
|
||||||
|
}
|
||||||
|
this.dirs.set(name, new MockDirectoryHandle(name));
|
||||||
|
}
|
||||||
|
return this.dirs.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFileHandle(name, options = {}) {
|
||||||
|
if (!this.files.has(name)) {
|
||||||
|
if (!options.create) {
|
||||||
|
throw new Error(`missing file: ${name}`);
|
||||||
|
}
|
||||||
|
this.files.set(name, new MockFileHandle(name));
|
||||||
|
}
|
||||||
|
return this.files.get(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const adapter = createLinuxCncToolDbNodeRuntimeAdapter({
|
const adapter = createLinuxCncToolDbNodeRuntimeAdapter({
|
||||||
dbSavefile: resolve("wasm-port/build/native/tool-db-runtime/sdk-node-runtime-adapter-db-nonran"),
|
dbSavefile: resolve("wasm-port/build/native/tool-db-runtime/sdk-node-runtime-adapter-db-nonran"),
|
||||||
@@ -59,22 +111,42 @@ const dbFileText = readFileSync(adapter.dbSavefile, "utf8");
|
|||||||
assert.match(dbFileText, /T11 P111 D0.33 Z0.11/);
|
assert.match(dbFileText, /T11 P111 D0.33 Z0.11/);
|
||||||
assert.match(dbFileText, /T14 P114\b/);
|
assert.match(dbFileText, /T14 P114\b/);
|
||||||
|
|
||||||
const diagnostics = createToolDbProcessDiagnostics({
|
const storageRoot = new MockDirectoryHandle();
|
||||||
dbProgramPath: "./db_nonran.py",
|
const storage = { getDirectory: async () => storageRoot };
|
||||||
opfsDbPath: "/machines/db-demo/tool-db/db_nonran_file",
|
const sessionAdapter = createLinuxCncToolDbNodeRuntimeAdapter({
|
||||||
transcript,
|
dbSavefile: resolve("wasm-port/build/native/tool-db-runtime/sdk-node-runtime-persistence-db-nonran"),
|
||||||
dbFileText,
|
stdoutLog: resolve("wasm-port/build/native/tool-db-runtime/sdk-node-runtime-persistence.stdout.log"),
|
||||||
startupToolCount: 10,
|
stderrLog: resolve("wasm-port/build/native/tool-db-runtime/sdk-node-runtime-persistence.stderr.log"),
|
||||||
mutationCount: 3,
|
|
||||||
runtimeMode: adapter.runtimeMode,
|
|
||||||
runtimeExecutionReady: true,
|
|
||||||
});
|
});
|
||||||
assert.equal(diagnostics.runtimeMode, "node-linuxcnc-db-program");
|
const session = await runToolDbProcessPortPersistenceSession({
|
||||||
assert.equal(diagnostics.runtimeExecutionReady, true);
|
port: createLinuxCncToolDbProcessPort({
|
||||||
assert.equal(diagnostics.protocolTranscriptReady, true);
|
dbProgramPath: "./db_nonran.py",
|
||||||
assert.equal(diagnostics.opfsPersistenceReady, true);
|
sourceFiles: [
|
||||||
assert.equal(diagnostics.tblFallbackSufficient, false);
|
"configs/sim/axis/db_demo/db_nonran.py",
|
||||||
assert.equal(diagnostics.executionEnabled, false);
|
"configs/sim/axis/db_demo/db.py",
|
||||||
assert.equal(diagnostics.promotionAllowed, false);
|
"lib/python/tooldb.py",
|
||||||
|
],
|
||||||
|
runtimeMode: sessionAdapter.runtimeMode,
|
||||||
|
runtimeAdapter: sessionAdapter,
|
||||||
|
}),
|
||||||
|
machineId: "db-demo",
|
||||||
|
runId: "node-runtime-session",
|
||||||
|
dbProgramPath: "./db_nonran.py",
|
||||||
|
readDbFileText: () => sessionAdapter.readDbFileText(),
|
||||||
|
storage,
|
||||||
|
});
|
||||||
|
assert.equal(session.apiName, "linuxcnc-tool-db-process-runtime-persistence-session");
|
||||||
|
assert.equal(session.ready, true);
|
||||||
|
assert.equal(session.diagnostics.runtimeMode, "node-linuxcnc-db-program");
|
||||||
|
assert.equal(session.diagnostics.runtimeExecutionReady, true);
|
||||||
|
assert.equal(session.diagnostics.protocolTranscriptReady, true);
|
||||||
|
assert.equal(session.diagnostics.opfsPersistenceReady, true);
|
||||||
|
assert.equal(session.diagnostics.tblFallbackSufficient, false);
|
||||||
|
assert.equal(session.diagnostics.executionEnabled, false);
|
||||||
|
assert.equal(session.diagnostics.promotionAllowed, false);
|
||||||
|
assert.equal(session.savedDb.displayPath, "/machines/db-demo/tool-db/db_nonran_file");
|
||||||
|
assert.equal(session.savedTranscript.displayPath, "/machines/db-demo/tool-db/transcripts/node-runtime-session/transcript.json");
|
||||||
|
assert.match(session.loadedDb.text, /T11 P111 D0.33 Z0.11/);
|
||||||
|
assert.match(session.loadedTranscript.payload.transcript[0].line, /^v2\.1$/);
|
||||||
|
|
||||||
console.log("tool_db_node_runtime_adapter=ok");
|
console.log("tool_db_node_runtime_adapter=ok");
|
||||||
|
|||||||
Reference in New Issue
Block a user