import assert from "node:assert/strict"; import { createLinuxCncKinematicsRuntime } from "../../app/src/runtime/linuxcnc-kinematics-runtime.js"; import { buildRtcpFrame } from "../../app/src/runtime/rtcp-frame.js"; import { createSimulationStore } from "../../app/src/state/store.js"; const identityFrame = buildRtcpFrame({ axisPose: { x: 43, y: -32.15, z: -11.306, a: 0, b: 0, c: 0 }, activeLine: 501, kinsType: "identity", rtcpEnabled: false, }); assert.equal(identityFrame.apiName, "web-rtcp-5axis-motion-frame"); assert.equal(identityFrame.rtcpState, "off"); assert.equal(identityFrame.readiness.frameReady, true); assert.equal(identityFrame.readiness.linuxCncKinematicsReady, false); assert.equal(identityFrame.readiness.promotionAllowed, false); assert.equal(identityFrame.semanticBoundary, "fixture_frame_ui_plumbing_not_linuxcnc_kinematics_proof"); assert.deepEqual(identityFrame.compensation, { x: 0, y: 0, z: 0 }); const tcpFrame = buildRtcpFrame({ axisPose: { x: 43, y: -32.15, z: -11.306, a: 15, b: 0, c: 30 }, activeLine: 502, kinsType: "tcp-xyzac", rtcpEnabled: true, }); assert.equal(tcpFrame.rtcpState, "on"); assert.equal(tcpFrame.kinsType, "tcp-xyzac"); assert.notEqual(tcpFrame.compensation.z, 0); assert.ok(Number.isFinite(tcpFrame.tcpPose.x)); assert.ok(Number.isFinite(tcpFrame.toolAxisVector.z)); assert.equal(tcpFrame.jointPose.length, 5); const runtime = await createLinuxCncKinematicsRuntime({ moduleId: "xyzac-trt" }); const linuxCncKinematicsResult = runtime.frameForJoints([10, 20, 30, 25, 40]); const linuxCncFrame = buildRtcpFrame({ axisPose: { x: 10, y: 20, z: 30, a: 25, b: 0, c: 40 }, activeLine: 503, kinsType: "tcp-xyzac", rtcpEnabled: true, linuxCncKinematicsResult, }); assert.equal(linuxCncFrame.sourceMode, "source-derived-kinematics-wasm"); assert.equal(linuxCncFrame.semanticBoundary, "linuxcnc_kinematics_wasm_c_abi"); assert.equal(linuxCncFrame.readiness.linuxCncKinematicsReady, true); assert.equal(linuxCncFrame.readiness.promotionAllowed, true); assert.equal(linuxCncFrame.readiness.fullLinuxCncProgramExecutionReady, false); assert.equal(linuxCncFrame.kinematicsModuleId, "xyzac-trt"); assert.equal(linuxCncFrame.jointPose[3].value, 25); assert.equal(linuxCncFrame.jointPose[4].value, 40); const fixtureInitialStore = createSimulationStore(); assert.equal(fixtureInitialStore.getState().rtcpState, "off"); assert.equal(fixtureInitialStore.getState().machine.powerOn, false); assert.equal(fixtureInitialStore.getState().machine.mode, "manual"); assert.equal( fixtureInitialStore.getState().linuxCncBoundaryAdapter.apiName, "web-rtcp-5axis-linuxcnc-boundary-adapter", ); assert.equal(fixtureInitialStore.getState().linuxCncBoundaryAdapter.panelSummary.schemaId, "xyzac-trt-switchkins-pyvcp"); assert.equal(fixtureInitialStore.getState().linuxCncBoundaryAdapter.sourceSummary.referenceCount >= 8, true); assert.equal(fixtureInitialStore.getState().linuxCncBoundaryAdapter.profileSummary.toolCount, 10); assert.equal(fixtureInitialStore.getState().linuxCncBoundaryAdapter.profileSummary.coordinates, "XYZAC"); assert.equal(fixtureInitialStore.getState().linuxCncBoundaryReadiness.ready, false); assert.equal(fixtureInitialStore.getState().linuxCncBoundaryReadiness.promotionAllowed, false); const kinematicsStore = createSimulationStore(); kinematicsStore.dispatch({ type: "ATTACH_KINEMATICS_RUNTIME", runtime }); let kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.linuxCncBoundaryAdapter.linuxCncKinematicsReady, true); assert.equal(kinematicsState.linuxCncBoundaryAdapter.interpreterRuntimeReady, false); assert.equal(kinematicsState.linuxCncBoundaryReadiness.ready, true); assert.equal(kinematicsState.linuxCncBoundaryReadiness.fullLinuxCncProgramExecutionReady, false); assert.equal(kinematicsState.linuxCncBoundaryReadiness.missing.includes("interpreter/remap runtime"), true); assert.equal(kinematicsState.rtcpFrame.sourceMode, "source-derived-kinematics-wasm"); assert.equal(kinematicsState.rtcpFrame.semanticBoundary, "linuxcnc_kinematics_wasm_c_abi"); assert.equal(kinematicsState.rtcpFrame.readiness.linuxCncKinematicsReady, true); kinematicsStore.dispatch({ type: "SET_RTCP", enabled: true }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.rtcpState, "on"); assert.equal(kinematicsState.rtcpFrame.sourceMode, "source-derived-kinematics-wasm"); assert.equal(kinematicsState.lastKinematicsResult.moduleId, "xyzac-trt"); assert.equal(kinematicsState.dro.tcpX, kinematicsState.rtcpFrame.tcpPose.x); kinematicsStore.dispatch({ type: "TOGGLE_POWER" }); kinematicsStore.dispatch({ type: "STEP" }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.runState, "stepping"); assert.equal(kinematicsState.rtcpFrame.sourceMode, "source-derived-kinematics-wasm"); assert.equal(kinematicsState.rtcpFrame.readiness.fullLinuxCncProgramExecutionReady, false); kinematicsStore.dispatch({ type: "SET_FRAME_SOURCE", sourceMode: "fixture-ui-only" }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.rtcpFrame.sourceMode, "fixture-ui-only"); assert.equal(kinematicsState.rtcpFrame.readiness.linuxCncKinematicsReady, false); const store = createSimulationStore(); store.dispatch({ type: "RUN" }); assert.equal(store.getState().activeLine, 501); assert.equal(store.getState().operatorMessage, "run blocked: power or estop state"); store.dispatch({ type: "TOGGLE_POWER" }); assert.equal(store.getState().machine.powerOn, true); store.dispatch({ type: "SET_RTCP", enabled: true }); let state = store.getState(); assert.equal(state.rtcpState, "on"); assert.equal(state.kinsType, "tcp-xyzac"); assert.equal(state.rtcpFrame.readiness.linuxCncKinematicsReady, false); assert.notEqual(state.dro.tcpZ, state.dro.z); const previousLine = state.activeLine; const previousTcpX = state.tcpPose.x; store.dispatch({ type: "STEP" }); state = store.getState(); assert.equal(state.activeLine, previousLine + 1); assert.equal(state.runState, "stepping"); assert.notEqual(state.tcpPose.x, previousTcpX); store.dispatch({ type: "JOG", axis: "x", direction: 1, increment: 2 }); state = store.getState(); assert.equal(state.machine.mode, "jog"); assert.equal(state.runState, "jogging"); assert.equal(Math.round(state.axisPose.x), Math.round(state.rtcpFrame.axisPose.x)); store.dispatch({ type: "RUN_MDI", command: "G0 X1" }); state = store.getState(); assert.equal(state.machine.mode, "mdi"); assert.equal(state.machine.mdiCommand, "G0 X1"); assert.equal(state.runState, "mdi"); store.dispatch({ type: "LOAD_PROGRAM", filename: "operator-demo.ngc", content: "G0 X0 Y0\nG1 X10 F100\nM30\n", }); state = store.getState(); assert.equal(state.activeProgram, "operator-demo.ngc"); assert.equal(state.programSource, "operator-file"); assert.equal(state.programStartLine, 1); assert.equal(state.activeLine, 1); assert.equal(state.programLines.length, 3); assert.equal(state.machine.mode, "auto"); store.dispatch({ type: "RUN" }); state = store.getState(); assert.equal(state.activeLine, 3); assert.equal(state.runState, "complete"); store.dispatch({ type: "SET_RTCP", enabled: false }); state = store.getState(); assert.equal(state.rtcpState, "off"); assert.equal(state.kinsType, "identity"); assert.equal(state.dro.tcpX, state.dro.x); store.dispatch({ type: "ADJUST_OVERRIDE", target: "feed", delta: -10 }); state = store.getState(); assert.equal(state.feed.feedOverride, 90); assert.equal(state.operatorMessage, "feed override adjusted"); store.dispatch({ type: "ADJUST_OVERRIDE", target: "rapid", delta: 20 }); state = store.getState(); assert.equal(state.feed.rapidOverride, 120); store.dispatch({ type: "ADJUST_SPINDLE_OVERRIDE", delta: 10 }); state = store.getState(); assert.equal(state.spindle.override, 110); store.dispatch({ type: "TOGGLE_COOLANT", kind: "flood" }); state = store.getState(); assert.equal(state.coolant.flood, false); store.dispatch({ type: "TOGGLE_COOLANT", kind: "mist" }); state = store.getState(); assert.equal(state.coolant.mist, true); store.dispatch({ type: "SET_VIEW", view: "x" }); state = store.getState(); assert.equal(state.preview.selectedView, "x"); store.dispatch({ type: "CLEAR_PREVIEW" }); state = store.getState(); assert.equal(state.preview.pathPoints, 0); store.dispatch({ type: "RELOAD_PROGRAM" }); state = store.getState(); assert.equal(state.preview.pathPoints, 3); assert.equal(state.activeLine, 1); assert.equal(state.runState, "idle"); store.dispatch({ type: "TOGGLE_FULLSCREEN" }); state = store.getState(); assert.equal(state.preview.fullscreen, true); store.dispatch({ type: "HOME" }); state = store.getState(); assert.equal(state.axisPose.x, 43); assert.equal(state.operatorMessage, "machine homed to fixture origin"); store.dispatch({ type: "ESTOP" }); state = store.getState(); assert.equal(state.machine.powerOn, false); assert.equal(state.machine.estopActive, true); assert.equal(state.runState, "estopped"); store.dispatch({ type: "RESET" }); state = store.getState(); assert.equal(state.machine.estopActive, false); assert.equal(state.operatorMessage, "machine reset complete"); console.log("rtcp_store_smoke=ok");