Files
linuxcncWebWASM/web/worker/test_xyzac_hal_tool_alarm.mjs

68 lines
5.7 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";
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-hal-tool" });
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");
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(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
const xml = await readFile(new URL("../../linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.xml", import.meta.url), "utf8");
const postgui = await readFile(new URL("../../linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal", import.meta.url), "utf8");
for (const pin of ["type0-button", "type1-button", "type2-button", "vismach-clear"]) assert.ok(xml.includes(pin));
for (const edge of ["pyvcp.type0-button", "pyvcp.type1-button", "pyvcp.type2-button", "vismach.plotclear"]) assert.ok(postgui.includes(edge));
for (const [code, type] of [["M429", 0], ["M428", 1], ["M430", 2]]) assert.equal(send("MDI_EXECUTE", `switch-${code}`, { block: code }).payload.kinematics.active_type, type);
const directSwitch = send("VCP_UPDATE", "direct-switch", { name: "xyzac", pin: "type1-button", value: 1 });
assert.equal(directSwitch.kind, "ERROR");
assert.equal(directSwitch.payload.code, "pyvcp_switch_requires_mdi");
send("SNAPSHOT_REQUEST", "before-clear");
const clear = send("VCP_UPDATE", "visual-clear", { name: "xyzac", pin: "vismach-clear", value: 1 });
assert.equal(clear.payload.visual_only, true);
assert.deepEqual(clear.payload.before, clear.payload.after);
const allowed = send("HAL_SET", "hal-allowed", { target: "pin", name: "motion.probe-input", type: "bit", value: true });
assert.equal(allowed.kind, "COMMAND_RESULT");
const denied = send("HAL_SET", "hal-denied", { target: "pin", name: "motion.switchkins-type", type: "s32", value: 2 });
assert.equal(denied.kind, "ERROR");
assert.equal(denied.payload.code, "wasm_hal_write_denied");
assert.equal(send("SPINDLE_COMMAND", "spindle-on", { spindle: 0, rpm: 1200, enabled: true }).kind, "COMMAND_RESULT");
send("IO_STEP", "spindle-step", { ticks: 10 });
const spindle = send("SNAPSHOT_REQUEST", "spindle-snapshot").payload.panel_snapshot.io.state.spindles[0];
assert.equal(spindle.command_rpm, 1200);
assert.equal(send("SPINDLE_COMMAND", "spindle-off", { spindle: 0, rpm: 0, enabled: false }).kind, "COMMAND_RESULT");
send("IO_STEP", "spindle-stop-step", { ticks: 20 });
assert.equal(send("COOLANT_COMMAND", "coolant-on", { mist: true, flood: true }).payload.panel_snapshot.device_io.source, "wasm_device_io");
assert.equal(send("TOOL_PREPARE", "tool-prepare", { tool: 7, pocket: 3 }).kind, "COMMAND_RESULT");
assert.equal(send("TOOL_PREPARED", "tool-prepared", { prepared: true }).kind, "COMMAND_RESULT");
assert.equal(send("TOOL_CHANGE", "tool-change").kind, "COMMAND_RESULT");
assert.equal(send("TOOL_CHANGED", "tool-changed", { changed: true }).payload.device_io.state.tool.current, 7);
assert.equal(send("MDI_EXECUTE", "g43", { block: "G43 H7" }).kind, "COMMAND_RESULT");
assert.equal(send("MDI_EXECUTE", "g49", { block: "G49" }).kind, "COMMAND_RESULT");
const ack = send("DEVICE_ACK_ISSUE", "ack-one", { timeout_ticks: 2 });
assert.equal(ack.kind, "COMMAND_RESULT");
assert.equal(send("DEVICE_ACK", "ack-confirm", { operation_id: "ack-one" }).kind, "COMMAND_RESULT");
assert.equal(send("DEVICE_ACK_ISSUE", "ack-timeout", { timeout_ticks: 1 }).kind, "COMMAND_RESULT");
send("SIM_STEP", "ack-timeout-step", { cycles: 1 });
const lateAck = send("DEVICE_ACK", "ack-late", { operation_id: "ack-timeout" });
assert.equal(lateAck.kind, "ERROR");
assert.equal(lateAck.payload.code, "wasm_sim_device_ack_ack_-1008");
assert.equal(send("DEVICE_ACK_ISSUE", "ack-abort", { timeout_ticks: 5 }).kind, "COMMAND_RESULT");
assert.equal(send("DEVICE_ACK_ABORT", "ack-abort-command", { operation_id: "ack-abort" }).kind, "COMMAND_RESULT");
const alarm = denied.payload;
assert.equal(typeof alarm.operation_id, "string");
assert.ok(session.timeline.export().events.events.some((event) => event.kind === "HAL_SET"));
const final = send("SNAPSHOT_REQUEST", "final").payload.panel_snapshot;
console.log(JSON.stringify({ schema: "xyzac-hal-tool-alarm-rtcp-07.v1", status: "verified", case: CASE, pyvcp: ["M429", "M428", "M430", "vismach-clear"], hal: { allowed: "motion.probe-input", denied: denied.payload.code }, tool: final.io.state.tool.current, spindle_rpm: spindle.command_rpm, coolant: final.device_io.state.coolant, ack: { normal: "verified", timeout: lateAck.payload.code, abort: "verified" }, alarm: { code: alarm.code, operation_id: alarm.operation_id }, state_hash: final.state_hash }));