接续L4-TOOL-DB浏览器Provider协议
结论:浏览器 Worker 已支持可插拔 Python runtime provider 的 line I/O,缺真实 Python/WASM runtime 时仍保持阻塞不解锁。
This commit is contained in:
@@ -577,7 +577,9 @@ private module paths:
|
|||||||
boundary. The bundled worker reports
|
boundary. The bundled worker reports
|
||||||
`blocked_browser_python_wasm_runtime_missing` until a real Python/WASM runtime
|
`blocked_browser_python_wasm_runtime_missing` until a real Python/WASM runtime
|
||||||
is installed; the Worker shell alone must not mark `DB_PROGRAM` execution
|
is installed; the Worker shell alone must not mark `DB_PROGRAM` execution
|
||||||
ready.
|
ready. The Worker supports a provider protocol (`start`, `writeLine`,
|
||||||
|
`readLine`, `close`) so a future Python/WASM runtime can provide line-based
|
||||||
|
`DB_PROGRAM` I/O without changing `ToolDbProcessPort`.
|
||||||
- `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
|
||||||
|
|||||||
@@ -96,7 +96,14 @@ export function createLinuxCncToolDbBrowserWorkerAdapter({
|
|||||||
return result.line;
|
return result.line;
|
||||||
},
|
},
|
||||||
|
|
||||||
close() {
|
async close() {
|
||||||
|
if (worker) {
|
||||||
|
try {
|
||||||
|
await request("close");
|
||||||
|
} catch {
|
||||||
|
// The worker may be blocked or already gone; termination below is authoritative.
|
||||||
|
}
|
||||||
|
}
|
||||||
rejectPending(new Error("Tool DB browser worker closed."));
|
rejectPending(new Error("Tool DB browser worker closed."));
|
||||||
worker?.terminate();
|
worker?.terminate();
|
||||||
worker = null;
|
worker = null;
|
||||||
|
|||||||
@@ -15,12 +15,21 @@ function pythonRuntimeAvailable() {
|
|||||||
return typeof self.loadPyodide === "function" || Boolean(self.pyodide?.runPythonAsync);
|
return typeof self.loadPyodide === "function" || Boolean(self.pyodide?.runPythonAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRuntimeProvider() {
|
||||||
|
return self.linuxCncToolDbPythonRuntimeProvider ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
self.addEventListener("message", async (event) => {
|
self.addEventListener("message", async (event) => {
|
||||||
const message = event.data ?? {};
|
const message = event.data ?? {};
|
||||||
const { id, type } = message;
|
const { id, type, line } = message;
|
||||||
try {
|
try {
|
||||||
if (type === "start") {
|
if (type === "start") {
|
||||||
const ready = pythonRuntimeAvailable();
|
const provider = getRuntimeProvider();
|
||||||
|
const providerReady = Boolean(provider?.start && provider?.writeLine && provider?.readLine);
|
||||||
|
const ready = providerReady || pythonRuntimeAvailable();
|
||||||
|
if (providerReady) {
|
||||||
|
await provider.start(message);
|
||||||
|
}
|
||||||
postReply(id, type, {
|
postReply(id, type, {
|
||||||
runtimeMode: "browser-python-wasm-worker",
|
runtimeMode: "browser-python-wasm-worker",
|
||||||
runtimeExecutionReady: ready,
|
runtimeExecutionReady: ready,
|
||||||
@@ -32,6 +41,25 @@ self.addEventListener("message", async (event) => {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const provider = getRuntimeProvider();
|
||||||
|
if (type === "writeLine" && provider?.writeLine) {
|
||||||
|
await provider.writeLine(line);
|
||||||
|
postReply(id, type, { executionEnabled: false, promotionAllowed: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === "readLine" && provider?.readLine) {
|
||||||
|
postReply(id, type, {
|
||||||
|
line: await provider.readLine(),
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (type === "close" && provider?.close) {
|
||||||
|
await provider.close();
|
||||||
|
postReply(id, type, { executionEnabled: false, promotionAllowed: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
throw new Error("blocked_browser_python_wasm_runtime_missing");
|
throw new Error("blocked_browser_python_wasm_runtime_missing");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
postError(id, type, error);
|
postError(id, type, error);
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
self.linuxCncToolDbPythonRuntimeProvider = {
|
||||||
|
lines: [
|
||||||
|
"v2.1",
|
||||||
|
"T10 P110 D0.10 Z0.10",
|
||||||
|
"FINI (get_cmd)",
|
||||||
|
"FINI (update recvd) t11 p111 d0.33 z0.11",
|
||||||
|
"FINI (update recvd) t14 p0",
|
||||||
|
"FINI (update recvd) t0 p0",
|
||||||
|
],
|
||||||
|
|
||||||
|
async start() {},
|
||||||
|
|
||||||
|
async writeLine() {},
|
||||||
|
|
||||||
|
async readLine() {
|
||||||
|
return this.lines.shift();
|
||||||
|
},
|
||||||
|
|
||||||
|
async close() {},
|
||||||
|
};
|
||||||
|
|
||||||
|
await import("../../runtime/workers/tool-db-python-worker.js");
|
||||||
@@ -85,6 +85,23 @@
|
|||||||
assertEqual(workerPlanResult.promotionAllowed, false, "worker promotion guard");
|
assertEqual(workerPlanResult.promotionAllowed, false, "worker promotion guard");
|
||||||
await workerPort.close();
|
await workerPort.close();
|
||||||
|
|
||||||
|
const fakeRuntimeAdapter = createLinuxCncToolDbBrowserWorkerAdapter({
|
||||||
|
workerUrl: new URL("./tool_db_fake_python_runtime_worker.js", import.meta.url),
|
||||||
|
});
|
||||||
|
const fakeRuntimePort = createLinuxCncToolDbProcessPort({
|
||||||
|
dbProgramPath: "./db_nonran.py",
|
||||||
|
runtimeMode: fakeRuntimeAdapter.runtimeMode,
|
||||||
|
runtimeAdapter: fakeRuntimeAdapter,
|
||||||
|
});
|
||||||
|
const fakeRuntimeStart = await fakeRuntimePort.start();
|
||||||
|
assertEqual(fakeRuntimeStart.runtimeExecutionReady, true, "fake provider runtime readiness");
|
||||||
|
assertEqual(fakeRuntimeStart.status, "browser_python_wasm_runtime_ready", "fake provider runtime status");
|
||||||
|
const fakeRuntimePlan = await fakeRuntimePort.runTransactionPlan();
|
||||||
|
assertEqual(fakeRuntimePlan.status, "runtime_adapter_transaction_plan_executed", "fake provider plan");
|
||||||
|
assertEqual(fakeRuntimePlan.promotionAllowed, false, "fake provider promotion guard");
|
||||||
|
assertEqual(validateToolDbTranscript(fakeRuntimePort.exportTranscript()).ready, true, "fake provider transcript");
|
||||||
|
await fakeRuntimePort.close();
|
||||||
|
|
||||||
const transcript = [
|
const transcript = [
|
||||||
{ direction: "read", line: "v2.1" },
|
{ direction: "read", line: "v2.1" },
|
||||||
{ direction: "write", line: "g" },
|
{ direction: "write", line: "g" },
|
||||||
|
|||||||
@@ -80,6 +80,66 @@ class FakeBlockedWorker {
|
|||||||
terminate() {}
|
terminate() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeReadyWorker {
|
||||||
|
constructor() {
|
||||||
|
this.listeners = new Map();
|
||||||
|
this.lines = [
|
||||||
|
"v2.1",
|
||||||
|
"T10 P110 D0.10 Z0.10",
|
||||||
|
"FINI (get_cmd)",
|
||||||
|
"FINI (update recvd) t11 p111 d0.33 z0.11",
|
||||||
|
"FINI (update recvd) t14 p0",
|
||||||
|
"FINI (update recvd) t0 p0",
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener(type, listener) {
|
||||||
|
this.listeners.set(type, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
postMessage(message) {
|
||||||
|
queueMicrotask(() => {
|
||||||
|
if (message.type === "start") {
|
||||||
|
this.reply(message, {
|
||||||
|
runtimeExecutionReady: true,
|
||||||
|
status: "browser_python_wasm_runtime_ready",
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.type === "writeLine") {
|
||||||
|
this.reply(message, { executionEnabled: false, promotionAllowed: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.type === "readLine") {
|
||||||
|
this.reply(message, {
|
||||||
|
line: this.lines.shift(),
|
||||||
|
executionEnabled: false,
|
||||||
|
promotionAllowed: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.type === "close") {
|
||||||
|
this.reply(message, { executionEnabled: false, promotionAllowed: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reply(message, payload) {
|
||||||
|
this.listeners.get("message")?.({
|
||||||
|
data: {
|
||||||
|
id: message.id,
|
||||||
|
type: message.type,
|
||||||
|
ok: true,
|
||||||
|
...payload,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate() {}
|
||||||
|
}
|
||||||
|
|
||||||
const browserWorkerAdapter = createLinuxCncToolDbBrowserWorkerAdapter({
|
const browserWorkerAdapter = createLinuxCncToolDbBrowserWorkerAdapter({
|
||||||
workerUrl: "./tool-db-python-worker.js",
|
workerUrl: "./tool-db-python-worker.js",
|
||||||
workerFactory: () => new FakeBlockedWorker(),
|
workerFactory: () => new FakeBlockedWorker(),
|
||||||
@@ -99,6 +159,25 @@ assert.equal(browserWorkerBlockedPlan.runtimeExecutionReady, false);
|
|||||||
assert.equal(browserWorkerBlockedPlan.promotionAllowed, false);
|
assert.equal(browserWorkerBlockedPlan.promotionAllowed, false);
|
||||||
await browserWorkerPort.close();
|
await browserWorkerPort.close();
|
||||||
|
|
||||||
|
const readyWorkerAdapter = createLinuxCncToolDbBrowserWorkerAdapter({
|
||||||
|
workerUrl: "./tool-db-python-worker.js",
|
||||||
|
workerFactory: () => new FakeReadyWorker(),
|
||||||
|
});
|
||||||
|
const readyWorkerPort = createLinuxCncToolDbProcessPort({
|
||||||
|
dbProgramPath: "./db_nonran.py",
|
||||||
|
runtimeMode: readyWorkerAdapter.runtimeMode,
|
||||||
|
runtimeAdapter: readyWorkerAdapter,
|
||||||
|
});
|
||||||
|
const readyWorkerStart = await readyWorkerPort.start();
|
||||||
|
assert.equal(readyWorkerStart.runtimeExecutionReady, true);
|
||||||
|
assert.equal(readyWorkerStart.status, "browser_python_wasm_runtime_ready");
|
||||||
|
const readyWorkerPlan = await readyWorkerPort.runTransactionPlan();
|
||||||
|
assert.equal(readyWorkerPlan.status, "runtime_adapter_transaction_plan_executed");
|
||||||
|
assert.equal(readyWorkerPlan.runtimeExecutionReady, true);
|
||||||
|
assert.equal(readyWorkerPlan.promotionAllowed, false);
|
||||||
|
assert.equal(validateToolDbTranscript(readyWorkerPort.exportTranscript()).ready, true);
|
||||||
|
await readyWorkerPort.close();
|
||||||
|
|
||||||
const transcript = [
|
const transcript = [
|
||||||
{ direction: "read", line: "v2.1" },
|
{ direction: "read", line: "v2.1" },
|
||||||
{ direction: "write", line: "g" },
|
{ direction: "write", line: "g" },
|
||||||
|
|||||||
Reference in New Issue
Block a user