Implement AXIS parity UI and execution baseline
This commit is contained in:
72
web/worker/test_sim_task.mjs
Normal file
72
web/worker/test_sim_task.mjs
Normal file
@@ -0,0 +1,72 @@
|
||||
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));
|
||||
|
||||
function base(sessionId) {
|
||||
return { schema_version: 1, context_id: CONTEXT_ID, session_id: sessionId };
|
||||
}
|
||||
|
||||
async function createSession(sessionId) {
|
||||
const core = await createModule({ wasmBinary });
|
||||
const session = new SimSession(core, { sessionId });
|
||||
const prefix = base(sessionId);
|
||||
assert.equal(session.accept({ ...prefix, kind: "HELLO", seq: 1, payload: {} }).kind, "CAPABILITIES");
|
||||
const ready = session.accept({ ...prefix, kind: "SESSION_ATTACH", seq: 2, operation_id: `${sessionId}-attach`, payload: {} });
|
||||
assert.equal(ready.kind, "SESSION_READY");
|
||||
assert.equal(ready.payload.task.schema, "sim_task.v1");
|
||||
assert.equal(ready.payload.task.state, "READY");
|
||||
assert.equal(ready.payload.task.seq, 2);
|
||||
return { core, session, prefix };
|
||||
}
|
||||
|
||||
const first = await createSession("task-a");
|
||||
const { session, prefix } = first;
|
||||
let response = session.accept({ ...prefix, kind: "LOAD_WORKSPACE", seq: 3, operation_id: "load", payload: { root_path: "/workspace" } });
|
||||
assert.equal(response.payload.task.state, "READY");
|
||||
response = session.accept({ ...prefix, kind: "PROGRAM_LOAD", seq: 4, operation_id: "program-load", payload: { path: "task.ngc", source: "G21 G90\nG1 X2 F60\nM2\n" } });
|
||||
assert.equal(response.payload.task.program_status, "loaded");
|
||||
response = session.accept({ ...prefix, kind: "PROGRAM_RUN", seq: 5, operation_id: "program-run", payload: {} });
|
||||
assert.equal(response.payload.task.state, "RUNNING");
|
||||
response = session.accept({ ...prefix, kind: "SIM_STEP", seq: 6, operation_id: "tick-1", payload: { cycles: 1 } });
|
||||
assert.equal(response.payload.task.simulation_tick, 1);
|
||||
const firstTickHash = response.payload.task.state_hash;
|
||||
response = session.accept({ ...prefix, kind: "PROGRAM_PAUSE", seq: 7, operation_id: "pause", payload: {} });
|
||||
assert.equal(response.payload.task.state, "PAUSED");
|
||||
response = session.accept({ ...prefix, kind: "PROGRAM_RESUME", seq: 8, operation_id: "resume", payload: {} });
|
||||
assert.equal(response.payload.task.state, "RUNNING");
|
||||
response = session.accept({ ...prefix, kind: "PROGRAM_ABORT", seq: 9, operation_id: "abort", payload: {} });
|
||||
assert.equal(response.payload.task.state, "STOPPED");
|
||||
assert.equal(response.payload.task.program_status, "aborted");
|
||||
|
||||
const checkpointResponse = session.accept({ ...prefix, kind: "CHECKPOINT", seq: 10, operation_id: "checkpoint", payload: {} });
|
||||
assert.equal(checkpointResponse.kind, "CHECKPOINT_READY");
|
||||
assert.equal(checkpointResponse.payload.checkpoint.state.task.schema, "sim_task.v1");
|
||||
const checkpoint = checkpointResponse.payload.checkpoint;
|
||||
const recovered = session.restoreCheckpoint(checkpoint, null, (operation) => operation);
|
||||
assert.equal(recovered.status, "recovered");
|
||||
assert.equal(recovered.state.task.state, "STOPPED");
|
||||
assert.equal(recovered.state.task.state_hash, checkpoint.state.task.state_hash);
|
||||
|
||||
const faultSession = await createSession("task-fault");
|
||||
const faultBase = faultSession.prefix;
|
||||
faultSession.session.accept({ ...faultBase, kind: "LOAD_WORKSPACE", seq: 3, operation_id: "fault-load", payload: { root_path: "/workspace" } });
|
||||
faultSession.session.accept({ ...faultBase, kind: "PROGRAM_LOAD", seq: 4, operation_id: "fault-program", payload: { source: "G21\nG1 X1\n" } });
|
||||
faultSession.session.accept({ ...faultBase, kind: "PROGRAM_RUN", seq: 5, operation_id: "fault-run", payload: {} });
|
||||
const estop = faultSession.session.accept({ ...faultBase, kind: "ESTOP", seq: 6, operation_id: "estop", payload: { active: true } });
|
||||
assert.equal(estop.payload.task.state, "ESTOP");
|
||||
faultSession.session.accept({ ...faultBase, kind: "ESTOP", seq: 7, operation_id: "estop-clear", payload: { active: false } });
|
||||
const recoveredIo = faultSession.session.accept({ ...faultBase, kind: "IO_RECOVER", seq: 8, operation_id: "io-recover", payload: {} });
|
||||
assert.equal(recoveredIo.payload.task.state, "READY");
|
||||
|
||||
const second = await createSession("task-a");
|
||||
second.session.accept({ ...second.prefix, kind: "LOAD_WORKSPACE", seq: 3, operation_id: "load", payload: { root_path: "/workspace" } });
|
||||
second.session.accept({ ...second.prefix, kind: "PROGRAM_LOAD", seq: 4, operation_id: "program-load", payload: { path: "task.ngc", source: "G21 G90\nG1 X2 F60\nM2\n" } });
|
||||
second.session.accept({ ...second.prefix, kind: "PROGRAM_RUN", seq: 5, operation_id: "program-run", payload: {} });
|
||||
const secondTick = second.session.accept({ ...second.prefix, kind: "SIM_STEP", seq: 6, operation_id: "tick-1", payload: { cycles: 1 } });
|
||||
assert.equal(secondTick.payload.task.state_hash, firstTickHash);
|
||||
|
||||
console.log(`sim_task=passed state=${recovered.state.task.state} tick_hash=${firstTickHash} recovery_hash=${recovered.state.task.state_hash}`);
|
||||
Reference in New Issue
Block a user