Document source UI gates, explicit native live-plot clearing, and remaining parity boundaries. Fix pause/resume transport continuity, repeated stepping, coordinate previews, and spindle override state. Optimize large program snapshots and rendering, and cover the control flows with native Tcl, WASM, and browser checks. Validation: 22 native tests and source/browser checks pass. Worker/App regression: 113 of 123 pass; 10 pre-existing failures remain documented with no new failures.
65 lines
5.4 KiB
JavaScript
65 lines
5.4 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { readFile } from "node:fs/promises";
|
|
import createModule from "../../build-wasm/linuxcnc-sim-core.js";
|
|
import { loadSimulationCase, toWorkerConfiguration } from "../workspace/sim-case-loader.mjs";
|
|
import { XyzacSession, CONTEXT_ID } from "./xyzac-session.mjs";
|
|
import { axisGate, xyzacGateMatrix } from "../app/axis-contract.mjs";
|
|
|
|
const CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.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 core = await createModule({ wasmBinary: await readFile(new URL("../../build-wasm/linuxcnc-sim-core.wasm", import.meta.url)) });
|
|
const session = new XyzacSession(core, { sessionId: "xyzac-gate-matrix" });
|
|
const base = { schema_version: 1, context_id: CONTEXT_ID, session_id: session.sessionId };
|
|
let seq = 0;
|
|
const send = (kind, operation_id, payload = {}) => session.accept({ ...base, kind, seq: ++seq, operation_id, payload });
|
|
send("HELLO", "hello");
|
|
send("SESSION_ATTACH", "attach");
|
|
const loaded = send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) });
|
|
assert.equal(loaded.payload.status, "verified");
|
|
const initial = loaded.payload.panel_snapshot;
|
|
assert.equal(initial.xyzac.coordinates, "XYZAC");
|
|
assert.equal(initial.xyzac.joint_count, 5);
|
|
assert.equal(initial.gates.run.enabled, false);
|
|
assert.equal(initial.gates.run.reason, "xyzac_machine_off");
|
|
assert.equal(initial.gates.runLine.reason, "xyzac_machine_off");
|
|
assert.equal(initial.gates.switchkinsIdentity.enabled, false);
|
|
assert.equal(initial.gates.switchkinsXyzac.enabled, false);
|
|
assert.equal(initial.gates.switchkinsUserk.enabled, false);
|
|
assert.equal(initial.gates.vismachClear.visual_only, true);
|
|
assert.equal(send("POWER", "power-on", { active: true }).payload.panel_snapshot.axis_state.machine_state, "ON");
|
|
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
|
|
const powered = send("SNAPSHOT_REQUEST", "powered-snapshot").payload.panel_snapshot;
|
|
assert.equal(powered.gates.switchkinsIdentity.enabled, true);
|
|
assert.equal(powered.gates.switchkinsXyzac.enabled, true);
|
|
assert.equal(powered.gates.switchkinsUserk.enabled, true);
|
|
const noFile = axisGate("runLine", { task: { state: "READY", mode: "MANUAL" }, program: {}, motion: {}, taskfile: false, highlight_line: null });
|
|
assert.equal(noFile.enabled, false);
|
|
assert.equal(noFile.reason, "axis_taskfile_missing");
|
|
const highlightMissing = axisGate("runLine", { task: { state: "READY", mode: "MANUAL" }, program: { source_hash: "fnv1a64:file" }, motion: {}, taskfile: true, highlight_line: null });
|
|
assert.equal(highlightMissing.reason, "axis_highlight_missing");
|
|
const invalidSwitch = xyzacGateMatrix({ task: { state: "READY", mode: "MANUAL" }, program: {}, motion: {}, taskfile: false, xyzac: { coordinates: "XYZ", joint_count: 3, hal_pin_vars: false, switchkins_net: false, finite_offsets: true, userk_capability: false } });
|
|
assert.equal(invalidSwitch.switchkinsXyzac.enabled, false);
|
|
assert.equal(invalidSwitch.switchkinsXyzac.reason, "xyzac_hal_pin_vars_missing");
|
|
send("PROGRAM_LOAD", "program-load", { path: "gate.ngc", source: "G21 G90\nG1 X10 A20 C30 F100\nM2\n" });
|
|
const running = send("PROGRAM_RUN", "program-run");
|
|
assert.equal(running.payload.panel_snapshot.gates.pause.enabled, true);
|
|
assert.equal(running.payload.panel_snapshot.gates.abort.enabled, true);
|
|
assert.equal(running.payload.panel_snapshot.gates.resume.enabled, false);
|
|
assert.equal(running.payload.panel_snapshot.gates.switchkinsXyzac.enabled, false);
|
|
send("PROGRAM_PAUSE", "pause");
|
|
const paused = send("SNAPSHOT_REQUEST", "paused-snapshot");
|
|
assert.equal(paused.payload.panel_snapshot.gates.resume.enabled, true);
|
|
assert.equal(paused.payload.panel_snapshot.gates.pause.enabled, false);
|
|
send("PROGRAM_ABORT", "abort");
|
|
const estopGates = xyzacGateMatrix({ task: { state: "ESTOP", mode: "MANUAL" }, program: { source_hash: "fnv1a64:file" }, motion: {}, taskfile: true, xyzac: { coordinates: "XYZAC", joint_count: 5, hal_pin_vars: true, switchkins_net: true, finite_offsets: true, userk_capability: true } });
|
|
assert.equal(estopGates.switchkinsXyzac.enabled, false);
|
|
assert.equal(estopGates.switchkinsXyzac.reason, "xyzac_estop_active");
|
|
send("ESTOP", "estop-on", { active: true });
|
|
assert.equal(send("ESTOP", "estop-reset", { active: false }).payload.panel_snapshot.axis_state.source_task_state, "STATE_ESTOP_RESET");
|
|
send("POWER", "power-on-again", { active: true });
|
|
const recovered = send("SNAPSHOT_REQUEST", "recovered-snapshot");
|
|
assert.equal(typeof recovered.payload.panel_snapshot.state_hash, "string");
|
|
assert.equal(recovered.payload.panel_snapshot.gates.vismachClear.visual_only, true);
|
|
console.log(JSON.stringify({ schema: "xyzac-gate-matrix-rtcp-03.v1", status: "verified", case: CASE, normal: ["runLine", "switchkinsIdentity", "switchkinsXyzac", "switchkinsUserk"], blocked: [noFile.reason, highlightMissing.reason, invalidSwitch.switchkinsXyzac.reason, estopGates.switchkinsXyzac.reason], running: { pause: true, abort: true }, paused: { resume: true }, estop: { switchkins_enabled: estopGates.switchkinsXyzac.enabled }, final_state_hash: recovered.payload.panel_snapshot.state_hash }));
|