Implement AXIS parity UI and execution baseline
This commit is contained in:
85
web/worker/test_full_simulation_s4.mjs
Normal file
85
web/worker/test_full_simulation_s4.mjs
Normal file
@@ -0,0 +1,85 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import createModule from "../../build-wasm/linuxcnc-sim-core.js";
|
||||
import { SimSession, CONTEXT_ID } from "./sim-session.mjs";
|
||||
import { fnv1a64 } from "./replay.mjs";
|
||||
|
||||
const wasmBinary = await readFile(new URL("../../build-wasm/linuxcnc-sim-core.wasm", import.meta.url));
|
||||
|
||||
async function createSession(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, operation_id, payload = {}) => {
|
||||
seq += 1;
|
||||
const response = session.accept({ ...base, kind, seq, operation_id, payload });
|
||||
assert.notEqual(response.kind, "ERROR", `${id}:${kind}:${response.payload?.code ?? "error"}`);
|
||||
return response;
|
||||
};
|
||||
send("HELLO", "hello");
|
||||
send("SESSION_ATTACH", "attach");
|
||||
send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace" });
|
||||
send("HAL_COMMAND", "hal-thread", { action: "thread", name: "servo-thread", period_ticks: 1 });
|
||||
send("HAL_COMMAND", "hal-component", { action: "component", instance: "gate", type: "and2", thread: "servo-thread" });
|
||||
send("HAL_SET", "hal-in0", { target: "pin", name: "gate.in0", value: 1 });
|
||||
send("HAL_SET", "hal-in1", { target: "pin", name: "gate.in1", value: 1 });
|
||||
send("PLC_CONFIG", "plc-config", { rungs: [{ op: "SET", target: "ready", value: 1 }, { op: "ADD", target: "count", source: "ready" }], sample_capacity: 4, backpressure: "drop_newest" });
|
||||
send("ENCODER_SET", "encoder", { channel: 0, speed_rps: 1, ppr: 10, scale: 4, index_enable: true });
|
||||
send("COOLANT_COMMAND", "coolant", { mist: true });
|
||||
send("PYTHON_IMPORT", "python-import", { module: "workspace" });
|
||||
send("PYTHON_CALL", "python-write", { module: "workspace", symbol: "write", args: ["/workspace/full.ngc", "G0 X1\n"] });
|
||||
send("REMAP_REGISTER", "remap", { name: "M6", prolog: "m6_prolog", epilog: "m6_epilog" });
|
||||
send("VCP_REGISTER", "vcp", { name: "panel", pins: [{ name: "enabled", type: "bit" }] });
|
||||
send("PROGRAM_LOAD", "program-load", { source: "G0 X1\nG1 X2\n", path: "full.ngc" });
|
||||
return { session, send };
|
||||
}
|
||||
|
||||
function endpoint(session) {
|
||||
return {
|
||||
tick: session.tick,
|
||||
task: { state: session.task.state.state, mode: session.task.state.mode, program_status: session.task.state.program_status, current_line: session.task.state.current_line },
|
||||
motion: session.motion.hash(),
|
||||
plc: session.plc.hash(),
|
||||
hal: session.hal.hash(),
|
||||
device_io: session.deviceIo.hash(),
|
||||
python: session.python.hash(),
|
||||
callbacks: session.callbacks.hash(),
|
||||
};
|
||||
}
|
||||
|
||||
async function runMode(mode) {
|
||||
const { session, send } = await createSession(`full-${mode}`);
|
||||
send("PROGRAM_RUN", "program-run");
|
||||
if (mode === "1x") for (let index = 0; index < 4; index += 1) send("SIM_STEP", `step-${index}`, { cycles: 1 });
|
||||
else send("SIM_STEP", "step-fast", { cycles: 4 });
|
||||
return { session, endpoint: endpoint(session), endpoint_hash: fnv1a64(endpoint(session)) };
|
||||
}
|
||||
|
||||
const oneX = await runMode("1x");
|
||||
const fast = await runMode("fast");
|
||||
assert.deepEqual(oneX.endpoint, fast.endpoint);
|
||||
assert.equal(oneX.endpoint_hash, fast.endpoint_hash);
|
||||
|
||||
const single = await createSession("full-single");
|
||||
const singleResult = single.send("PROGRAM_STEP", "single-step");
|
||||
assert.equal(singleResult.payload.status, "verified");
|
||||
const singleEndpoint = endpoint(single.session);
|
||||
|
||||
const recoverSource = await createSession("full-recover");
|
||||
recoverSource.send("PROGRAM_RUN", "recover-run");
|
||||
recoverSource.send("SIM_STEP", "recover-step-1", { cycles: 1 });
|
||||
const checkpoint = recoverSource.send("CHECKPOINT", "recover-checkpoint").payload.checkpoint;
|
||||
const recovery = recoverSource.session.restoreCheckpoint(checkpoint, recoverSource.session.timeline.export(), (operation) => operation);
|
||||
assert.equal(recovery.status, "recovered");
|
||||
assert.equal(recoverSource.session.tick, checkpoint.simulation_tick);
|
||||
const recoveredEndpoint = endpoint(recoverSource.session);
|
||||
const report = {
|
||||
schema: "full-simulation-s4-sample.v1",
|
||||
status: "verified",
|
||||
context_id: CONTEXT_ID,
|
||||
modes: { one_x: oneX.endpoint, fast: fast.endpoint, single_step: singleEndpoint, recover: recoveredEndpoint },
|
||||
parity: { one_x_fast_equal: oneX.endpoint_hash === fast.endpoint_hash, recovery_status: recovery.status, recovery_tick: recoverSource.session.tick === checkpoint.simulation_tick },
|
||||
hashes: { one_x: oneX.endpoint_hash, fast: fast.endpoint_hash, single_step: fnv1a64(singleEndpoint), recover: fnv1a64(recoveredEndpoint) },
|
||||
};
|
||||
console.log(JSON.stringify(report));
|
||||
Reference in New Issue
Block a user