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"; const CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"; const COMMANDS = [["M429", 0], ["M428", 1], ["M430", 2], ["M428", 1]]; const HELIX = "G21 G90 G94\nG0 X10 Y0 Z10 A0 C0\nG93\nG2 X10 Y0 Z5 A30 C45 I-10 J0 P3 F2\nM2\n"; 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 selectedPose = (value) => ({ a: value.a, c: value.c, x: value.x, y: value.y, z: value.z }); const bundle = await loadSimulationCase({ path: CASE, fetchImpl }); assert.equal(bundle.status, "ready"); 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-trace-parity" }); const base = { schema_version: 1, context_id: CONTEXT_ID, session_id: session.sessionId }; let seq = 0; const send = (kind, operationId, payload = {}) => session.accept({ ...base, kind, seq: ++seq, operation_id: operationId, payload }); assert.equal(send("HELLO", "hello").kind, "CAPABILITIES"); assert.equal(send("SESSION_ATTACH", "attach").kind, "SESSION_READY"); assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified"); assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified"); assert.equal(session.xyzacKinematics.forward([12, -5, 20, 30, 45]).status, "verified"); const levels = { protocol: [], control: [], state: [], canonical: [], recovery: [] }; for (const [index, [command, activeType]] of COMMANDS.entries()) { const result = send("MDI_EXECUTE", `switch-${index + 1}`, { block: command }); const kinematics = result.payload.kinematics; const tick = index + 1; assert.equal(result.kind, "COMMAND_RESULT"); assert.equal(result.payload.status, "verified"); assert.equal(kinematics.active_type, activeType); // POWER_ON is a selected-case startup prerequisite and is not part of the // native switchkins action trace. Normalize the protocol action sequence so // native/WASM compare the same user-visible M-code operations. levels.protocol.push({ command, result: "verified", seq: index + 4, tick }); levels.control.push({ active_type: activeType, command, hal_pin: result.payload.remap.hal_pin, tick }); levels.state.push({ active_type: activeType, joints: kinematics.joints, pose: selectedPose(kinematics.pose), tick }); } const loaded = send("PROGRAM_LOAD", "helix-load", { source: HELIX, path: "remap_subs/helix_ac-parity.ngc" }); assert.equal(loaded.kind, "COMMAND_RESULT"); const arc = loaded.payload.preview.segments.find((segment) => segment.motion_type === 2); assert.ok(arc); levels.canonical.push({ end: selectedPose(arc.end), motion_type: arc.motion_type, source_line: arc.source_line, tick: 5, turn: arc.turn, velocity: arc.velocity }); const before = session.xyzacKinematics.snapshot(); const checkpoint = send("CHECKPOINT", "parity-checkpoint").payload.checkpoint; assert.equal(checkpoint.state.kinematics.state_hash, before.state_hash); assert.equal(send("MDI_EXECUTE", "parity-change", { block: "M429" }).payload.kinematics.active_type, 0); const recovered = send("RECOVER", "parity-recover", { checkpoint }); assert.equal(recovered.payload.status, "recovered"); const after = recovered.payload.state.kinematics; levels.recovery.push({ active_type: after.active_type, joints_equal: JSON.stringify(after.joints) === JSON.stringify(before.joints), pose_equal: JSON.stringify(after.pose) === JSON.stringify(before.pose), state_hash_equal: after.state_hash === before.state_hash, tick: 6 }); assert.deepEqual(levels.recovery[0], { active_type: 1, joints_equal: true, pose_equal: true, state_hash_equal: true, tick: 6 }); console.log(JSON.stringify({ case: "xyzac-trt.ini", levels, schema: "xyzac-structured-trace.v1", source: "linuxcnc-wasm-worker", status: "verified" }));