接入 LinuxCNC TP 运行反馈
This commit is contained in:
@@ -6,12 +6,34 @@
|
||||
</head>
|
||||
<body>
|
||||
<pre id="result">gmoccapy_shell_smoke=pending</pre>
|
||||
<iframe id="app-frame" src="../../app/index.html" title="gmoccapy shell"></iframe>
|
||||
<iframe id="app-frame" title="gmoccapy shell"></iframe>
|
||||
<script type="module">
|
||||
const result = document.querySelector("#result");
|
||||
const frame = document.querySelector("#app-frame");
|
||||
const appSrc = new URLSearchParams(window.location.search).get("app") || "../../app/index.html";
|
||||
frame.src = appSrc;
|
||||
|
||||
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
async function waitForInterpreterExecution(win) {
|
||||
for (let attempt = 0; attempt < 40; attempt += 1) {
|
||||
const state = win.webRtcp5AxisSimulation.getState();
|
||||
if (!state.interpreterExecutionPending && state.programExecutionSourceMode === "linuxcnc-interpreter-wasm") {
|
||||
return state;
|
||||
}
|
||||
await wait(25);
|
||||
}
|
||||
return win.webRtcp5AxisSimulation.getState();
|
||||
}
|
||||
async function waitForMachineFileExecution(win) {
|
||||
for (let attempt = 0; attempt < 60; attempt += 1) {
|
||||
const state = win.webRtcp5AxisSimulation.getState();
|
||||
if (!state.interpreterExecutionPending && state.machineFileExecution) {
|
||||
return state;
|
||||
}
|
||||
await wait(25);
|
||||
}
|
||||
return win.webRtcp5AxisSimulation.getState();
|
||||
}
|
||||
|
||||
async function runSmoke() {
|
||||
await new Promise((resolve, reject) => {
|
||||
@@ -89,25 +111,84 @@
|
||||
throw new Error(`forbidden frontend framework dependency detected: ${forbidden.join(", ")}`);
|
||||
}
|
||||
|
||||
const runtimeReadiness = await win.webRtcp5AxisSimulation.kinematicsRuntimeReady;
|
||||
if (runtimeReadiness.loaded !== true || runtimeReadiness.moduleId !== "xyzac-trt") {
|
||||
throw new Error(`LinuxCNC kinematics runtime did not load in browser: ${JSON.stringify(runtimeReadiness)}`);
|
||||
}
|
||||
if (runtimeReadiness.executionContext !== "worker") {
|
||||
throw new Error(`LinuxCNC kinematics runtime should run in worker: ${JSON.stringify(runtimeReadiness)}`);
|
||||
}
|
||||
const interpreterReadiness = await win.webRtcp5AxisSimulation.interpreterRuntimeReady;
|
||||
if (interpreterReadiness.loaded !== true || interpreterReadiness.runProgramReady !== true) {
|
||||
throw new Error(`LinuxCNC interpreter runtime did not load in browser: ${JSON.stringify(interpreterReadiness)}`);
|
||||
}
|
||||
if (interpreterReadiness.executionContext !== "worker") {
|
||||
throw new Error(`LinuxCNC interpreter runtime should run in worker: ${JSON.stringify(interpreterReadiness)}`);
|
||||
}
|
||||
if (
|
||||
interpreterReadiness.plannerRuntimeReady !== true ||
|
||||
interpreterReadiness.plannerSemanticBoundary !== "linuxcnc_tp_queue_runtime_timing_from_canonical_motion"
|
||||
) {
|
||||
throw new Error(`LinuxCNC TP planner runtime did not load in browser worker: ${JSON.stringify(interpreterReadiness)}`);
|
||||
}
|
||||
const state = win.webRtcp5AxisSimulation.getState();
|
||||
if (state.sourceMode !== "fixture-ui-only") {
|
||||
if (state.sourceMode !== "source-derived-kinematics-wasm") {
|
||||
throw new Error(`unexpected source mode: ${state.sourceMode}`);
|
||||
}
|
||||
if (state.machineProfile !== "xyzac-trt") {
|
||||
throw new Error(`unexpected profile: ${state.machineProfile}`);
|
||||
if (state.kinematicsExecutionContext !== "worker") {
|
||||
throw new Error(`unexpected kinematics execution context: ${state.kinematicsExecutionContext}`);
|
||||
}
|
||||
const profileSelector = doc.querySelector('[data-action="select-profile"]');
|
||||
if (!profileSelector || profileSelector.options.length < 2) {
|
||||
throw new Error("missing five-axis profile selector");
|
||||
}
|
||||
profileSelector.value = "xyzbc-trt";
|
||||
profileSelector.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await wait(500);
|
||||
await win.webRtcp5AxisSimulation.refreshKinematicsFrame({ operatorMessage: "browser smoke refreshed xyzbc frame" });
|
||||
const xyzbcState = win.webRtcp5AxisSimulation.getState();
|
||||
if (xyzbcState.machineProfile !== "xyzbc-trt" || xyzbcState.profile.traj.coordinates !== "XYZBC") {
|
||||
throw new Error("profile selector did not switch to XYZBC");
|
||||
}
|
||||
if (xyzbcState.kinematicsRuntimeReadiness?.moduleId !== "xyzbc-trt") {
|
||||
throw new Error(`XYZBC kinematics runtime did not load: ${JSON.stringify(xyzbcState.kinematicsRuntimeReadiness)}`);
|
||||
}
|
||||
const restoredProfileSelector = doc.querySelector('[data-action="select-profile"]');
|
||||
restoredProfileSelector.value = "xyzac-trt";
|
||||
restoredProfileSelector.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await wait(500);
|
||||
await win.webRtcp5AxisSimulation.refreshKinematicsFrame({ operatorMessage: "browser smoke restored xyzac frame" });
|
||||
const restoredProfileState = win.webRtcp5AxisSimulation.getState();
|
||||
if (restoredProfileState.machineProfile !== "xyzac-trt") {
|
||||
throw new Error(`unexpected profile: ${restoredProfileState.machineProfile}`);
|
||||
}
|
||||
if (state.rtcpFrame?.apiName !== "web-rtcp-5axis-motion-frame") {
|
||||
throw new Error("missing RTCP frame state");
|
||||
}
|
||||
if (state.rtcpFrame?.readiness?.linuxCncKinematicsReady !== false) {
|
||||
throw new Error("fixture RTCP frame must not claim LinuxCNC kinematics readiness");
|
||||
if (state.rtcpFrame?.readiness?.linuxCncKinematicsReady !== true) {
|
||||
throw new Error("browser RTCP frame must use LinuxCNC kinematics readiness");
|
||||
}
|
||||
if (!doc.querySelector('[data-rtcp-value="tcp"]')?.textContent.includes("TCP")) {
|
||||
throw new Error("missing TCP DRO strip");
|
||||
}
|
||||
if (!doc.querySelector('[data-rtcp-diagnostic="boundary"]')?.textContent.includes("fixture_frame_ui_plumbing")) {
|
||||
if (!doc.querySelector('[data-rtcp-diagnostic="boundary"]')?.textContent.includes("linuxcnc_kinematics_wasm_c_abi")) {
|
||||
throw new Error("missing RTCP boundary diagnostic");
|
||||
}
|
||||
if (!doc.querySelector('[data-linuxcnc-ini="status"]')?.textContent.includes("xyzac-trt.ini")) {
|
||||
throw new Error("missing LinuxCNC INI status diagnostic");
|
||||
}
|
||||
if (!doc.querySelector('[data-linuxcnc-ini="kins"]')?.textContent.includes("xyzac-trt-kins")) {
|
||||
throw new Error("missing LinuxCNC INI kinematics diagnostic");
|
||||
}
|
||||
if (!doc.querySelector('[data-linuxcnc-ini="limits"]')?.textContent.includes("X[-200,200]")) {
|
||||
throw new Error("missing LinuxCNC INI axis limits diagnostic");
|
||||
}
|
||||
if (!doc.querySelector('[data-linuxcnc-task-policy="boundary"]')?.textContent.includes("linuxcnc_task_state_mode_command_gate")) {
|
||||
throw new Error("missing LinuxCNC task policy boundary diagnostic");
|
||||
}
|
||||
if (!doc.querySelector('[data-linuxcnc-task-policy="source"]')?.textContent.includes("linuxcnc/src/emc/task/emctaskmain.cc")) {
|
||||
throw new Error("missing LinuxCNC emctaskmain source diagnostic");
|
||||
}
|
||||
if (!doc.querySelector('[data-tool-preview="summary"]')?.textContent.includes("T1")) {
|
||||
throw new Error("missing tool preview summary");
|
||||
}
|
||||
@@ -131,11 +212,19 @@
|
||||
if (!doc.querySelector('[data-machine-state="summary"]')?.textContent.includes("power on")) {
|
||||
throw new Error("machine state did not render power on");
|
||||
}
|
||||
doc.querySelector('[data-action="HOME"]').click();
|
||||
await wait(50);
|
||||
doc.querySelector('[data-action="mode-auto"]').click();
|
||||
await wait(50);
|
||||
if (!doc.querySelector('[data-linuxcnc-task-policy="gates"]')?.textContent.includes("auto")) {
|
||||
throw new Error("LinuxCNC task policy did not expose auto run gate");
|
||||
}
|
||||
|
||||
win.webRtcp5AxisSimulation.dispatch({
|
||||
type: "LOAD_PROGRAM",
|
||||
filename: "operator-demo.ngc",
|
||||
content: [
|
||||
"G90 G17",
|
||||
"G0 X0 Y0 Z0",
|
||||
"G1 X10 F100",
|
||||
"G1 Y10",
|
||||
@@ -143,18 +232,154 @@
|
||||
"G1 Y0",
|
||||
"G0 Z5",
|
||||
"M5",
|
||||
"M30",
|
||||
"M2",
|
||||
].join("\n"),
|
||||
});
|
||||
await wait(50);
|
||||
await waitForInterpreterExecution(win);
|
||||
if (win.webRtcp5AxisSimulation.getState().activeProgram !== "operator-demo.ngc") {
|
||||
throw new Error("LOAD_PROGRAM did not update active program");
|
||||
}
|
||||
if (doc.querySelector('[data-active-program-line]')?.textContent !== "Current line 1") {
|
||||
throw new Error("loaded program did not render current line 1");
|
||||
if (win.webRtcp5AxisSimulation.getState().programExecutionSourceMode !== "linuxcnc-interpreter-wasm") {
|
||||
throw new Error("LOAD_PROGRAM did not execute through LinuxCNC interpreter WASM");
|
||||
}
|
||||
if (doc.querySelector(".gcode-row.active")?.dataset.programLine !== "1") {
|
||||
throw new Error("loaded program active row should be line 1");
|
||||
if (win.webRtcp5AxisSimulation.getState().programExecution?.summary?.motionEventCount < 5) {
|
||||
throw new Error("LinuxCNC interpreter execution did not produce motion events");
|
||||
}
|
||||
if (win.webRtcp5AxisSimulation.getState().programExecution?.summary?.plannerRuntimeReady !== true) {
|
||||
throw new Error("LinuxCNC TP planner timing was not ready for operator program");
|
||||
}
|
||||
if (win.webRtcp5AxisSimulation.getState().programExecutionTiming?.semanticBoundary !== "linuxcnc_tp_queue_runtime_timing_from_canonical_motion") {
|
||||
throw new Error(`program timing did not use LinuxCNC TP queue runtime: ${JSON.stringify(win.webRtcp5AxisSimulation.getState().programExecutionTiming)}`);
|
||||
}
|
||||
if (win.webRtcp5AxisSimulation.getState().programExecutionTiming?.sampleCount < 2) {
|
||||
throw new Error("LinuxCNC TP runtime feedback samples were not exposed");
|
||||
}
|
||||
if (win.webRtcp5AxisSimulation.getState().programRuntimeFeedback?.semanticBoundary !== "linuxcnc_tp_run_cycle_feedback_without_hardware") {
|
||||
throw new Error(`initial runtime feedback did not use LinuxCNC TP sample: ${JSON.stringify(win.webRtcp5AxisSimulation.getState().programRuntimeFeedback)}`);
|
||||
}
|
||||
if (!doc.querySelector('[data-program-execution-source]')?.textContent.includes("linuxcnc-interpreter-wasm")) {
|
||||
throw new Error("program execution source DOM did not render interpreter source");
|
||||
}
|
||||
if (!doc.querySelector('[data-program-execution-summary]')?.textContent.includes("motion")) {
|
||||
throw new Error("program execution summary DOM did not render motion count");
|
||||
}
|
||||
if (!doc.querySelector('[data-program-timing="summary"]')?.textContent.match(/\\d+:\\d/)) {
|
||||
throw new Error("program timing summary DOM did not render estimated execution time");
|
||||
}
|
||||
if (!doc.querySelector('[data-program-timing="segment"]')?.textContent.includes("mm/min")) {
|
||||
throw new Error("program timing segment DOM did not render segment velocity");
|
||||
}
|
||||
if (!doc.querySelector('[data-program-runtime-feedback="source"]')?.textContent.includes("linuxcnc-tp-runtime-sample")) {
|
||||
throw new Error("program runtime feedback DOM did not render LinuxCNC TP sample source");
|
||||
}
|
||||
if (!doc.querySelector('[data-program-runtime-feedback="dtg"]')?.textContent.includes("DTG")) {
|
||||
throw new Error("program runtime feedback DOM did not render DTG");
|
||||
}
|
||||
if (!doc.querySelector('[data-program-switchkins-summary]')?.textContent.includes("0 events")) {
|
||||
throw new Error("program switchkins summary DOM did not render zero-event state");
|
||||
}
|
||||
win.webRtcp5AxisSimulation.dispatch({
|
||||
type: "LOAD_PROGRAM",
|
||||
filename: "operator-arc-demo.ngc",
|
||||
content: [
|
||||
"G90 G17",
|
||||
"G0 X1 Y0 Z0",
|
||||
"G2 X0 Y1 I-1 J0 F60",
|
||||
"M2",
|
||||
].join("\n"),
|
||||
});
|
||||
await waitForInterpreterExecution(win);
|
||||
const arcState = win.webRtcp5AxisSimulation.getState();
|
||||
if (!arcState.programExecution?.summary?.motionTypes?.includes("ARC_FEED")) {
|
||||
throw new Error("LinuxCNC interpreter did not produce ARC_FEED for browser arc program");
|
||||
}
|
||||
if (arcState.programExecution?.summary?.plannerRuntimeReady !== true) {
|
||||
throw new Error("LinuxCNC TP planner timing was not ready for browser arc program");
|
||||
}
|
||||
const arcSegment = arcState.programExecutionTiming?.segments?.find((segment) => segment.type === "ARC_FEED");
|
||||
if (!arcSegment || arcSegment.durationSeconds <= 1.4) {
|
||||
throw new Error(`LinuxCNC TP arc timing segment missing or too short: ${JSON.stringify(arcState.programExecutionTiming)}`);
|
||||
}
|
||||
const stagedMachineFiles = await win.webRtcp5AxisSimulation.stageMachineFiles();
|
||||
await wait(50);
|
||||
if (stagedMachineFiles.save.status !== "saved" || stagedMachineFiles.save.fileCount < 5) {
|
||||
throw new Error(`machine file staging did not save files: ${JSON.stringify(stagedMachineFiles.save)}`);
|
||||
}
|
||||
if (!stagedMachineFiles.save.files.some((file) => file.sourceRel.endsWith("remap_subs/428remap.ngc"))) {
|
||||
throw new Error("machine file staging did not include M428 remap");
|
||||
}
|
||||
if (!doc.querySelector('[data-machine-file-staging="status"]')?.textContent.includes("staged")) {
|
||||
throw new Error("machine file staging status did not render");
|
||||
}
|
||||
const sourceSelect = doc.querySelector('[data-action="select-linuxcnc-gcode-source"]');
|
||||
if (!sourceSelect || sourceSelect.options.length < 4) {
|
||||
throw new Error("LinuxCNC 5-axis G-code source selector did not render staged demos");
|
||||
}
|
||||
const impellerSource = "configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/impeller-7bl-xyzac.ngc";
|
||||
sourceSelect.value = impellerSource;
|
||||
sourceSelect.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await waitForInterpreterExecution(win);
|
||||
const linuxCncSourceState = win.webRtcp5AxisSimulation.getState();
|
||||
if (linuxCncSourceState.programSource !== "linuxcnc-vendored-5axis-gcode") {
|
||||
throw new Error(`LinuxCNC source program was not loaded: ${linuxCncSourceState.programSource}`);
|
||||
}
|
||||
if (!linuxCncSourceState.activeProgram.endsWith("impeller-7bl-xyzac.ngc")) {
|
||||
throw new Error(`unexpected LinuxCNC source program: ${linuxCncSourceState.activeProgram}`);
|
||||
}
|
||||
if (!doc.querySelector('[data-linuxcnc-gcode-source="selected"]')?.textContent.includes("impeller-7bl-xyzac.ngc")) {
|
||||
throw new Error("selected LinuxCNC G-code source did not render");
|
||||
}
|
||||
if (!doc.querySelector('[data-program-source]')?.textContent.includes("linuxcnc-vendored-5axis-gcode")) {
|
||||
throw new Error("program source DOM did not show LinuxCNC vendored source");
|
||||
}
|
||||
win.webRtcp5AxisSimulation.dispatch({ type: "RUN_MACHINE_FILE_PROGRAM" });
|
||||
const machineFileRunState = await waitForMachineFileExecution(win);
|
||||
if (machineFileRunState.machineFileExecution?.summary?.machineFileExecutionReady !== true) {
|
||||
throw new Error(`machine-file backed remap run did not complete: ${JSON.stringify(machineFileRunState.machineFileExecution?.summary)}`);
|
||||
}
|
||||
if (!machineFileRunState.machineFileExecution.resultText.includes("fiveaxis_file_reached_exit=1")) {
|
||||
throw new Error("machine-file backed remap run did not reach exit");
|
||||
}
|
||||
if (!machineFileRunState.machineFileExecution.machineFilePlan?.selectedProgramFilename?.includes("impeller-7bl-xyzac.ngc")) {
|
||||
throw new Error("machine-file backed remap run did not use selected LinuxCNC G-code source");
|
||||
}
|
||||
if (!doc.querySelector('[data-machine-file-execution="status"]')?.textContent.includes("ready")) {
|
||||
throw new Error("machine-file execution status did not render");
|
||||
}
|
||||
if (machineFileRunState.fullExecutionBoundary?.machineFileBackedRemapReady !== true) {
|
||||
throw new Error(`full execution boundary did not record remap readiness: ${JSON.stringify(machineFileRunState.fullExecutionBoundary)}`);
|
||||
}
|
||||
if (machineFileRunState.fullExecutionBoundary?.fullLinuxCncProgramExecutionReady !== false) {
|
||||
throw new Error("full LinuxCNC program execution must remain blocked without native task/planner/HAL");
|
||||
}
|
||||
if (!machineFileRunState.fullExecutionBoundary.blockers.some((blocker) => blocker.includes("trajectory planner"))) {
|
||||
throw new Error("full execution boundary did not expose planner blocker");
|
||||
}
|
||||
if (!doc.querySelector('[data-full-execution-boundary="status"]')?.textContent.includes("remap ready")) {
|
||||
throw new Error("full execution boundary status did not render remap readiness");
|
||||
}
|
||||
if (!doc.querySelector('[data-full-execution-boundary="status"]')?.textContent.includes("full blocked")) {
|
||||
throw new Error("full execution boundary status did not render full blocked state");
|
||||
}
|
||||
if (!doc.querySelector('[data-full-execution-boundary="blockers"]')?.textContent.includes("native LinuxCNC task")) {
|
||||
throw new Error("full execution blocker diagnostic did not render native task blocker");
|
||||
}
|
||||
if (!doc.querySelector('[data-full-execution-boundary="evidence"]')?.textContent.includes("linuxcnc_machine_file_remap_ready_planner_task_hal_blocked")) {
|
||||
throw new Error("full execution evidence diagnostic did not render boundary semantic");
|
||||
}
|
||||
const savedSession = await win.webRtcp5AxisSimulation.saveSession();
|
||||
await wait(50);
|
||||
if (savedSession.snapshot.format !== "web-rtcp-5axis-session-snapshot") {
|
||||
throw new Error("saveSession did not write a five-axis session snapshot");
|
||||
}
|
||||
if (!doc.querySelector('[data-session-persistence="status"]')?.textContent.includes("saved")) {
|
||||
throw new Error("session save status did not render");
|
||||
}
|
||||
if (doc.querySelector('[data-active-program-line]')?.textContent !== "Current line 2") {
|
||||
throw new Error("loaded program did not render first LinuxCNC motion line");
|
||||
}
|
||||
if (doc.querySelector(".gcode-row.active")?.dataset.programLine !== "2") {
|
||||
throw new Error("loaded program active row should be first LinuxCNC motion line");
|
||||
}
|
||||
|
||||
win.webRtcp5AxisSimulation.dispatch({ type: "RUN" });
|
||||
@@ -162,8 +387,29 @@
|
||||
if (win.webRtcp5AxisSimulation.getState().runState !== "running") {
|
||||
throw new Error("RUN action did not update state after power on");
|
||||
}
|
||||
if (doc.querySelector(".gcode-row.active")?.dataset.programLine !== "6") {
|
||||
throw new Error("RUN did not highlight the executing current line");
|
||||
if (win.webRtcp5AxisSimulation.getState().programRuntimeFeedback?.sourceMode !== "linuxcnc-tp-runtime-sample") {
|
||||
throw new Error("RUN did not advance with LinuxCNC TP runtime feedback");
|
||||
}
|
||||
if (win.webRtcp5AxisSimulation.getState().feed.currentVelocity <= 0) {
|
||||
throw new Error("RUN did not expose LinuxCNC TP current velocity");
|
||||
}
|
||||
if (Number(doc.querySelector(".gcode-row.active")?.dataset.programLine || 0) < 2) {
|
||||
throw new Error("RUN did not highlight a LinuxCNC canonical motion line");
|
||||
}
|
||||
doc.querySelector('[data-action="PAUSE"]').click();
|
||||
await wait(50);
|
||||
if (win.webRtcp5AxisSimulation.getState().runState !== "paused") {
|
||||
throw new Error("PAUSE action did not update state");
|
||||
}
|
||||
doc.querySelector('[data-action="RUN"]').click();
|
||||
await wait(50);
|
||||
if (!win.webRtcp5AxisSimulation.getState().operatorMessage.includes("resume paused program")) {
|
||||
throw new Error("RUN should be blocked while program is paused");
|
||||
}
|
||||
doc.querySelector('[data-action="RESUME"]').click();
|
||||
await wait(50);
|
||||
if (win.webRtcp5AxisSimulation.getState().runState !== "running") {
|
||||
throw new Error("RESUME action did not restore running state");
|
||||
}
|
||||
const tcpButton = doc.querySelector('[data-action="kins-tcp"]');
|
||||
tcpButton.click();
|
||||
@@ -181,8 +427,11 @@
|
||||
if (canvas.dataset.threeRtcpState !== "on") {
|
||||
throw new Error("Three.js preview did not consume RTCP enabled frame");
|
||||
}
|
||||
if (doc.querySelector('[data-rtcp-diagnostic="kinematics-ready"]')?.textContent !== "pending") {
|
||||
throw new Error("RTCP diagnostics must keep LinuxCNC kinematics pending for fixture mode");
|
||||
if (doc.querySelector('[data-rtcp-diagnostic="kinematics-ready"]')?.textContent !== "ready") {
|
||||
throw new Error("RTCP diagnostics must show LinuxCNC kinematics ready");
|
||||
}
|
||||
if (doc.querySelector('[data-rtcp-diagnostic="execution-context"]')?.textContent !== "worker") {
|
||||
throw new Error("RTCP diagnostics must show worker kinematics context");
|
||||
}
|
||||
if (!doc.querySelector('[data-linuxcnc-boundary="adapter"]')?.textContent.includes("linuxcnc-boundary-adapter")) {
|
||||
throw new Error("missing LinuxCNC boundary adapter diagnostic");
|
||||
@@ -193,12 +442,19 @@
|
||||
if (!doc.querySelector('[data-linuxcnc-boundary="profile-summary"]')?.textContent.includes("XYZAC / 5 joints / 10 tools")) {
|
||||
throw new Error("missing LinuxCNC profile summary diagnostic");
|
||||
}
|
||||
if (!doc.querySelector('[data-linuxcnc-boundary="readiness"]')?.textContent.includes("blocked")) {
|
||||
throw new Error("LinuxCNC boundary readiness should remain blocked");
|
||||
if (!doc.querySelector('[data-linuxcnc-boundary="readiness"]')?.textContent.includes("ready")) {
|
||||
throw new Error("LinuxCNC boundary readiness should show kinematics ready");
|
||||
}
|
||||
if (!doc.querySelector('[data-linuxcnc-boundary="interpreter"]')?.textContent.includes("linuxcnc_interpreter_wasm_canonical_events")) {
|
||||
throw new Error("missing LinuxCNC interpreter boundary diagnostic");
|
||||
}
|
||||
if (doc.querySelector('[data-linuxcnc-boundary="interpreter-context"]')?.textContent !== "worker") {
|
||||
throw new Error("LinuxCNC interpreter diagnostics must show worker context");
|
||||
}
|
||||
canvas = doc.querySelector("[data-five-axis-canvas]");
|
||||
const tcpPoseBeforeStep = canvas.dataset.threeTcpPose;
|
||||
win.webRtcp5AxisSimulation.dispatch({ type: "STEP" });
|
||||
await win.webRtcp5AxisSimulation.refreshKinematicsFrame({ operatorMessage: "browser smoke refreshed worker frame" });
|
||||
await wait(50);
|
||||
if (win.webRtcp5AxisSimulation.getState().activeLine <= rtcpState.activeLine) {
|
||||
throw new Error("STEP did not advance RTCP frame line");
|
||||
@@ -208,6 +464,13 @@
|
||||
throw new Error("Three.js preview did not update TCP pose after STEP");
|
||||
}
|
||||
assertCanvasNonblank(canvas, "updated Three.js preview");
|
||||
doc.querySelector('[data-action="STOP"]').click();
|
||||
await wait(50);
|
||||
if (win.webRtcp5AxisSimulation.getState().machine.interpState !== "idle") {
|
||||
throw new Error("STOP did not clear interpreter state");
|
||||
}
|
||||
doc.querySelector('[data-action="mode-manual"]').click();
|
||||
await wait(50);
|
||||
doc.querySelector('[data-action="mode-jog"]').click();
|
||||
await wait(50);
|
||||
const xBeforeJog = win.webRtcp5AxisSimulation.getState().axisPose.x;
|
||||
@@ -216,11 +479,47 @@
|
||||
if (win.webRtcp5AxisSimulation.getState().axisPose.x <= xBeforeJog) {
|
||||
throw new Error("JOG X+ did not move the axis");
|
||||
}
|
||||
await win.webRtcp5AxisSimulation.restoreSession();
|
||||
await wait(50);
|
||||
const restoredState = win.webRtcp5AxisSimulation.getState();
|
||||
if (restoredState.axisPose.x !== xBeforeJog) {
|
||||
throw new Error("restoreSession did not restore saved axis pose");
|
||||
}
|
||||
if (restoredState.programExecutionSourceMode !== "linuxcnc-interpreter-wasm") {
|
||||
throw new Error("restoreSession did not restore LinuxCNC interpreter execution source");
|
||||
}
|
||||
if (!doc.querySelector('[data-session-persistence="status"]')?.textContent.includes("restored")) {
|
||||
throw new Error("session restore status did not render");
|
||||
}
|
||||
if (win.webRtcp5AxisSimulation.getState().machine.allHomed !== true) {
|
||||
doc.querySelector('[data-action="mode-manual"]').click();
|
||||
await wait(50);
|
||||
doc.querySelector('[data-action="HOME"]').click();
|
||||
await wait(50);
|
||||
}
|
||||
doc.querySelector('[data-action="mode-mdi"]').click();
|
||||
const mdiInput = doc.querySelector('[data-action="mdi-command"]');
|
||||
if (!mdiInput) {
|
||||
throw new Error("missing MDI command input");
|
||||
}
|
||||
mdiInput.value = "G90 X12.5 Y-4 Z1.25 F900";
|
||||
doc.querySelector('[data-action="mdi-submit"]').click();
|
||||
await wait(50);
|
||||
let mdiState = win.webRtcp5AxisSimulation.getState();
|
||||
if (mdiState.runState !== "mdi") {
|
||||
throw new Error("MDI action did not update run state");
|
||||
}
|
||||
if (mdiState.axisPose.x !== 12.5 || mdiState.axisPose.y !== -4 || mdiState.axisPose.z !== 1.25) {
|
||||
throw new Error(`MDI input did not move axes: ${JSON.stringify(mdiState.axisPose)}`);
|
||||
}
|
||||
if (!doc.querySelector(".mdi-history")?.textContent.includes("G90 X12.5")) {
|
||||
throw new Error("MDI history did not render executed command");
|
||||
}
|
||||
doc.querySelector('[data-action="MDI_RUN"]').click();
|
||||
await wait(50);
|
||||
if (win.webRtcp5AxisSimulation.getState().runState !== "mdi") {
|
||||
throw new Error("MDI action did not update run state");
|
||||
mdiState = win.webRtcp5AxisSimulation.getState();
|
||||
if (mdiState.runState !== "mdi" || mdiState.machine.mdiCommand !== "G90 X12.5 Y-4 Z1.25 F900") {
|
||||
throw new Error("bottom MDI button did not execute staged command");
|
||||
}
|
||||
doc.querySelector('[data-action="reset"]').click();
|
||||
await wait(50);
|
||||
@@ -272,6 +571,8 @@
|
||||
if (win.webRtcp5AxisSimulation.getState().preview.fullscreen !== true) {
|
||||
throw new Error("fullscreen button did not update state");
|
||||
}
|
||||
doc.querySelector('[data-action="mode-manual"]').click();
|
||||
await wait(50);
|
||||
doc.querySelector('[data-action="HOME"]').click();
|
||||
await wait(50);
|
||||
const homedState = win.webRtcp5AxisSimulation.getState();
|
||||
|
||||
Reference in New Issue
Block a user