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 { SimSession, CONTEXT_ID } from "./sim-session.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 SimSession(core, { sessionId: "xyzac-touch-off-source-flow" }); const base = { schema_version: 1, context_id: CONTEXT_ID, session_id: session.sessionId }; let seq = 0; const send = (kind, payload = {}) => session.accept({ ...base, kind, seq: ++seq, operation_id: `touch-${seq}`, payload }); send("HELLO"); send("SESSION_ATTACH"); assert.equal(send("LOAD_WORKSPACE", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified"); assert.equal(send("PROGRAM_LOAD", { source: "G21 G90\nG0 X10 Y5 Z200 A30 C45\nG1 X20 Y10 Z180 A35 C50 F100\nM2\n", path: "touch.ngc" }).payload.status, "verified"); assert.equal(send("POWER", { active: true }).payload.status, "verified"); assert.equal(send("HOME", { all: true }).payload.status, "verified"); assert.equal(send("SIM_STEP", { cycles: 1 }).payload.status, "verified"); const jogStart = send("JOG_START", { axis: "X", direction: 1, increment: null, continuous: true }); assert.equal(jogStart.payload.status, "verified"); let jogSample = jogStart; for (let sample = 0; sample < 5; sample += 1) jogSample = send("SIM_STEP", { cycles: 1, response: "motion_delta" }); assert.ok(Number(jogSample.payload.wasm_motion?.state?.position?.x ?? 0) > 0); assert.equal(send("JOG_STOP", {}).payload.status, "verified"); const beforeTouch = session.wasmMotion.read().state.position; const epochBefore = send("SNAPSHOT_REQUEST").payload.panel_snapshot.visual.live_path_epoch; const systemTouch = send("TOUCH_OFF", { axis: "X", value: 12, system: "P1 G54", units: "mm" }); assert.equal(systemTouch.kind, "COMMAND_RESULT"); assert.equal(systemTouch.payload.touch_off.source_block, "G10 L20 P1 X12"); assert.equal(systemTouch.payload.touch_off.block, "G10 L20 P1 X12"); assert.equal(systemTouch.payload.task.mode, "MANUAL"); let panel = send("SNAPSHOT_REQUEST").payload.panel_snapshot; assert.deepEqual(session.wasmMotion.read().state.position, beforeTouch); assert.ok(Math.abs(panel.coordinates.relative[0] - 12) < 1e-9); assert.ok(Math.abs(panel.visual.toolpath[0].end.x - (beforeTouch.x - 12 + 10)) < 1e-9); assert.equal(panel.visual.live_path_epoch, epochBefore); for (const axis of ["A", "C"]) { const angular = send("TOUCH_OFF", { axis, value: 15, system: "P1 G54", units: "inch" }); assert.equal(angular.kind, "COMMAND_RESULT", JSON.stringify(angular.payload)); assert.equal(angular.payload.block, `G10 L20 P1 ${axis}15`); panel = send("SNAPSHOT_REQUEST").payload.panel_snapshot; assert.equal(panel.coordinates.relative[axis === "A" ? 3 : 4], 15); } const savedOffsets = panel.program.coordinate_context; assert.equal(send("PROGRAM_LOAD", { source: "G21 G90\nG0 X13 A15 C15\nM2\n", path: "touch-reload.ngc" }).kind, "COMMAND_RESULT"); panel = send("SNAPSHOT_REQUEST").payload.panel_snapshot; assert.deepEqual(panel.program.coordinate_context, savedOffsets); assert.ok(Math.abs(panel.visual.toolpath[0].end.x - beforeTouch.x - 1) < 1e-9); assert.equal(send("TOUCH_OFF", { axis: "X", value: 0, system: "G54.1" }).payload.code, "touch_off_system_invalid"); assert.equal(send("TOUCH_OFF", { axis: "X", value: 20, system: "G59.3" }).kind, "COMMAND_RESULT"); assert.equal(send("MDI_EXECUTE", { block: "G59.3" }).kind, "COMMAND_RESULT"); panel = send("SNAPSHOT_REQUEST").payload.panel_snapshot; assert.equal(panel.coordinates.system, 9); assert.ok(Math.abs(panel.coordinates.relative[0] - 20) < 1e-9); assert.equal(send("MDI_EXECUTE", { block: "G54" }).kind, "COMMAND_RESULT"); assert.equal(send("SET_MODE", { mode: "MANUAL" }).kind, "COMMAND_RESULT"); assert.equal(send("TOOL_PREPARE", { tool: 1, pocket: 1 }).payload.status, "verified"); assert.equal(send("TOOL_PREPARED", { prepared: true }).payload.status, "verified"); assert.equal(send("TOOL_CHANGE", {}).payload.status, "verified"); assert.equal(send("TOOL_CHANGED", { changed: true }).payload.status, "verified"); const toolTouch = send("TOOL_TOUCH_OFF", { axis: "Z", value: 3, system: "G54", units: "mm" }); assert.equal(toolTouch.kind, "ERROR"); assert.equal(toolTouch.payload.gate_reason, "blocked_by_abi_tool_offsets"); const malformed = send("TOUCH_OFF", { axis: "Q", value: 0, system: "G54", units: "mm" }); assert.equal(malformed.kind, "ERROR"); assert.equal(malformed.payload.code, "touch_off_axis_invalid"); console.log(JSON.stringify({ schema: "xyzac-touch-off-source-flow.v1", status: "verified", source_block: systemTouch.payload.touch_off.source_block, wasm_block: systemTouch.payload.touch_off.block, tool_touch_off: toolTouch.payload.gate_reason, invalid: malformed.payload.code }));