Complete 3axis tutorial native and Web/WASM parity tools
This commit is contained in:
83
web/worker/test_wasm_motion_checkpoint.mjs
Normal file
83
web/worker/test_wasm_motion_checkpoint.mjs
Normal file
@@ -0,0 +1,83 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import createModule from "../../build-wasm/linuxcnc-sim-core.js";
|
||||
import { fnv1a64 } from "./replay.mjs";
|
||||
import { loadSimulationCase, toWorkerConfiguration } from "../workspace/sim-case-loader.mjs";
|
||||
import { SimSession, CONTEXT_ID } from "./sim-session.mjs";
|
||||
|
||||
const CASE = "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini";
|
||||
const fetchImpl = async (url) => {
|
||||
try {
|
||||
const body = await readFile(new URL(`../../${String(url).replace(/^\//, "")}`, import.meta.url));
|
||||
return { ok: true, text: async () => body.toString("utf8") };
|
||||
} catch {
|
||||
return { ok: false, text: async () => "" };
|
||||
}
|
||||
};
|
||||
|
||||
const bundle = await loadSimulationCase({ path: CASE, fetchImpl });
|
||||
const wasmBinary = await readFile(new URL("../../build-wasm/linuxcnc-sim-core.wasm", import.meta.url));
|
||||
const source = "G21 G90\nG0 X0 Y0 Z0\nG1 X40 Y20 F120\nG1 Z-2 F60\nG0 X0 Y0 Z0\nM2\n";
|
||||
|
||||
async function create(id) {
|
||||
const core = await createModule({ wasmBinary });
|
||||
const session = new SimSession(core, { sessionId: id });
|
||||
const base = { schema_version: 1, context_id: CONTEXT_ID, session_id: id };
|
||||
let seq = 0;
|
||||
const send = (kind, operationId, payload = {}) => session.accept({ ...base, kind, seq: ++seq, operation_id: operationId, payload });
|
||||
send("HELLO", "hello");
|
||||
send("SESSION_ATTACH", "attach");
|
||||
const loaded = send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) });
|
||||
assert.equal(loaded.payload.motion_authority, "wasm");
|
||||
send("PROGRAM_LOAD", "program", { path: "checkpoint.ngc", source });
|
||||
send("PROGRAM_RUN", "run");
|
||||
return { session, send };
|
||||
}
|
||||
|
||||
const continuous = await create("wasm-motion-checkpoint");
|
||||
const before = continuous.send("SIM_STEP", "before", { cycles: 3 });
|
||||
assert.ok(before.payload.wasm_motion.state.queue_depth > 0);
|
||||
const checkpoint = continuous.send("CHECKPOINT", "checkpoint").payload.checkpoint;
|
||||
assert.equal(checkpoint.state.wasm_motion.schema, "sim_motion_queue_state.v1");
|
||||
assert.equal(checkpoint.state.motion_authority, "wasm");
|
||||
assert.equal(checkpoint.state_hash, fnv1a64(checkpoint.state));
|
||||
|
||||
continuous.send("SIM_STEP", "after", { cycles: 4 });
|
||||
const continuousState = continuous.session.workspacePackageState();
|
||||
|
||||
const recovered = await create("wasm-motion-checkpoint");
|
||||
for (const method of ["syncProgram", "step", "applyMotion"]) {
|
||||
recovered.session.authoritative[method] = () => { throw new Error(`js_motion_authority_called:${method}`); };
|
||||
}
|
||||
const recovery = recovered.session.restoreCheckpoint(checkpoint, continuous.session.timeline.export(), (operation) => operation);
|
||||
assert.equal(recovery.status, "recovered");
|
||||
const recoveredState = recovered.session.workspacePackageState();
|
||||
assert.equal(fnv1a64(continuousState), fnv1a64(recoveredState));
|
||||
assert.equal(recoveredState.wasm_motion.state.queue_depth, continuousState.wasm_motion.state.queue_depth);
|
||||
assert.deepEqual(recoveredState.wasm_motion.state.position, continuousState.wasm_motion.state.position);
|
||||
|
||||
const trace = continuous.session.exportTrace();
|
||||
const traced = await create("wasm-motion-checkpoint");
|
||||
const traceImport = traced.session.importTrace(trace);
|
||||
assert.equal(traceImport.status, "verified");
|
||||
assert.equal(traced.session.workspacePackageState().wasm_motion.state_hash, continuousState.wasm_motion.state_hash);
|
||||
assert.deepEqual(traced.session.workspacePackageState().wasm_motion.state.position, continuousState.wasm_motion.state.position);
|
||||
|
||||
const damaged = structuredClone(checkpoint);
|
||||
damaged.state.wasm_motion.bytes = `${damaged.state.wasm_motion.bytes.startsWith("00") ? "ff" : "00"}${damaged.state.wasm_motion.bytes.slice(2)}`;
|
||||
damaged.state_hash = fnv1a64(damaged.state);
|
||||
const blocked = recovered.session.restoreCheckpoint(damaged, null, (operation) => operation);
|
||||
assert.equal(blocked.status, "blocked");
|
||||
|
||||
console.log(JSON.stringify({
|
||||
schema: "wasm-motion-checkpoint-t3.v1",
|
||||
status: "verified",
|
||||
case: CASE,
|
||||
checkpoint_hash: checkpoint.checkpoint_hash,
|
||||
continuous_hash: fnv1a64(continuousState),
|
||||
recovered_hash: fnv1a64(recoveredState),
|
||||
endpoint_equal: fnv1a64(continuousState) === fnv1a64(recoveredState),
|
||||
trace_import: traceImport.status,
|
||||
checkpoint_queue_depth: checkpoint.state.wasm_motion.state.queue_depth,
|
||||
blocked_damaged_checkpoint: blocked.status,
|
||||
}));
|
||||
Reference in New Issue
Block a user