Files
cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_xyzbc_trt_web_app.mjs
2026-07-07 09:40:33 -04:00

591 lines
30 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { createMemorySessionStorage } from "../../app/src/runtime/five-axis-session.js";
import { parseLinuxCncIni } from "../../app/src/runtime/linuxcnc-ini-runtime.js";
import {
createMachineFileStagingPlan,
selectMachineFileProgram,
stageProfileMachineFiles,
} from "../../app/src/runtime/linuxcnc-machine-file-staging.js";
import { createLinuxCncInterpreterRuntime } from "../../app/src/runtime/linuxcnc-interpreter-runtime.js";
import {
buildTaskHalProgramMotionPlan,
buildTaskHalSessionFromMachineFiles,
wrapTaskHalSdk,
} from "../../app/src/runtime/linuxcnc-task-hal-runtime.js";
import { getFiveAxisProfile } from "../../app/src/profiles/index.js";
import { createSimulationStore } from "../../app/src/state/store.js";
import { createLinuxCncTaskHalSdk } from "../../../wasm-port/runtime/sdk/src/linuxcnc-task-hal.js";
import { buildVismachModelState, XYZBC_TRT_VISMACH_PINS } from "../../app/src/runtime/vismach-model-state.js";
import { AXIS_BUTTON_PARITY } from "../../app/src/ui/axis-shell.js";
import {
applyToolCommandSequence,
createToolRuntimeState,
} from "../../app/src/runtime/tool-db-simulation.js";
import { buildAxisExecutionTraceFromProgram } from "../../app/src/runtime/axis-preview-path.js";
const profile = getFiveAxisProfile();
assert.equal(profile.id, "xyzbc-trt");
assert.equal(profile.machineName, "sim-xyzbc-trt-kins (switchkins)");
assert.equal(profile.traj.coordinates, "XYZBC");
assert.deepEqual(profile.coordinates, ["X", "Y", "Z", "B", "C"]);
assert.equal(profile.kinematics, "xyzbc-trt-kins");
assert.equal(profile.kinematicsModuleId, "xyzbc-trt");
assert.equal(profile.machineFileStaging.defaultProgramFilename, "xyzbc_switchkins.ngc");
assert.equal(profile.panelSchema.id, "xyzbc-trt-switchkins-pyvcp");
assert.ok(profile.halPins.includes("xyzbc-trt-kins.x-offset"));
assert.ok(profile.halPins.includes("motion.switchkins-type"));
assert.deepEqual(profile.hal.halcmd.feedbackNets.map((net) => net.signal), [
"table-x",
"saddle-y",
"spindle-z",
"tilt-b",
"rotate-c",
]);
assert.deepEqual(profile.hal.halcmd.offsetNets.map((net) => net.target), [
"xyzbc-trt-kins.tool-offset",
"xyzbc-trt-gui.tool-offset",
"xyzbc-trt-gui.x-offset",
"xyzbc-trt-gui.z-offset",
]);
assert.deepEqual(profile.samplePrograms, [
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc",
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/boat-xyzbc.ngc",
]);
assert.ok(profile.remaps.some((remap) => remap.code === "M428" && remap.switchkinsType === 1));
assert.ok(profile.remaps.some((remap) => remap.code === "M429" && remap.switchkinsType === 0));
assert.ok(profile.remaps.some((remap) => remap.code === "M430" && remap.switchkinsType === 2));
const iniText = await readFile(
new URL("../../../wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", import.meta.url),
"utf8",
);
const ini = parseLinuxCncIni(iniText, {
path: profile.iniPath,
profileId: profile.id,
});
assert.equal(ini.validation.ready, true);
assert.equal(ini.machineName, "sim-xyzbc-trt-kins (switchkins)");
assert.equal(ini.kinematics.name, "xyzbc-trt-kins");
assert.equal(ini.kinematicsModuleId, "xyzbc-trt");
assert.equal(ini.traj.coordinates, "XYZBC");
assert.equal(ini.rs274ngc.halPinVars, true);
assert.equal(ini.rs274ngc.parameterFile, "xyzbc.var");
assert.equal(ini.emcio.toolTable, "xyzbc-trt.tbl");
assert.equal(ini.jointConfig[3].axis, "B");
assert.equal(ini.jointConfig[4].axis, "C");
assert.deepEqual(ini.halui.mdiCommands, ["M429", "M428", "M430"]);
const storage = createMemorySessionStorage();
const staged = await stageProfileMachineFiles(profile, { storage });
assert.equal(staged.plan.profileId, "xyzbc-trt");
assert.equal(staged.plan.wasmDir, "/work/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt");
assert.equal(staged.save.status, "saved");
assert.equal(staged.save.storageMode, "memory");
assert.ok(staged.save.opfsRoot.endsWith("/xyzbc-trt"));
assert.ok(staged.save.files.every((file) => file.opfsPath.startsWith("web-rtcp-5axis-xyzbc-trt-sim-plan/machines/xyzbc-trt/")));
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc-trt.ini") && file.kind === "ini"));
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc-trt.xml") && file.kind === "pyvcp"));
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc-trt.tbl") && file.kind === "toolTable"));
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/428remap.ngc") && file.kind === "remap"));
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/429remap.ngc") && file.kind === "remap"));
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/430remap.ngc") && file.kind === "remap"));
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/xyzbc_switchkins_sub.ngc") && file.kind === "remap"));
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/centering.ngc") && file.kind === "remap"));
assert.ok(staged.save.files.some((file) => file.sourceRel.endsWith("remap_subs/helix_bc.ngc") && file.kind === "remap"));
assert.ok(staged.save.gcodeSources.some((source) => source.filename === "xyzbc_switchkins.ngc"));
assert.ok(staged.save.gcodeSources.some((source) => source.filename === "boat-xyzbc.ngc"));
assert.equal(staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc.var") && file.kind === "parameters"), true);
assert.equal(staged.save.files.find((file) => file.sourceRel.endsWith("xyzbc-trt.tbl")).text.includes("T2 P2 Z10 D8"), true);
const selectedPlan = selectMachineFileProgram(
staged.plan,
staged.save,
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc",
);
assert.equal(selectedPlan.selectedProgramFilename, "xyzbc_switchkins.ngc");
assert.ok(selectedPlan.wasmProgramPath.endsWith("/demos/xyzbc_switchkins.ngc"));
const selectedProgramFile = staged.save.files.find((file) => file.sourceRel === selectedPlan.selectedProgramSourceRel);
const semanticExecutionPath = buildAxisExecutionTraceFromProgram({
filename: selectedPlan.selectedProgramFilename,
sourceRel: selectedPlan.selectedProgramSourceRel,
content: selectedProgramFile.text,
});
assert.equal(semanticExecutionPath.status, "ok");
assert.equal(semanticExecutionPath.samplePeriodMs, 50);
assert.equal(semanticExecutionPath.sampleCount, 1300);
assert.equal(semanticExecutionPath.segmentCount, 29);
assert.equal(semanticExecutionPath.lineExecutionTrace.length, 64);
assert.equal(semanticExecutionPath.axisValuesByLine.length, 29);
assert.equal(semanticExecutionPath.gcodeExecutionProcess.status, "ok");
assert.equal(semanticExecutionPath.gcodeExecutionProcess.executionStepCount, 128);
assert.equal(semanticExecutionPath.gcodeExecutionProcess.sourceLineCoverage.length, 65);
assert.equal(semanticExecutionPath.gcodeExecutionProcess.summary.motionStepCount, 29);
assert.equal(semanticExecutionPath.gcodeExecutionProcess.summary.parameterAssignmentStepCount, 41);
assert.deepEqual(semanticExecutionPath.samples.find((sample) => sample.motionType === "arc").machineState.cutting, {
active: true,
cuttingSpeedMmPerMin: 1000,
});
assert.equal(semanticExecutionPath.samples[0].machineState.spindle.speedRpm, 0);
assert.equal(semanticExecutionPath.samples[0].machineState.coolant.flood, false);
assert.equal(semanticExecutionPath.samples[0].machineState.toolChange.activeTool, 2);
assert.equal(semanticExecutionPath.gcodeExecutionProcess.executionSteps.some((step) => (
step.sourceFile === "helix_bc.ngc"
&& step.line === 17
&& step.result.operation === "feed-helix"
&& step.result.motion?.endJoint?.b === 20
&& step.result.motion?.endJoint?.c === 45
&& step.result.motion?.endJoint?.z === 5
&& step.result.machineStateAfter?.cutting?.active === true
&& step.result.machineStateAfter?.feed?.actualMmPerMin === 1000
)), true);
assert.equal(semanticExecutionPath.axisValuesByLine.some((entry) => (
entry.sourceFile === "helix_bc.ngc"
&& entry.line === 17
&& entry.operation === "feed-helix"
&& entry.joint.b === 20
&& entry.joint.c === 45
&& entry.joint.z === 5
&& entry.machineState.cutting.cuttingSpeedMmPerMin === 1000
)), true);
const interpreter = await createLinuxCncInterpreterRuntime();
const execution = interpreter.runMachineFileProgram({
plan: selectedPlan,
files: staged.save.files,
executionMode: "fiveAxisRemap",
});
assert.equal(execution.summary.machineFileExecutionReady, true);
assert.equal(execution.plannerTiming.plannerRuntimeReady, true);
assert.equal(execution.plannerTiming.samples.length > 0, true);
const taskHalWasm = await readFile(
new URL("../../../wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm", import.meta.url),
);
const taskHal = wrapTaskHalSdk(await createLinuxCncTaskHalSdk({
wasmBinary: taskHalWasm,
print() {},
printErr() {},
}));
const taskHalSession = buildTaskHalSessionFromMachineFiles({
profile,
plan: selectedPlan,
save: staged.save,
selectedProgramRel: selectedPlan.selectedProgramSourceRel,
});
taskHal.initSession(taskHalSession);
taskHal.stageFiles(taskHalSession.files);
taskHal.openProgram(taskHalSession.programPath);
taskHal.loadProgramMotionPlan(buildTaskHalProgramMotionPlan({
programPath: taskHalSession.programPath,
motion: execution.motion,
timing: execution.plannerTiming,
linearUnits: execution.plannerTiming.linearUnits || "mm",
programLines: staged.save.files
.find((file) => (file.wasmPath || file.path) === taskHalSession.programPath)
?.text.split(/\r?\n/) || [],
}));
taskHal.sendCommand({ type: "EMC_TASK_SET_STATE", state: "ON" });
taskHal.sendCommand({ type: "EMC_TASK_SET_MODE", mode: "AUTO" });
taskHal.sendCommand({ type: "EMC_TASK_PLAN_RUN", line: 0 });
taskHal.runCycles({ taskPeriodNs: 20000000, servoPeriodNs: 1000000, taskCycles: 3 });
const taskHalStatus = taskHal.readStatus();
assert.equal(taskHalStatus.summary.taskRuntimeReady, true);
assert.equal(taskHalStatus.summary.halSyncReady, true);
assert.equal(taskHalStatus.ui.activeLine >= 1, true);
assert.equal(Number.isFinite(taskHalStatus.motionStatus.axis.x), true);
assert.equal(Number.isFinite(taskHalStatus.motionStatus.axis.y), true);
assert.equal(Number.isFinite(taskHalStatus.motionStatus.axis.z), true);
const store = createSimulationStore();
const state = store.getState();
assert.equal(state.machineProfile, "xyzbc-trt");
assert.equal(state.profile.id, "xyzbc-trt");
assert.equal(state.sessionName, "xyzbc-trt-web-session");
assert.equal(state.kinematicsRuntimeReadiness, null);
const storeStage = await store.stageMachineFiles({ storage: createMemorySessionStorage() });
assert.equal(store.getState().toolRuntimeState.ready, true);
assert.equal(store.getState().toolRuntimeState.currentTool.toolNumber, 0);
store.dispatch({
type: "LOAD_LINUXCNC_GCODE_SOURCE",
sourceRel: "configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc",
});
assert.equal(store.getState().toolRuntimeState.activeToolNumber, 0);
assert.equal(store.getState().programAxisPreviewPath.source, "web-axis-source-execution-expanded-ngcgui-subroutines");
assert.equal(store.getState().programAxisPreviewPath.semanticBoundary, "linuxcnc_xyzbc_switchkins_ngc_execution_expanded_by_source_subroutines");
assert.equal(store.getState().programAxisPreviewPath.gcodeExecutionProcess.executionStepCount, 128);
assert.equal(store.getState().programAxisPreviewPath.gcodeExecutionProcess.summary.motionStepCount, 29);
assert.equal(store.getState().programAxisPreviewPath.gcodeExecutionProcess.summary.parameterAssignmentStepCount, 41);
assert.equal(store.getState().programAxisPreviewPath.samples[0].tool.id, 2);
assert.equal(store.getState().programAxisPreviewPath.samples[0].tool.diameter, 8);
assert.equal(store.getState().programAxisPreviewPath.samples[0].sourceFile, "xyzbc_switchkins_sub.ngc");
assert.equal(store.getState().programAxisPreviewPath.samples[0].statement.includes("g53 g0"), true);
store.dispatch({ type: "RUN_READY" });
assert.equal(store.getState().machine.powerOn, false);
assert.equal(store.getState().machine.allHomed, false);
assert.equal(store.getState().machine.mode, "manual");
assert.equal(store.getState().operatorMessage, "RUN setup ready: reset ESTOP, power on, Home All, then Run");
await store.dispatch({ type: "RUN_FROM_OPERATOR" });
assert.equal(store.getState().runState, "idle");
assert.equal(store.getState().operatorMessage, "run blocked: machine must be on");
store.dispatch({ type: "TOGGLE_POWER" });
await store.dispatch({ type: "RUN_FROM_OPERATOR" });
assert.equal(store.getState().runState, "idle");
assert.equal(store.getState().operatorMessage, "run blocked: home machine first");
store.dispatch({ type: "HOME" });
await store.dispatch({ type: "RUN_FROM_OPERATOR" });
const liveRunState = store.getState();
assert.equal(liveRunState.programUiExecution.source, "programAxisPreviewPath.samples");
assert.equal(liveRunState.programUiExecution.samplePeriodMs, 50);
assert.equal(liveRunState.programUiExecution.executedSampleCount, liveRunState.programExecutionSampleIndex + 1);
assert.equal(liveRunState.programUiExecution.sourceFile, liveRunState.programRuntimeFeedback.sourceFile);
assert.equal(liveRunState.programUiExecution.line, liveRunState.programRuntimeFeedback.line);
assert.equal(liveRunState.programUiExecution.gcodeStepIndex === null, false);
assert.equal(liveRunState.programUiExecution.semanticBoundary, "ui_updates_from_real_expanded_linuxcnc_gcode_sample_stream");
assert.deepEqual(liveRunState.toolAxisVector, {
x: liveRunState.programUiExecution.toolAxis.i,
y: liveRunState.programUiExecution.toolAxis.j,
z: liveRunState.programUiExecution.toolAxis.k,
});
assert.deepEqual(liveRunState.programRuntimeFeedback.tcp, liveRunState.programUiExecution.tcp);
assert.notDeepEqual(liveRunState.programRuntimeFeedback.tcp, liveRunState.programRuntimeFeedback.axisPose);
assert.equal(liveRunState.linuxCncProcessMonitor.path.uiExecution.sourceFile, liveRunState.programUiExecution.sourceFile);
assert.equal(liveRunState.linuxCncProcessMonitor.path.activeGcode, liveRunState.programUiExecution.statement);
assert.equal(store.getState().linuxCncProcessMonitor.toolChange.activeToolNumber, 2);
assert.equal(store.getState().linuxCncProcessMonitor.toolChange.diameter, 8);
assert.equal(store.getState().linuxCncProcessMonitor.sourceReferences.task, "linuxcnc/src/emc/task/emctaskmain.cc");
const toolOffsetDb = applyToolCommandSequence(store.getState().toolDbSimulation, [
{ code: "T", toolNumber: 2 },
{ code: "M6" },
{ code: "G43", h: 2 },
]);
const toolRuntimeState = createToolRuntimeState(toolOffsetDb, {
fallbackToolLength: state.toolPreview.length,
});
assert.equal(toolRuntimeState.ready, true);
assert.equal(toolRuntimeState.activeOffsetApplied, true);
assert.equal(toolRuntimeState.activeToolNumber, 2);
assert.equal(toolRuntimeState.activePocket, 2);
assert.equal(toolRuntimeState.currentTool.offset.z, 10);
assert.equal(toolRuntimeState.currentTool.diameter, 8);
assert.equal(toolRuntimeState.kinematics.toolOffsetZ, 10);
assert.equal(toolRuntimeState.kinematics.halPin, "motion.tooloffset.z");
assert.deepEqual(toolRuntimeState.kinematics.downstreamPins, [
"xyzbc-trt-kins.tool-offset",
"xyzbc-trt-gui.tool-offset",
]);
assert.equal(toolRuntimeState.pathTool.id, 2);
assert.equal(toolRuntimeState.pathTool.pocket, 2);
assert.equal(toolRuntimeState.pathTool.length, 10);
assert.equal(toolRuntimeState.pathTool.diameter, 8);
assert.equal(toolRuntimeState.vismach.toolOffset, 10);
const vismachState = buildVismachModelState({
...state,
axisPose: { x: 12.5, y: -6.25, z: 3.75, b: 35, c: -90 },
toolPreview: { ...state.toolPreview, length: 84.019 },
toolRuntimeState,
});
assert.deepEqual(Object.keys(vismachState.pins), XYZBC_TRT_VISMACH_PINS);
assert.equal(vismachState.pins["table-x"], 12.5);
assert.equal(vismachState.pins["saddle-y"], -6.25);
assert.equal(vismachState.pins["spindle-z"], 3.75);
assert.equal(vismachState.pins["tilt-b"], 35);
assert.equal(vismachState.pins["rotate-c"], -90);
assert.equal(vismachState.pins["x-offset"], -20);
assert.equal(vismachState.pins["z-offset"], -15);
assert.equal(vismachState.pins["tool-offset"], 10);
assert.deepEqual(vismachState.transforms.table.sourcePins, ["table-x"]);
assert.deepEqual(vismachState.transforms.saddle.sourcePins, ["saddle-y"]);
assert.deepEqual(vismachState.transforms.spindle.sourcePins, ["spindle-z"]);
assert.deepEqual(vismachState.transforms.tilt.sourcePins, ["tilt-b"]);
assert.deepEqual(vismachState.transforms.rotary.sourcePins, ["rotate-c"]);
assert.deepEqual(vismachState.transforms.tool.sourcePins, ["tool-offset", "x-offset", "z-offset"]);
assert.equal(vismachState.transforms.tool.translate.x, -20);
assert.equal(vismachState.transforms.tool.translate.z, -25);
assert.equal(vismachState.halNets.length, 9);
assert.equal(vismachState.semanticBoundary, "web_threejs_vismach_equivalent_driven_by_xyzbc_trt_hal_pins");
const pausedVismachState = buildVismachModelState({
...state,
runState: "paused",
machine: { ...state.machine, interpState: "paused", taskPaused: true },
axisPose: { x: 12.5, y: -6.25, z: 3.75, b: 35, c: -90 },
programRuntimeFeedback: {
axisPose: { x: 99, y: 88, z: 77, b: 66, c: 55 },
},
});
assert.equal(pausedVismachState.pins["table-x"], 12.5);
assert.equal(pausedVismachState.pins["saddle-y"], -6.25);
assert.equal(pausedVismachState.pins["spindle-z"], 3.75);
assert.equal(pausedVismachState.pins["tilt-b"], 35);
assert.equal(pausedVismachState.pins["rotate-c"], -90);
const stagedState = store.getState();
assert.equal(storeStage.save.profileId, "xyzbc-trt");
assert.equal(stagedState.machineFileStaging.profileId, "xyzbc-trt");
assert.ok(stagedState.machineProject.projectRoot.startsWith("web-rtcp-5axis-xyzbc-trt-sim-plan/machines/xyzbc-trt"));
assert.ok(stagedState.machineFileStaging.gcodeSources.some((source) => source.filename === "xyzbc_switchkins.ngc"));
assert.equal(AXIS_BUTTON_PARITY.length >= 50, true);
for (const action of [
"estop",
"power",
"run-ready",
"run",
"pause",
"resume",
"pause-resume",
"step",
"stop",
"home-all",
"jog-plus",
"jog-minus",
"spindle-reverse",
"spindle-stop",
"spindle-forward",
"kins-identity",
"kins-tcp",
"kins-userk",
"clear-preview",
]) {
assert.ok(AXIS_BUTTON_PARITY.some((item) => item.action === action && item.sourceSymbol && item.sourceLines && item.expected), action);
}
const buttonStore = createSimulationStore();
buttonStore.dispatch({ type: "ESTOP" });
assert.equal(buttonStore.getState().machine.estopActive, true);
buttonStore.dispatch({ type: "TOGGLE_POWER" });
assert.equal(buttonStore.getState().machine.taskState, "off");
assert.equal(buttonStore.getState().machine.powerOn, false);
buttonStore.dispatch({ type: "TOGGLE_POWER" });
assert.equal(buttonStore.getState().machine.taskState, "off");
assert.equal(buttonStore.getState().machine.powerOn, false);
await buttonStore.dispatch({ type: "RUN_FROM_OPERATOR" });
assert.equal(buttonStore.getState().runState, "powered-off");
assert.equal(buttonStore.getState().operatorMessage, "run blocked: machine must be on");
buttonStore.dispatch({ type: "ESTOP" });
buttonStore.dispatch({ type: "RESET" });
assert.equal(buttonStore.getState().machine.taskState, "estop-reset");
await buttonStore.dispatch({ type: "RUN_FROM_OPERATOR" });
assert.equal(buttonStore.getState().runState, "idle");
assert.equal(buttonStore.getState().operatorMessage, "run blocked: machine must be on");
buttonStore.dispatch({ type: "TOGGLE_POWER" });
assert.equal(buttonStore.getState().machine.powerOn, true);
await buttonStore.dispatch({ type: "RUN_FROM_OPERATOR" });
assert.equal(buttonStore.getState().runState, "idle");
assert.equal(buttonStore.getState().operatorMessage, "run blocked: home machine first");
buttonStore.dispatch({ type: "HOME" });
assert.equal(buttonStore.getState().machine.allHomed, true);
assert.equal(buttonStore.getState().linuxCncProcessMonitor.axes.joint.x, 0);
assert.equal(buttonStore.getState().linuxCncProcessMonitor.axes.joint.y, 0);
assert.equal(buttonStore.getState().linuxCncProcessMonitor.axes.joint.z, 10);
await buttonStore.dispatch({ type: "RUN_FROM_OPERATOR" });
assert.equal(buttonStore.getState().machine.mode, "auto");
assert.equal(["running", "complete"].includes(buttonStore.getState().runState), true);
assert.equal(Boolean(buttonStore.getState().programRuntimeFeedback), true);
if (buttonStore.getState().runState === "running") {
buttonStore.dispatch({ type: "PAUSE_RESUME" });
assert.equal(buttonStore.getState().runState, "paused");
assert.equal(buttonStore.getState().machine.interpState, "paused");
assert.equal(buttonStore.getState().feed.currentVelocity, 0);
buttonStore.dispatch({ type: "PAUSE_RESUME" });
assert.equal(buttonStore.getState().runState, "running");
assert.equal(buttonStore.getState().machine.interpState, "reading");
buttonStore.dispatch({ type: "PAUSE_RESUME" });
assert.equal(buttonStore.getState().runState, "paused");
assert.equal(buttonStore.getState().machine.interpState, "paused");
assert.equal(buttonStore.getState().feed.currentVelocity, 0);
const beforeStepSample = buttonStore.getState().programExecutionSampleIndex;
buttonStore.dispatch({ type: "STEP" });
assert.equal(buttonStore.getState().runState, "stepping");
assert.equal(buttonStore.getState().machine.interpState, "paused");
assert.equal(buttonStore.getState().programExecutionSampleIndex > beforeStepSample, true);
buttonStore.dispatch({ type: "RESUME" });
assert.equal(buttonStore.getState().runState, "running");
assert.equal(buttonStore.getState().machine.interpState, "reading");
buttonStore.dispatch({ type: "PAUSE" });
assert.equal(buttonStore.getState().runState, "paused");
assert.equal(buttonStore.getState().machine.interpState, "paused");
assert.equal(buttonStore.getState().feed.currentVelocity, 0);
buttonStore.dispatch({ type: "RESUME" });
assert.equal(buttonStore.getState().runState, "running");
assert.equal(buttonStore.getState().machine.interpState, "reading");
}
buttonStore.dispatch({ type: "STOP" });
buttonStore.dispatch({ type: "TOGGLE_POWER" });
assert.equal(buttonStore.getState().machine.taskState, "off");
assert.equal(buttonStore.getState().machine.powerOn, false);
assert.equal(buttonStore.getState().machine.allHomed, false);
buttonStore.dispatch({ type: "TOGGLE_POWER" });
assert.equal(buttonStore.getState().machine.taskState, "off");
assert.equal(buttonStore.getState().machine.powerOn, false);
const idleInterpRunningStore = createSimulationStore({
runState: "running",
machine: {
powerOn: true,
estopActive: false,
taskState: "on",
mode: "auto",
interpState: "idle",
interpResumeState: "reading",
taskPaused: false,
allHomed: true,
noForceHoming: false,
},
});
idleInterpRunningStore.dispatch({ type: "PAUSE" });
assert.equal(idleInterpRunningStore.getState().runState, "running");
assert.equal(idleInterpRunningStore.getState().machine.interpState, "idle");
assert.equal(idleInterpRunningStore.getState().machine.taskPaused, false);
assert.equal(idleInterpRunningStore.getState().operatorMessage, "pause blocked: interpreter is not running");
idleInterpRunningStore.dispatch({ type: "PAUSE_RESUME" });
assert.equal(idleInterpRunningStore.getState().runState, "running");
assert.equal(idleInterpRunningStore.getState().machine.interpState, "idle");
assert.equal(idleInterpRunningStore.getState().operatorMessage, "pause ignored: interpreter is idle");
const staleManualRunningStore = createSimulationStore({
runState: "running",
machine: {
powerOn: true,
estopActive: false,
taskState: "on",
mode: "manual",
interpState: "reading",
interpResumeState: "reading",
taskPaused: false,
allHomed: true,
noForceHoming: false,
},
});
staleManualRunningStore.dispatch({ type: "PAUSE_RESUME" });
assert.equal(staleManualRunningStore.getState().runState, "running");
assert.equal(staleManualRunningStore.getState().machine.mode, "manual");
assert.equal(staleManualRunningStore.getState().machine.interpState, "reading");
assert.equal(staleManualRunningStore.getState().machine.taskPaused, false);
assert.equal(staleManualRunningStore.getState().operatorMessage, "pause blocked: task mode must be auto or MDI");
assert.equal(staleManualRunningStore.getState().taskHalPauseLock, null);
const lockedPauseState = staleManualRunningStore.getState();
staleManualRunningStore.dispatch({
type: "TASK_HAL_STATUS_APPLIED",
operatorMessage: "simulated stale running task/HAL status",
status: {
taskRuntimeReady: true,
taskCommandsDriveMotionRuntime: true,
task: {
state: "ON",
mode: "AUTO",
interpState: "READING",
execState: "EXEC",
taskCycle: 123,
},
motionStatus: {
cycle: 25,
motionHalSyncReady: true,
motion: {
programLine: Number(lockedPauseState.activeLine || 1) + 5,
currentVel: 12,
requestedVel: 12,
inPosition: false,
},
axis: {
x: 99,
y: 88,
z: 77,
b: 66,
c: 55,
},
},
halSnapshot: {
ready: true,
pins: {
"motion.program-line": { value: Number(lockedPauseState.activeLine || 1) + 5 },
},
},
},
});
assert.equal(staleManualRunningStore.getState().runState, "running");
assert.equal(staleManualRunningStore.getState().machine.mode, "manual");
assert.equal(staleManualRunningStore.getState().machine.interpState, "reading");
assert.equal(staleManualRunningStore.getState().taskHalPauseLock, null);
const controlStore = createSimulationStore();
controlStore.dispatch({ type: "ESTOP" });
controlStore.dispatch({ type: "RESET" });
controlStore.dispatch({ type: "TOGGLE_POWER" });
controlStore.dispatch({ type: "HOME" });
controlStore.dispatch({ type: "SET_MODE", mode: "manual" });
controlStore.dispatch({ type: "SET_MODE", mode: "mdi" });
controlStore.dispatch({ type: "RUN_MDI", command: "M428" });
assert.equal(controlStore.getState().kinsType, "tcp-xyzbc");
assert.equal(controlStore.getState().linuxCncProcessMonitor.axes.rtcpState, "on");
controlStore.dispatch({ type: "RUN_MDI", command: "M429" });
assert.equal(controlStore.getState().kinsType, "identity");
controlStore.dispatch({ type: "RUN_MDI", command: "M430" });
assert.equal(controlStore.getState().kinsType, "userk");
controlStore.dispatch({ type: "SET_MODE", mode: "manual" });
controlStore.dispatch({ type: "SET_ACTIVE_JOINT", joint: 4 });
const cBeforeJog = controlStore.getState().axisPose.c;
controlStore.dispatch({ type: "JOG", axis: "c", direction: 1, increment: 1 });
assert.equal(controlStore.getState().axisPose.c, cBeforeJog + 1);
controlStore.dispatch({ type: "RUN_MDI", command: "G10 L20 P0 C0", manualTouchOff: true });
assert.equal(controlStore.getState().mdiHistory[0], "G10 L20 P0 C0");
assert.equal(controlStore.getState().machine.mode, "manual");
assert.equal(controlStore.getState().runState, "idle");
assert.equal(controlStore.getState().operatorMessage, "manual touch off G10 L20 P0 C0");
controlStore.dispatch({ type: "RUN_MDI", command: "G43", manualTouchOff: true });
assert.equal(controlStore.getState().mdiHistory[0], "G43");
assert.equal(controlStore.getState().machine.mode, "manual");
assert.equal(controlStore.getState().runState, "idle");
const feedBefore = controlStore.getState().feed.feedOverride;
controlStore.dispatch({ type: "ADJUST_OVERRIDE", target: "feed", delta: 10 });
assert.equal(controlStore.getState().feed.feedOverride, feedBefore + 10);
assert.equal(controlStore.getState().linuxCncProcessMonitor.feed.feedOverridePercent, feedBefore + 10);
controlStore.dispatch({ type: "SET_SPINDLE_DIRECTION", direction: "reverse" });
assert.equal(controlStore.getState().spindle.direction, "reverse");
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.enabled, true);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.on, 1);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.forward, 0);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.reverse, 1);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.speedOut, 1600);
controlStore.dispatch({ type: "SET_SPINDLE_DIRECTION", direction: "stop" });
assert.equal(controlStore.getState().spindle.direction, "stop");
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.enabled, false);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.on, 0);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.forward, 0);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.reverse, 0);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.speedOut, 0);
controlStore.dispatch({ type: "SET_SPINDLE_DIRECTION", direction: "forward" });
assert.equal(controlStore.getState().spindle.direction, "forward");
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.enabled, true);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.actualRpm, 1600);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.on, 1);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.forward, 1);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.reverse, 0);
assert.equal(controlStore.getState().linuxCncProcessMonitor.spindle.halPins.speedOut, 1600);
controlStore.dispatch({ type: "TOGGLE_COOLANT", kind: "flood" });
assert.equal(controlStore.getState().coolant.flood, true);
assert.equal(controlStore.getState().linuxCncProcessMonitor.coolant.flood, true);
controlStore.dispatch({ type: "SET_BLOCK_DELETE", enabled: true });
assert.equal(controlStore.getState().gmoccapyGui.optionalBlocks, true);
controlStore.dispatch({ type: "SET_OPTIONAL_STOP", enabled: true });
assert.equal(controlStore.getState().gmoccapyGui.optionalStop, true);
controlStore.dispatch({ type: "SET_IGNORE_LIMITS", enabled: true });
assert.equal(controlStore.getState().gmoccapyGui.ignoreLimits, true);
controlStore.dispatch({ type: "SET_VIEW", view: "x" });
assert.equal(controlStore.getState().preview.selectedView, "x");
controlStore.dispatch({ type: "CLEAR_PREVIEW" });
assert.equal(controlStore.getState().preview.pathPoints, 0);
const stagingPlan = await createMachineFileStagingPlan({ profile });
assert.equal(stagingPlan.summary.remapFileCount >= 3, true);
assert.equal(stagingPlan.files.some((file) => file.sourceRel.endsWith("xyzbc.var")), true);
console.log("xyzbc_trt_web_app_smoke=ok");