import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { createLinuxCncInterpreterRuntime } from "../../app/src/runtime/linuxcnc-interpreter-runtime.js"; const runtime = await createLinuxCncInterpreterRuntime(); const readiness = runtime.readiness(); assert.equal(runtime.apiName, "web-rtcp-5axis-linuxcnc-interpreter-runtime"); assert.equal(runtime.loaded, true); assert.equal(runtime.sourceMode, "linuxcnc-interpreter-wasm"); assert.equal(runtime.semanticBoundary, "linuxcnc_interpreter_wasm_canonical_events"); assert.equal(readiness.loaded, true); assert.equal(readiness.runProgramReady, true); assert.equal(readiness.remapRuntimeReady, false); assert.equal(readiness.plannerRuntimeReady, true); assert.equal(readiness.plannerSemanticBoundary, "linuxcnc_tp_queue_runtime_timing_from_canonical_motion"); const programText = [ "G90 G17", "G0 X0 Y0 Z0", "G1 X10 Y2 F100", "G1 X12 Y4 A5 C7", "M2", ].join("\n"); const execution = runtime.runProgram(programText); assert.equal(execution.apiName, "web-rtcp-5axis-linuxcnc-interpreter-program-execution"); assert.equal(execution.sourceMode, "linuxcnc-interpreter-wasm"); assert.equal(execution.semanticBoundary, "linuxcnc_interpreter_wasm_canonical_events"); assert.equal(execution.summary.ready, true); assert.equal(execution.summary.motionEventCount >= 3, true); assert.equal(execution.summary.canonicalEventCount > execution.summary.motionEventCount, true); assert.equal(execution.summary.motionTypes.includes("STRAIGHT_TRAVERSE"), true); assert.equal(execution.summary.motionTypes.includes("STRAIGHT_FEED"), true); assert.equal(execution.summary.finalAxes.x, 12); assert.equal(execution.summary.finalAxes.y, 4); assert.equal(execution.summary.finalAxes.a, 5); assert.equal(execution.summary.finalAxes.c, 7); assert.equal(execution.motion[2].feedRate, 100); assert.equal(execution.summary.remapRuntimeReady, false); assert.equal(execution.summary.plannerRuntimeReady, true); assert.equal(execution.plannerTiming.semanticBoundary, "linuxcnc_tp_queue_runtime_timing_from_canonical_motion"); assert.equal(execution.plannerTiming.totalSeconds > 0, true); assert.equal(execution.plannerTiming.motionCount, execution.motion.length); assert.equal(execution.summary.fullLinuxCncProgramExecutionReady, false); const g18ArcProgramText = [ "G90 G18", "G0 X1 Z0 Y0", "G3 X0 Z1 I-1 K0 Y2 F80", "M2", ].join("\n"); const g18ArcExecution = runtime.runProgram(g18ArcProgramText); const g18ArcMotion = g18ArcExecution.motion[1]; assert.equal(g18ArcExecution.summary.ready, true); assert.equal(g18ArcExecution.summary.motionEventCount, 2); assert.equal(g18ArcExecution.summary.motionTypes.includes("ARC_FEED"), true); assert.equal(g18ArcExecution.summary.finalAxes.x, 0); assert.equal(g18ArcExecution.summary.finalAxes.y, 2); assert.equal(g18ArcExecution.summary.finalAxes.z, 1); assert.equal(g18ArcMotion.type, "ARC_FEED"); assert.equal(g18ArcMotion.axes.arc.plane, 180); assert.equal(g18ArcMotion.axes.arc.firstAxis, "z"); assert.equal(g18ArcMotion.axes.arc.secondAxis, "x"); assert.equal(g18ArcMotion.axes.arc.thirdAxis, "y"); assert.equal(g18ArcMotion.axes.z, 1); assert.equal(g18ArcMotion.axes.x, 0); assert.equal(g18ArcMotion.axes.y, 2); assert.equal(g18ArcMotion.feedRate, 80); assert.equal(g18ArcExecution.summary.plannerRuntimeReady, true); const switchkinsProgramText = [ "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"); const switchkinsExecution = runtime.runProgram(switchkinsProgramText); assert.equal(switchkinsExecution.summary.ready, true); assert.equal(switchkinsExecution.summary.motionEventCount, 3); assert.equal(switchkinsExecution.summary.switchkinsEventCount, 2); assert.deepEqual(switchkinsExecution.summary.switchkinsCodes, ["M428", "M429"]); assert.equal( switchkinsExecution.summary.switchkinsRemapBoundary, "linuxcnc_switchkins_remap_mcode_preserved_web_runtime_applied", ); assert.equal(switchkinsExecution.switchkinsEvents[0].switchkinsType, 1); assert.equal(switchkinsExecution.switchkinsEvents[1].switchkinsType, 0); assert.equal(switchkinsExecution.motion[0].kinsType, "tcp"); assert.equal(switchkinsExecution.motion[1].axes.a, 15); assert.equal(switchkinsExecution.motion[1].axes.c, 30); assert.equal(switchkinsExecution.motion[1].switchkinsCode, "M428"); assert.equal(switchkinsExecution.motion[2].kinsType, "identity"); assert.equal(switchkinsExecution.motion[2].switchkinsCode, "M429"); assert.equal(switchkinsExecution.summary.remapRuntimeReady, false); assert.equal(switchkinsExecution.summary.plannerRuntimeReady, true); assert.equal(switchkinsExecution.plannerTiming.totalSeconds > 0, true); assert.equal(switchkinsExecution.summary.fullLinuxCncProgramExecutionReady, false); const gmoccapyImpellerProgram = readFileSync(fileURLToPath(new URL( "../../../linuxcnc/configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc", import.meta.url, )), "utf8"); const gmoccapyImpellerExecution = runtime.runProgram(gmoccapyImpellerProgram); const firstG93FeedMotion = gmoccapyImpellerExecution.motion.find((event) => event.type === "STRAIGHT_FEED"); assert.equal(gmoccapyImpellerExecution.summary.ready, true); assert.equal(gmoccapyImpellerExecution.motion[0].feedMode, "inverse-time"); assert.equal(firstG93FeedMotion.line, 9); assert.equal(Math.abs(firstG93FeedMotion.feedRate - 635.745) < 0.001, true); assert.notEqual(firstG93FeedMotion.feedRate, 318); console.log("linuxcnc_interpreter_runtime_smoke=ok");