建立ToolDbProcessPort合约层

结论:已新增ToolDbProcessPort合约、OPFS tool DB flat-file/transcript store和Node/WASM gates,baseline保持28/28/131/0且promotion继续锁定。
This commit is contained in:
2026-06-19 06:36:26 +08:00
parent d9ac3770a7
commit d8e0196c61
13 changed files with 711 additions and 0 deletions

View File

@@ -91,6 +91,14 @@ export {
planIniFileContextStaging,
planSimConfigStaging,
} from "./sim-config-staging.js";
export {
TOOL_DB_PROCESS_PORT_CONTRACT_VERSION,
TOOL_DB_TRANSACTION_PLAN,
createLinuxCncToolDbProcessPort,
createToolDbProcessDiagnostics,
createToolDbTransactionPlan,
validateToolDbTranscript,
} from "./tool-db-process-port.js";
export {
defaultMachinePaths,
@@ -103,6 +111,9 @@ export {
previewCachePath,
sessionSnapshotPath,
splitOpfsPath,
toolDbFilePath,
toolDbRootPath,
toolDbTranscriptPath,
toolTablePath,
} from "../../opfs/path-model.js";
export {
@@ -138,6 +149,13 @@ export {
loadMachineToolTableFromOpfs,
saveMachineToolTableToOpfs,
} from "../../opfs/linuxcnc-tool-table-bridge.js";
export {
createToolDbStorePaths,
loadToolDbFile,
loadToolDbTranscript,
saveToolDbFile,
saveToolDbTranscript,
} from "../../opfs/tool-db-store.js";
export { loadMachineSessionFromOpfs } from "../../opfs/linuxcnc-machine-session-bridge.js";
export { readMachineSessionReadiness } from "../../opfs/machine-session-readiness.js";

View File

@@ -0,0 +1,241 @@
const TOOL_DB_PROTOCOL_VERSION = "v2.1";
export const TOOL_DB_PROCESS_PORT_CONTRACT_VERSION = 1;
export const TOOL_DB_TRANSACTION_PLAN = [
{
phase: "startup_handshake",
direction: "read",
expect: TOOL_DB_PROTOCOL_VERSION,
requiresFini: false,
},
{
phase: "initial_get_all",
direction: "write",
line: "g",
expect: "T10..T19",
requiresFini: true,
},
{
phase: "tool_offset_notify",
direction: "write",
line: "p t11 p111 d0.33 z0.11",
expect: "FINI",
requiresFini: true,
},
{
phase: "spindle_load_notify",
direction: "write",
line: "l t14 p0",
expect: "T14 P0 after query",
requiresFini: true,
},
{
phase: "spindle_unload_notify",
direction: "write",
line: "u t0 p0",
expect: "T14 P114 after query",
requiresFini: true,
},
];
function requireString(value, label) {
if (typeof value !== "string" || value.length === 0) {
throw new Error(`${label} must be a non-empty string.`);
}
return value;
}
function normalizeSourceFiles(sourceFiles = []) {
if (!Array.isArray(sourceFiles)) {
throw new Error("sourceFiles must be an array.");
}
return sourceFiles.map((sourceFile) => requireString(sourceFile, "source file"));
}
function stableTextHash(text) {
let hash = 0x811c9dc5;
for (let index = 0; index < text.length; index += 1) {
hash ^= text.charCodeAt(index);
hash = Math.imul(hash, 0x01000193) >>> 0;
}
return hash.toString(16).padStart(8, "0");
}
function transcriptText(transcript) {
return transcript
.map((entry) => `${entry.direction}:${entry.line}`)
.join("\n");
}
export function createToolDbTransactionPlan() {
return TOOL_DB_TRANSACTION_PLAN.map((step) => ({ ...step }));
}
export function createToolDbProcessDiagnostics({
dbProgramPath,
opfsDbPath = "-",
transcript = [],
dbFileText = "",
startupToolCount = 0,
mutationCount = 0,
runtimeMode = "contract-only",
runtimeExecutionReady = false,
} = {}) {
requireString(dbProgramPath, "dbProgramPath");
const transcriptHash = stableTextHash(transcriptText(transcript));
const dbFileHash = stableTextHash(dbFileText);
return {
contractVersion: TOOL_DB_PROCESS_PORT_CONTRACT_VERSION,
dbProgramPath,
opfsDbPath,
runtimeMode,
runtimeExecutionReady: runtimeExecutionReady === true,
protocolTranscriptReady: transcript.length > 0,
opfsPersistenceReady: dbFileText.length > 0,
transcriptHash,
dbFileHash,
startupToolCount,
mutationCount,
tblFallbackSufficient: false,
executionEnabled: false,
promotionAllowed: false,
};
}
export function validateToolDbTranscript(transcript) {
if (!Array.isArray(transcript)) {
throw new Error("transcript must be an array.");
}
const lines = transcript.map((entry) => entry?.line ?? "");
const hasVersion = lines.includes(TOOL_DB_PROTOCOL_VERSION);
const hasGetAll = lines.includes("g");
const hasFini = lines.some((line) => line.startsWith("FINI"));
const hasPut = lines.includes("p t11 p111 d0.33 z0.11");
const hasLoad = lines.includes("l t14 p0");
const hasUnload = lines.includes("u t0 p0");
return {
protocolVersion: TOOL_DB_PROTOCOL_VERSION,
hasVersion,
hasGetAll,
hasFini,
hasPut,
hasLoad,
hasUnload,
ready: hasVersion && hasGetAll && hasFini && hasPut && hasLoad && hasUnload,
};
}
export function createLinuxCncToolDbProcessPort({
dbProgramPath,
sourceFiles = [],
opfsRoot = null,
runtimeMode = "contract-only",
runtimeAdapter = null,
} = {}) {
requireString(dbProgramPath, "dbProgramPath");
const normalizedSourceFiles = normalizeSourceFiles(sourceFiles);
const transcript = [];
let started = false;
let closed = false;
function assertOpen() {
if (closed) {
throw new Error("ToolDbProcessPort is closed.");
}
}
return {
dbProgramPath,
sourceFiles: normalizedSourceFiles,
opfsRoot,
runtimeMode,
async start() {
assertOpen();
started = true;
if (runtimeAdapter?.start) {
await runtimeAdapter.start();
}
return {
runtimeMode,
runtimeExecutionReady: Boolean(runtimeAdapter),
executionEnabled: false,
promotionAllowed: false,
};
},
async writeLine(line) {
assertOpen();
if (!started) {
throw new Error("ToolDbProcessPort must be started before writeLine().");
}
const normalizedLine = requireString(line, "line");
transcript.push({ direction: "write", line: normalizedLine });
if (runtimeAdapter?.writeLine) {
await runtimeAdapter.writeLine(normalizedLine);
}
},
async readLine() {
assertOpen();
if (!started) {
throw new Error("ToolDbProcessPort must be started before readLine().");
}
if (!runtimeAdapter?.readLine) {
throw new Error("ToolDbProcessPort has no runtime adapter; contract-only mode cannot read DB_PROGRAM output.");
}
const line = await runtimeAdapter.readLine();
transcript.push({ direction: "read", line });
return line;
},
async runTransactionPlan(plan = createToolDbTransactionPlan()) {
assertOpen();
if (!runtimeAdapter) {
return {
status: "blocked_runtime_adapter_required",
plan,
runtimeExecutionReady: false,
executionEnabled: false,
promotionAllowed: false,
tblFallbackSufficient: false,
};
}
for (const step of plan) {
if (step.direction === "write") {
await this.writeLine(step.line);
} else {
await this.readLine();
}
}
return {
status: "runtime_adapter_transaction_plan_executed",
plan,
transcript: this.exportTranscript(),
runtimeExecutionReady: true,
executionEnabled: false,
promotionAllowed: false,
tblFallbackSufficient: false,
};
},
exportTranscript() {
return transcript.map((entry) => ({ ...entry }));
},
close() {
if (runtimeAdapter?.close) {
runtimeAdapter.close();
}
closed = true;
return {
closed,
executionEnabled: false,
promotionAllowed: false,
};
},
};
}