import assert from "node:assert/strict"; import { getFiveAxisProfile, fiveAxisProfiles } from "../../app/src/profiles/index.js"; import { buildRtcpFrame } from "../../app/src/runtime/rtcp-frame.js"; import { createLinuxCncBoundaryAdapter, createLinuxCncBoundaryReadiness } from "../../app/src/runtime/linuxcnc-boundary-adapter.js"; import { createProfileSourceReferenceSummary } from "../../app/src/profiles/source-reference-map.js"; import { createSimulationStore, validateRunPreconditions } from "../../app/src/state/store.js"; const profile = getFiveAxisProfile("gmoccapy-xyzab"); assert.equal(profile.id, "gmoccapy-xyzab"); assert.equal(profile.title, "gmoccapy XYZAB trivkins reference"); assert.equal(profile.iniPath, "linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.ini"); assert.deepEqual(profile.coordinates, ["X", "Y", "Z", "A", "B"]); assert.equal(profile.traj.coordinates, "XYZAB"); assert.equal(profile.joints.length, 5); assert.equal(profile.jointConfig.length, 5); assert.equal(profile.kinematics, "trivkins coordinates=xyzab"); assert.equal(profile.kinematicsModuleId, "gmoccapy-xyzab-trivkins-reference"); assert.equal(profile.tcpCapable, false); assert.equal(profile.rtcpProof, false); assert.equal(profile.linuxCncKinematicsReady, false); assert.equal(profile.promotionAllowed, false); assert.equal(profile.semanticBoundary, "gmoccapy_xyzab_trivkins_reference_only_not_rtcp_proof"); assert.equal(profile.homing.noForceHoming, false); assert.equal(profile.homing.noForceHomingIniValue, 0); assert.equal(profile.hal.postguiRequiresHalcompReady, true); assert.deepEqual(profile.hal.halFiles, ["core_sim_XYZAB.hal", "spindle_sim.hal", "simulated_home.hal"]); assert.deepEqual(profile.hal.postguiHalFiles, ["gmoccapy_postgui.hal"]); assert.equal(profile.nativeRuntime.task, "milltask"); assert.equal(profile.nativeRuntime.emcmot, "motmod"); assert.equal(profile.nativeRuntime.halui, "halui"); assert.equal(profile.nativeRuntime.displayCycleTimeMs, 100); assert.equal(profile.nativeRuntime.servoPeriodNs, 1000000); assert.equal(profile.axisLimits.X.max, 600); assert.equal(profile.axisLimits.Y.max, 400); assert.equal(profile.axisLimits.Z.min, -400); assert.equal(profile.axisLimits.B.maxVelocity, 90); assert.equal(profile.jointConfig[4].axis, "B"); assert.equal(profile.jointConfig[4].homeOffset, 45); assert.equal(profile.hal.halcmd.feedbackNets.length, 5); assert.equal(profile.hal.halcmd.offsetNets.some((net) => net.target === "gmoccapy.tooloffset-z"), true); assert.equal(profile.hal.halcmd.simulationLoops.some((net) => net.signal === "tool-change-loop"), true); assert.equal(profile.hal.halcmd.toolChange.strategy, "iocontrol-loopback"); assert.equal(profile.hal.halcmd.toolChange.manualGmoccapyPinsConnected, false); assert.deepEqual(profile.hal.halcmd.toolChange.postguiUnlinks, [ "iocontrol.0.tool-change", "iocontrol.0.tool-changed", "iocontrol.0.tool-prep-number", ]); assert.equal(profile.hal.halcmd.toolChange.commentedManualGuiNets.length, 3); assert.equal(profile.hal.halcmd.toolChange.activeLoop.signal, "tool-change-loop"); assert.equal(profile.halPins.includes("gmoccapy.program.current-line"), true); assert.equal(profile.halPins.includes("gmoccapy.spindle_at_speed_led"), true); assert.equal(profile.halPins.includes("gmoccapy.jog.axis.jog-x-plus"), true); assert.equal(profile.halPins.includes("gmoccapy.jog.axis.jog-y-minus"), true); assert.equal(profile.halPins.includes("gmoccapy.jog.axis.jog-b-minus"), true); assert.equal(profile.halPins.includes("gmoccapy.jog.jog-inc-0"), true); assert.equal(profile.halPins.includes("gmoccapy.jog.jog-inc-5"), true); assert.equal(profile.halPins.includes("gmoccapy.jog.jog-increment"), true); assert.equal(profile.halPins.includes("gmoccapy.jog.turtle-jog"), true); assert.equal(profile.halPins.includes("gmoccapy.ignore-limits"), true); assert.equal(profile.halPins.includes("gmoccapy.optional-stop"), true); assert.equal(profile.halPins.includes("gmoccapy.blockdelete"), true); assert.equal(profile.halPins.includes("gmoccapy.unlock-settings"), true); assert.equal(profile.halPins.includes("gmoccapy.feed.feed-override.count-enable"), true); assert.equal(profile.halPins.includes("gmoccapy.feed.reset-feed-override"), true); assert.equal(profile.halPins.includes("gmoccapy.spindle.reset-spindle-override"), true); assert.equal(profile.halPins.includes("gmoccapy.jog.jog-velocity.direct-value"), true); assert.equal(profile.halPins.includes("gmoccapy.delete-message"), true); assert.equal(profile.halPins.includes("gmoccapy.warning-confirm"), true); assert.equal(profile.halPins.includes("gmoccapy.probeheight"), true); assert.equal(profile.halPins.includes("gmoccapy.blockheight"), true); assert.equal(profile.halPins.includes("gmoccapy.toolmeasurement"), true); assert.equal(profile.halPins.includes("gmoccapy.searchvel"), true); assert.equal(profile.halPins.includes("gmoccapy.probevel"), true); assert.equal(profile.halPins.includes("gmoccapy.toolchange-confirm"), true); assert.equal(profile.toolsensor.configured, false); assert.equal(profile.toolsensor.useToolMeasurementPref, false); assert.equal(profile.toolsensor.blockHeightPref, 0); assert.equal(profile.toolsensor.reason.includes("no [TOOLSENSOR]"), true); assert.equal(profile.userMessages.configured, false); assert.equal(profile.userMessages.count, 0); assert.equal(profile.userMessages.reason.includes("no [DISPLAY] MESSAGE_*"), true); assert.equal(profile.nativePages.filePage.configured, true); assert.equal(profile.nativePages.filePage.programPrefix, "../../nc_files/"); assert.equal(profile.nativePages.filePage.webImplementation, "partial"); assert.equal(profile.nativePages.macroPage.configured, true); assert.equal(profile.nativePages.macroPage.count, 5); assert.equal(profile.nativePages.macroPage.macros.find((macro) => macro.name === "increment").args.length, 2); assert.equal(profile.nativePages.macroPage.macros.find((macro) => macro.name === "go_to_position").command, "O call [X-pos] [Y-pos] [Z-pos]"); assert.equal(profile.nativePages.toolEditor.configured, true); assert.equal(profile.nativePages.toolEditor.writebackEnabled, false); assert.equal(profile.nativePages.toolEditor.toolCount, 17); assert.equal(profile.nativePages.implementationMatrixBoundary.includes("diagnostic-only"), true); assert.equal(profile.toolTable.toolCount, 17); assert.equal(profile.macros.includes("go_to_position X-pos Y-pos Z-pos"), true); assert.equal(profile.panelSchema.id, "gmoccapy-xyzab-hal-component"); assert.equal(profile.panelSchema.boundary, "gmoccapy_hal_component_reference_only"); assert.equal(profile.panelSchema.groups.length, 2); const taskModeControls = profile.panelSchema.groups.find((group) => group.id === "task-mode-actions").controls; assert.deepEqual(taskModeControls.map((control) => [control.id, control.halpin]), [ ["tbtn-estop", "gmoccapy.v-button.button-0"], ["tbtn-on", "gmoccapy.v-button.button-1"], ["rbt-manual", "gmoccapy.v-button.button-2"], ["rbt-mdi", "gmoccapy.v-button.button-3"], ["rbt-auto", "gmoccapy.v-button.button-4"], ]); assert.deepEqual(fiveAxisProfiles.map(({ id }) => id), ["xyzac-trt", "xyzbc-trt", "gmoccapy-xyzab"]); const sourceSummary = createProfileSourceReferenceSummary("gmoccapy-xyzab"); assert.equal(sourceSummary.profileId, "gmoccapy-xyzab"); assert.equal(sourceSummary.referenceCount >= 10, true); assert.equal(sourceSummary.kinds.includes("ini"), true); assert.equal(sourceSummary.kinds.includes("halfile"), true); assert.equal(sourceSummary.kinds.includes("postgui_hal"), true); assert.equal(sourceSummary.kinds.includes("icon_manifest"), true); assert.equal(sourceSummary.kinds.includes("python_gui"), true); assert.equal(sourceSummary.promotionAllowed, false); assert.equal(sourceSummary.semanticBoundary, "profile_source_map_only_not_runtime_proof"); assert.ok(sourceSummary.references.some((reference) => reference.path.endsWith("gmoccapy_XYZAB.ini"))); assert.ok(sourceSummary.references.some((reference) => reference.path.endsWith("gmoccapy_postgui.hal"))); assert.ok(sourceSummary.references.some((reference) => reference.path.endsWith("gmoccapy-button-icons.json"))); const adapter = createLinuxCncBoundaryAdapter({ profile }); assert.equal(adapter.profileId, "gmoccapy-xyzab"); assert.equal(adapter.profileSummary.coordinates, "XYZAB"); assert.equal(adapter.profileSummary.jointCount, 5); assert.equal(adapter.profileSummary.mdiCommandCount, 0); assert.equal(adapter.profileSummary.remapCount, 2); assert.equal(adapter.profileSummary.toolCount, 17); assert.equal(adapter.profileSummary.feedbackNetCount, 5); assert.equal(adapter.profileSummary.halFileCount, 4); assert.equal(adapter.promotionAllowed, false); assert.equal(adapter.linuxCncKinematicsReady, false); const readiness = createLinuxCncBoundaryReadiness(adapter); assert.equal(readiness.ready, false); assert.equal(readiness.promotionAllowed, false); assert.equal(readiness.missing.includes("kinematics runtime"), true); const frame = buildRtcpFrame({ axisPose: { x: 10, y: 20, z: -5, a: 30, b: 45, c: 99 }, activeLine: 10, profile, }); assert.equal(frame.profileId, "gmoccapy-xyzab"); assert.deepEqual(frame.jointPose.map((joint) => joint.axis), ["X", "Y", "Z", "A", "B"]); assert.equal(frame.jointPose[4].value, 45); assert.equal(frame.readiness.linuxCncKinematicsReady, false); assert.equal(frame.readiness.promotionAllowed, false); const store = createSimulationStore(); store.dispatch({ type: "SET_PROFILE", profileId: "gmoccapy-xyzab" }); assert.equal(store.getState().machineProfile, "gmoccapy-xyzab"); assert.equal(store.getState().profile.tcpCapable, false); assert.equal(store.getState().rtcpState, "off"); store.dispatch({ type: "SET_KINS_TYPE", kinsType: "tcp-xyzac" }); assert.equal(store.getState().kinsType, "identity"); assert.equal(store.getState().rtcpState, "off"); assert.equal(store.getState().operatorMessage, "kinematics tcp-xyzac blocked: gmoccapy-xyzab is not TCP capable"); store.dispatch({ type: "SET_RTCP", enabled: true }); assert.equal(store.getState().rtcpState, "off"); assert.equal(store.getState().operatorMessage, "RTCP blocked: gmoccapy-xyzab is trivkins coordinates=xyzab reference only"); const preconditions = validateRunPreconditions(store.getState(), { requireTaskHalRuntime: false, requireTaskHalSession: false, }); assert.equal(preconditions.ok, false); assert.equal(preconditions.operatorMessage, "run blocked: unsupported five-axis profile gmoccapy-xyzab"); console.log("gmoccapy_xyzab_profile_smoke=ok");