import assert from "node:assert/strict"; import { getFiveAxisProfile } from "../../app/src/profiles/index.js"; import { createSimulationStore, validateRunPreconditions } from "../../app/src/state/store.js"; import { gateLinuxCncTaskAction } from "../../app/src/state/linuxcnc-task-policy.js"; const profile = getFiveAxisProfile("gmoccapy-xyzab"); const store = createSimulationStore(); store.dispatch({ type: "SET_PROFILE", profileId: "gmoccapy-xyzab" }); assert.equal(store.getState().profile.id, profile.id); assert.equal(store.getState().machine.noForceHoming, false); let gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: machine must be on"); store.dispatch({ type: "TOGGLE_POWER" }); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: switch to auto mode first"); store.dispatch({ type: "SET_MODE", mode: "auto" }); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: home machine first"); store.dispatch({ type: "SET_MODE", mode: "manual" }); store.dispatch({ type: "HOME" }); store.dispatch({ type: "SET_MODE", mode: "auto" }); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: LinuxCNC INI not loaded"); store.dispatch({ type: "ATTACH_INI_CONFIG", profileId: "gmoccapy-xyzab", iniConfig: { profileId: "gmoccapy-xyzab", path: profile.iniPath, sourceText: "[TRAJ]\nCOORDINATES = X Y Z A B\nNO_FORCE_HOMING = 0\n[KINS]\nKINEMATICS = trivkins coordinates=xyzab\n", validation: { ready: true }, traj: { coordinates: "XYZAB", noForceHoming: false }, kinematics: { name: "trivkins", sparm: "coordinates=xyzab" }, kinematicsParameters: profile.kinematicsParameters, kinematicsModuleId: profile.kinematicsModuleId, axisLimits: profile.axisLimits, jointConfig: profile.jointConfig, display: profile.display, rs274ngc: profile.rs274ngc, hal: { ...profile.hal, halcmd: profile.hal.halcmd, initialSets: [], }, halui: profile.halui, emcmot: { servoPeriodNs: 1000000 }, emcio: {}, task: { cycleTimeSeconds: 0.001 }, }, }); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: LinuxCNC machine files not staged"); store.dispatch({ type: "MACHINE_FILE_STAGING_COMPLETE", plan: { profileId: "gmoccapy-xyzab", selectedProgramSourceRel: null, }, save: { fileCount: 2, files: [ { sourceRel: "configs/sim/gmoccapy/gmoccapy_XYZAB.ini", wasmPath: "/work/sim/gmoccapy/gmoccapy_XYZAB.ini", kind: "ini", text: "", bytes: 0, }, ], summary: { gcodeFileCount: 0 }, }, }); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: no machine-file G-code opened for task/HAL session"); store.dispatch({ type: "MACHINE_FILE_STAGING_COMPLETE", plan: { profileId: "gmoccapy-xyzab", selectedProgramSourceRel: "configs/sim/gmoccapy/demos/xyzab-demo.ngc", }, save: { fileCount: 2, files: [ { sourceRel: "configs/sim/gmoccapy/demos/xyzab-demo.ngc", wasmPath: "/work/sim/gmoccapy/demos/xyzab-demo.ngc", kind: "demo", text: "G0 X0\nM2\n", bytes: 9, }, ], summary: { gcodeFileCount: 1 }, }, selectedGcodeSourceRel: "configs/sim/gmoccapy/demos/xyzab-demo.ngc", }); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: task/HAL runtime not ready"); store.dispatch({ type: "ATTACH_TASK_HAL_RUNTIME", runtime: { loaded: true, readiness() { return { loaded: true, taskRuntimeReady: true, motionRuntimeReady: true, halRuntimeReady: true, }; }, }, }); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, true); assert.equal(gate.status.canRunAutoStrict, true); assert.equal(gate.status.profileId, "gmoccapy-xyzab"); const runPreconditions = validateRunPreconditions(store.getState(), { requireTaskHalRuntime: false, requireTaskHalSession: false, }); assert.equal(runPreconditions.ok, false); assert.equal(runPreconditions.operatorMessage, "run blocked: unsupported five-axis profile gmoccapy-xyzab"); const manualStore = createSimulationStore(); manualStore.dispatch({ type: "SET_PROFILE", profileId: "gmoccapy-xyzab" }); manualStore.dispatch({ type: "TOGGLE_POWER" }); gate = gateLinuxCncTaskAction(manualStore.getState(), { type: "JOG", axis: "x", direction: 1 }); assert.equal(gate.allowed, true); gate = gateLinuxCncTaskAction(manualStore.getState(), { type: "HOME" }); assert.equal(gate.allowed, true); manualStore.dispatch({ type: "HOME" }); manualStore.dispatch({ type: "SET_MODE", mode: "mdi" }); gate = gateLinuxCncTaskAction(manualStore.getState(), { type: "RUN_MDI" }); assert.equal(gate.allowed, true); const controlStore = createSimulationStore(); controlStore.dispatch({ type: "SET_PROFILE", profileId: "gmoccapy-xyzab" }); controlStore.dispatch({ type: "ESTOP" }); gate = gateLinuxCncTaskAction(controlStore.getState(), { type: "TOGGLE_COOLANT", kind: "flood" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "coolant blocked: machine must be on"); gate = gateLinuxCncTaskAction(controlStore.getState(), { type: "SET_SPINDLE_DIRECTION", direction: "forward" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "spindle blocked: machine must be on"); controlStore.dispatch({ type: "RESET" }); controlStore.dispatch({ type: "TOGGLE_POWER" }); controlStore.dispatch({ type: "SET_SPINDLE_DIRECTION", direction: "forward" }); assert.equal(controlStore.getState().spindle.enabled, true); assert.equal(controlStore.getState().spindle.direction, "forward"); controlStore.dispatch({ type: "SET_SPINDLE_DIRECTION", direction: "stop" }); assert.equal(controlStore.getState().spindle.enabled, false); assert.equal(controlStore.getState().spindle.direction, "stop"); controlStore.dispatch({ type: "TOGGLE_COOLANT", kind: "flood" }); assert.equal(controlStore.getState().coolant.flood, true); console.log("gmoccapy_xyzab_gates_smoke=ok");