import assert from "node:assert/strict"; import { createLinuxCncInterpreterRuntime } from "../../app/src/runtime/linuxcnc-interpreter-runtime.js"; import { createLinuxCncKinematicsRuntime } from "../../app/src/runtime/linuxcnc-kinematics-runtime.js"; import { parseLinuxCncIni } from "../../app/src/runtime/linuxcnc-ini-runtime.js"; import { buildRtcpFrame } from "../../app/src/runtime/rtcp-frame.js"; import { createSimulationStore } from "../../app/src/state/store.js"; import { readFile } from "node:fs/promises"; async function waitForInterpreterExecution(store) { for (let attempt = 0; attempt < 20; attempt += 1) { const state = store.getState(); if (!state.interpreterExecutionPending && state.programExecutionSourceMode === "linuxcnc-interpreter-wasm") { return state; } await new Promise((resolve) => setTimeout(resolve, 0)); } return store.getState(); } 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 interpreterRuntime = await createLinuxCncInterpreterRuntime(); 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); assert.equal(fixtureInitialStore.getState().fullExecutionBoundary.fullLinuxCncProgramExecutionReady, false); assert.equal(fixtureInitialStore.getState().fullExecutionBoundary.promotionAllowed, false); assert.equal(fixtureInitialStore.getState().fullExecutionBoundary.phase, "blocked"); assert.equal(fixtureInitialStore.getState().linuxCncTaskPolicy.semanticBoundary, "linuxcnc_task_state_mode_command_gate"); assert.equal( fixtureInitialStore.getState().linuxCncTaskPolicy.sourceReferences.some((reference) => reference.path === "linuxcnc/src/emc/task/emctaskmain.cc"), true, ); assert.equal(fixtureInitialStore.getState().linuxCncTaskPolicy.canRunAuto, false); const xyzacIniText = await readFile( new URL("../../../wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", import.meta.url), "utf8", ); const xyzacIniConfig = parseLinuxCncIni(xyzacIniText, { path: fixtureInitialStore.getState().profile.iniPath, profileId: fixtureInitialStore.getState().machineProfile, }); fixtureInitialStore.dispatch({ type: "ATTACH_INI_CONFIG", iniConfig: xyzacIniConfig }); assert.equal(fixtureInitialStore.getState().iniConfigReadiness.loaded, true); assert.equal(fixtureInitialStore.getState().iniConfigReadiness.ready, true); assert.equal(fixtureInitialStore.getState().profile.axisLimits.X.max, 200); fixtureInitialStore.dispatch({ type: "TOGGLE_POWER" }); fixtureInitialStore.dispatch({ type: "HOME" }); fixtureInitialStore.dispatch({ type: "SET_MODE", mode: "mdi" }); fixtureInitialStore.dispatch({ type: "RUN_MDI", command: "G90 X999 Y-999 Z999 A999 C99999" }); assert.equal(fixtureInitialStore.getState().axisPose.x, 200); assert.equal(fixtureInitialStore.getState().axisPose.y, -100); assert.equal(fixtureInitialStore.getState().axisPose.z, 120); assert.equal(fixtureInitialStore.getState().axisPose.a, 50); assert.equal(fixtureInitialStore.getState().axisPose.c, 36000); 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.fullExecutionBoundary.satisfied.includes("linuxcnc-kinematics-wasm"), true); assert.equal(kinematicsState.fullExecutionBoundary.missing.includes("linuxcnc interpreter WASM runtime"), true); assert.equal(kinematicsState.kinematicsExecutionContext, "direct"); 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: "HOME" }); kinematicsStore.dispatch({ type: "SET_MODE", mode: "auto" }); 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: "ATTACH_INTERPRETER_RUNTIME", runtime: interpreterRuntime }); kinematicsStore.dispatch({ type: "LOAD_PROGRAM", filename: "linuxcnc-canonical-demo.ngc", content: [ "G90 G17", "G0 X0 Y0 Z0", "G1 X10 Y2 F100", "G1 X12 Y4 A5 C7", "M2", ].join("\n"), }); kinematicsState = await waitForInterpreterExecution(kinematicsStore); assert.equal(kinematicsState.linuxCncBoundaryAdapter.linuxCncInterpreterReady, true); assert.equal(kinematicsState.linuxCncBoundaryReadiness.linuxCncInterpreterReady, true); assert.equal(kinematicsState.programExecutionSourceMode, "linuxcnc-interpreter-wasm"); assert.equal(kinematicsState.programExecution.summary.motionEventCount >= 3, true); assert.equal(kinematicsState.programExecutionTiming.motionCount >= 3, true); assert.equal(kinematicsState.programExecutionTiming.sampleCount >= 3, true); assert.equal(kinematicsState.programExecutionTiming.totalSeconds > 0, true); assert.equal(kinematicsState.programExecutionTiming.semanticBoundary, "linuxcnc_tp_queue_runtime_timing_from_canonical_motion"); assert.equal(kinematicsState.programExecution.summary.plannerRuntimeReady, true); assert.equal(kinematicsState.fullExecutionBoundary.plannerRuntimeReady, true); assert.equal(kinematicsState.programExecution.summary.fullLinuxCncProgramExecutionReady, false); assert.equal(kinematicsState.fullExecutionBoundary.readyForUiSimulation, true); assert.equal(kinematicsState.fullExecutionBoundary.machineFileBackedRemapReady, false); assert.equal(kinematicsState.fullExecutionBoundary.fullLinuxCncProgramExecutionReady, false); assert.equal( kinematicsState.fullExecutionBoundary.semanticBoundary, "linuxcnc_interpreter_canonical_ready_planner_task_hal_blocked", ); kinematicsStore.dispatch({ type: "STEP" }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.programExecutionMotionIndex, 1); assert.equal(kinematicsState.activeLine, kinematicsState.programExecution.motion[1].line); assert.equal(kinematicsState.programRuntimeFeedback.sourceMode, "linuxcnc-tp-runtime-sample"); assert.equal(kinematicsState.programRuntimeFeedback.semanticBoundary, "linuxcnc_tp_run_cycle_feedback_without_hardware"); assert.equal(kinematicsState.axisPose.x, kinematicsState.programRuntimeFeedback.axisPose.x); assert.notEqual(kinematicsState.axisPose.x, kinematicsState.programExecution.motion[1].axes.x); assert.equal(kinematicsState.programRuntimeFeedback.queueDepth >= 0, true); assert.equal(kinematicsState.programElapsedSeconds > 0, true); assert.equal(kinematicsState.programRemainingSeconds >= 0, true); assert.equal(kinematicsState.feed.currentVelocity > 0, true); kinematicsStore.dispatch({ type: "LOAD_PROGRAM", filename: "linuxcnc-switchkins-rtcp-demo.ngc", content: [ "G90 G17", "M428", "G0 X0 Y0 Z0 A0 C0", "G1 X10 Y2 Z-1 A15 C30 F120", "M429", "G1 X0 Y0 Z0 A0 C0 F120", "M2", ].join("\n"), }); kinematicsState = await waitForInterpreterExecution(kinematicsStore); assert.equal(kinematicsState.programExecutionSourceMode, "linuxcnc-interpreter-wasm"); assert.equal(kinematicsState.programExecution.summary.switchkinsEventCount, 2); assert.equal(kinematicsState.programExecution.motion[0].switchkinsType, 1); assert.equal(kinematicsState.kinsType, "tcp-xyzac"); assert.equal(kinematicsState.rtcpState, "on"); assert.equal(kinematicsState.rtcpFrame.kinematicsSwitchkinsType, 1); kinematicsStore.dispatch({ type: "STEP" }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.programExecutionMotionIndex, 1); assert.equal(kinematicsState.programRuntimeFeedback.semanticBoundary, "linuxcnc_tp_run_cycle_feedback_without_hardware"); assert.equal(kinematicsState.axisPose.a > 0 && kinematicsState.axisPose.a < 15, true); assert.equal(kinematicsState.axisPose.c > 0 && kinematicsState.axisPose.c < 30, true); assert.equal(kinematicsState.programRuntimeFeedback.distanceToGo > 0, true); assert.equal(kinematicsState.kinsType, "tcp-xyzac"); assert.equal(kinematicsState.rtcpState, "on"); assert.equal(kinematicsState.rtcpFrame.kinematicsSwitchkinsType, 1); const switchkinsStepSampleIndex = kinematicsState.programExecutionSampleIndex; kinematicsStore.dispatch({ type: "RESUME" }); kinematicsStore.dispatch({ type: "RUN" }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.programExecutionSampleIndex > switchkinsStepSampleIndex, true); assert.equal(kinematicsState.programExecutionMotionIndex, 1); assert.equal(kinematicsState.programRuntimeFeedback.semanticBoundary, "linuxcnc_tp_run_cycle_feedback_without_hardware"); assert.equal(kinematicsState.programRuntimeFeedback.distanceToGo > 0, true); assert.equal(kinematicsState.kinsType, "tcp-xyzac"); assert.equal(kinematicsState.rtcpState, "on"); assert.equal(kinematicsState.rtcpFrame.kinematicsSwitchkinsType, 1); kinematicsStore.dispatch({ type: "PAUSE" }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.runState, "paused"); assert.equal(kinematicsState.machine.interpState, "paused"); assert.equal(kinematicsState.machine.taskPaused, true); kinematicsStore.dispatch({ type: "RUN" }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.operatorMessage, "run blocked: resume paused program first"); assert.equal(kinematicsState.runState, "paused"); kinematicsStore.dispatch({ type: "RESUME" }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.runState, "running"); assert.equal(kinematicsState.machine.interpState, "reading"); assert.equal(kinematicsState.machine.taskPaused, false); kinematicsStore.dispatch({ type: "STOP" }); kinematicsState = kinematicsStore.getState(); assert.equal(kinematicsState.runState, "stopped"); assert.equal(kinematicsState.machine.interpState, "idle"); 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(); let state; assert.deepEqual( store.getState().availableProfiles.map((profile) => profile.id), ["xyzac-trt", "xyzbc-trt", "gmoccapy-xyzab"], ); store.dispatch({ type: "SET_PROFILE", profileId: "xyzbc-trt" }); assert.equal(store.getState().machineProfile, "xyzbc-trt"); assert.equal(store.getState().profile.traj.coordinates, "XYZBC"); const xyzbcRuntime = await createLinuxCncKinematicsRuntime({ moduleId: "xyzbc-trt" }); store.dispatch({ type: "ATTACH_KINEMATICS_RUNTIME", runtime: xyzbcRuntime }); store.dispatch({ type: "SET_RTCP", enabled: true }); state = store.getState(); assert.equal(state.kinsType, "tcp-xyzbc"); assert.equal(state.rtcpFrame.kinematicsModuleId, "xyzbc-trt"); assert.equal(state.rtcpFrame.jointPose[3].axis, "B"); store.dispatch({ type: "SET_PROFILE", profileId: "xyzac-trt" }); store.dispatch({ type: "RUN" }); assert.equal(store.getState().activeLine, 501); assert.equal(store.getState().operatorMessage, "run blocked: machine must be on"); store.dispatch({ type: "TOGGLE_POWER" }); assert.equal(store.getState().machine.powerOn, true); store.dispatch({ type: "HOME" }); assert.equal(store.getState().machine.allHomed, true); store.dispatch({ type: "SET_MODE", mode: "auto" }); assert.equal(store.getState().linuxCncTaskPolicy.canRunAuto, true); assert.equal(store.getState().linuxCncTaskPolicy.canExecuteMdi, false); store.dispatch({ type: "SET_RTCP", enabled: true }); 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: "STOP" }); store.dispatch({ type: "SET_MODE", mode: "manual" }); store.dispatch({ type: "JOG", axis: "x", direction: 1, increment: 2 }); state = store.getState(); assert.equal(state.machine.mode, "manual"); assert.equal(state.runState, "jogging"); assert.equal(Math.round(state.axisPose.x), Math.round(state.rtcpFrame.axisPose.x)); store.dispatch({ type: "SET_MODE", mode: "mdi" }); 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"); assert.equal(state.axisPose.x, 1); assert.equal(state.programSource, "operator-mdi"); assert.equal(state.programLines[0], "G0 X1"); assert.equal(state.mdiHistory[0], "G0 X1"); const mdiRelativeBase = store.getState().axisPose; store.dispatch({ type: "RUN_MDI", command: "G91 X2 Y-3 F1200 M3 S2400 M8" }); state = store.getState(); assert.equal(state.machine.mdiDistanceMode, "relative"); assert.equal(state.axisPose.x, mdiRelativeBase.x + 2); assert.equal(state.axisPose.y, mdiRelativeBase.y - 3); assert.equal(state.feed.feedRate, 1200); assert.equal(state.spindle.enabled, true); assert.equal(state.spindle.rpm, 2400); assert.equal(state.coolant.flood, true); store.dispatch({ type: "RUN_MDI", command: "M428" }); state = store.getState(); assert.equal(state.kinsType, "tcp-xyzac"); assert.equal(state.rtcpState, "on"); store.dispatch({ type: "RUN_MDI", command: "M429 M9 M5" }); state = store.getState(); assert.equal(state.kinsType, "identity"); assert.equal(state.rtcpState, "off"); assert.equal(state.coolant.flood, false); assert.equal(state.coolant.mist, false); assert.equal(state.spindle.enabled, false); store.dispatch({ type: "LOAD_PROGRAM", filename: "operator-demo.ngc", content: "G0 X0 Y0\nG1 X10 F100\nM30\n", }); state = await waitForInterpreterExecution(store); 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); const floodBeforeToggle = store.getState().coolant.flood; store.dispatch({ type: "TOGGLE_COOLANT", kind: "flood" }); state = store.getState(); assert.equal(state.coolant.flood, !floodBeforeToggle); const mistBeforeToggle = store.getState().coolant.mist; store.dispatch({ type: "TOGGLE_COOLANT", kind: "mist" }); state = store.getState(); assert.equal(state.coolant.mist, !mistBeforeToggle); 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: "SET_MODE", mode: "manual" }); 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.machine.powerOn, false); assert.equal(state.machine.taskState, "estop-reset"); assert.equal(state.operatorMessage, "estop reset; machine off"); const taskHalSwitchkinsStore = createSimulationStore({ programStartLine: 1, activeLine: 5, kinsType: "tcp-xyzac", rtcpState: "on", programExecutionSourceMode: "linuxcnc-interpreter-wasm", programExecution: { sourceMode: "linuxcnc-interpreter-wasm", motion: [ { type: "STRAIGHT_TRAVERSE", line: 5, axes: { x: 1, y: 2, z: 3, a: 10, c: 20 }, switchkinsType: 1, kinsType: "tcp", }, { type: "STRAIGHT_FEED", line: 20, axes: { x: 2, y: 3, z: 4, a: 0, c: 0 }, switchkinsType: 0, kinsType: "identity", }, ], summary: { motionEventCount: 2, switchkinsEventCount: 2, switchkinsCodes: ["M428", "M429"], }, }, }); taskHalSwitchkinsStore.dispatch({ type: "TASK_HAL_STATUS_APPLIED", status: taskHalStatusForSwitchkinsLine({ activeLine: 5, switchkinsType: 0 }), operatorMessage: "task/HAL status retained program TCP switchkins", }); state = taskHalSwitchkinsStore.getState(); assert.equal(state.activeLine, 5); assert.equal(state.kinsType, "tcp-xyzac"); assert.equal(state.rtcpState, "on"); taskHalSwitchkinsStore.dispatch({ type: "TASK_HAL_STATUS_APPLIED", status: taskHalStatusForSwitchkinsLine({ activeLine: 20, switchkinsType: 0 }), operatorMessage: "task/HAL status applied program identity switchkins", }); state = taskHalSwitchkinsStore.getState(); assert.equal(state.activeLine, 20); assert.equal(state.kinsType, "identity"); assert.equal(state.rtcpState, "off"); console.log("rtcp_store_smoke=ok"); function taskHalStatusForSwitchkinsLine({ activeLine, switchkinsType }) { return { semanticBoundary: "linuxcnc_task_motion_hal_wasm_simulation_runtime", task: { state: "ON", mode: "AUTO", interpState: "READING", execState: "WAITING_FOR_MOTION", }, motionStatus: { motion: { programLine: activeLine, motionType: 1, switchkinsType, currentVel: 1, requestedVel: 1, inPosition: false, }, }, ui: { taskState: "on", taskMode: "auto", interpState: "reading", activeLine, switchkinsType, axisPoseFrame: "work", axisPose: { x: 1, y: 2, z: 3, a: 10, b: 0, c: 20 }, currentVelocity: 60, servoCycle: 1, taskCycle: 1, motionQueueDepth: 1, }, }; }