Files
linuxcncWebWASM/web/worker/test_wasm_motion_checkpoint.mjs

84 lines
4.3 KiB
JavaScript

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,
}));