import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import { LINUXCNC_PARITY_ITEMS, RIGHT_SIDEBAR_PARITY_ENTRIES, SWITCHKINS_PARITY_CODES, } from "../../app/src/runtime/linuxcnc-parity-matrix.js"; import { createMemorySessionStorage } from "../../app/src/runtime/five-axis-session.js"; import { createLinuxCncInterpreterRuntime } from "../../app/src/runtime/linuxcnc-interpreter-runtime.js"; import { parseLinuxCncIni } from "../../app/src/runtime/linuxcnc-ini-runtime.js"; import { getFiveAxisProfile } from "../../app/src/profiles/index.js"; import { createSimulationStore } from "../../app/src/state/store.js"; function item(matrix, id) { const found = matrix.items.find((entry) => entry.id === id); assert.ok(found, `missing parity matrix item ${id}`); return found; } async function waitForValidatedProgram(store) { for (let attempt = 0; attempt < 30; attempt += 1) { const state = store.getState(); if (!state.interpreterExecutionPending && state.programValidation?.ready) { return state; } await new Promise((resolve) => setTimeout(resolve, 0)); } return store.getState(); } async function loadIniConfigFromVendoredSource(profile) { const text = await readFile( new URL(`../../../wasm-port/vendor/linuxcnc/${profile.iniPath}`, import.meta.url), "utf8", ); return parseLinuxCncIni(text, { path: profile.iniPath, profileId: profile.id, }); } assert.equal(LINUXCNC_PARITY_ITEMS.length >= 11, true); assert.deepEqual(RIGHT_SIDEBAR_PARITY_ENTRIES, [ "E-STOP", "POWER", "RESET", "AUTO", "MANUAL", "JOG", "MDI", "IDENTITY", "TCP", ]); assert.deepEqual(SWITCHKINS_PARITY_CODES, ["M428", "M429", "M430"]); const store = createSimulationStore(); let state = store.getState(); let matrix = state.linuxCncParityMatrix; assert.equal(matrix.apiName, "web-rtcp-5axis-linuxcnc-parity-matrix"); assert.equal(matrix.profileId, "xyzac-trt"); assert.equal(matrix.status, "implemented"); assert.equal(matrix.implementedCount, matrix.itemCount); assert.equal(matrix.rightSidebarComplete, true); assert.equal(matrix.switchkinsComplete, true); assert.deepEqual(matrix.actualRightSidebarEntries, RIGHT_SIDEBAR_PARITY_ENTRIES); assert.equal(matrix.requiredSwitchkinsCodes.every((code) => matrix.activeSwitchkinsCodes.includes(code)), true); assert.equal(matrix.linuxCncPrograms.some((path) => path.endsWith("impeller-7bl-xyzac.ngc")), true); assert.equal(matrix.linuxCncPrograms.some((path) => path.endsWith("boat-xyzbc.ngc")), true); assert.equal(matrix.linuxCncFunctions.includes("E-STOP/POWER/RESET task-state entry"), true); assert.equal(matrix.linuxCncFunctions.includes("M428 TCP"), true); assert.equal(matrix.linuxCncFunctions.includes("M429 identity"), true); assert.equal(matrix.linuxCncFunctions.includes("M430 userk"), true); assert.equal(item(matrix, "xyzac-trt-axis-vismach-config").active, true); assert.equal(item(matrix, "xyzac-trt-axis-vismach-config").sourceMapped, true); assert.equal(item(matrix, "xyzac-trt-axis-vismach-config").linuxCncPaths.some((path) => path.endsWith("xyzac-trt.ini")), true); assert.equal(item(matrix, "xyzbc-trt-axis-vismach-config").implemented, true); assert.equal(item(matrix, "xyzbc-trt-axis-vismach-config").active, false); assert.equal(item(matrix, "gmoccapy-trt-config").linuxCncPaths.some((path) => path.includes("gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac-trt.ini")), true); assert.equal(item(matrix, "gmoccapy-trt-config").functions.includes("DISPLAY gmoccapy"), true); assert.equal(item(matrix, "gmoccapy-native-operator-ui").active, true); assert.equal(item(matrix, "right-sidebar-task-interlocks").active, true); assert.equal(item(matrix, "vismach-machine-preview").active, true); assert.equal(item(matrix, "source-derived-kinematics-switchkins").active, true); assert.equal(item(matrix, "linuxcnc-interpreter-program-validation").active, false); assert.equal(item(matrix, "machine-project-directory").active, false); assert.equal(item(matrix, "task-hal-status-loop").implemented, true); assert.equal(item(matrix, "gmoccapy-postgui-tool-spindle").active, true); store.dispatch({ type: "SET_PROFILE", profileId: "xyzbc-trt" }); state = store.getState(); matrix = state.linuxCncParityMatrix; assert.equal(matrix.profileId, "xyzbc-trt"); assert.equal(item(matrix, "xyzac-trt-axis-vismach-config").active, false); assert.equal(item(matrix, "xyzbc-trt-axis-vismach-config").active, true); assert.equal(item(matrix, "source-derived-kinematics-switchkins").evidence.activeSwitchkinsCodes.includes("M428"), true); store.dispatch({ type: "SET_PROFILE", profileId: "xyzac-trt" }); const profile = getFiveAxisProfile("xyzac-trt"); store.dispatch({ type: "ATTACH_INI_CONFIG", profileId: profile.id, iniConfig: await loadIniConfigFromVendoredSource(profile), }); store.dispatch({ type: "ATTACH_INTERPRETER_RUNTIME", runtime: await createLinuxCncInterpreterRuntime(), }); await store.stageMachineFiles({ storage: createMemorySessionStorage() }); state = store.getState(); matrix = state.linuxCncParityMatrix; assert.equal(item(matrix, "machine-project-directory").active, true); assert.equal(item(matrix, "machine-project-directory").evidence.iniSourceMatchesProfile, true); assert.equal(item(matrix, "machine-project-directory").evidence.gcodeFileCount >= 8, true); store.dispatch({ type: "LOAD_LINUXCNC_GCODE_SOURCE", sourceRel: "configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/impeller-7bl-xyzac.ngc", }); state = await waitForValidatedProgram(store); matrix = state.linuxCncParityMatrix; assert.equal(state.programValidation.ready, true); assert.equal(item(matrix, "linuxcnc-interpreter-program-validation").active, true); assert.equal(item(matrix, "linuxcnc-interpreter-program-validation").evidence.sourceGuard, "linuxcnc_vendored_5axis_gcode_source_file"); assert.equal(item(matrix, "linuxcnc-interpreter-program-validation").evidence.motionEventCount > 0, true); assert.equal(item(matrix, "linuxcnc-interpreter-program-validation").evidence.plannerSampleCount > 0, true); assert.equal(matrix.activeCount >= 9, true); console.log("linuxcnc_parity_matrix_smoke=ok");