完善五轴 RTCP 仿真与验证资料
@@ -0,0 +1,550 @@
|
|||||||
|
import fs from "node:fs/promises";
|
||||||
|
import http from "node:http";
|
||||||
|
import path from "node:path";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
|
import puppeteer from "puppeteer-core";
|
||||||
|
import { PNG } from "pngjs";
|
||||||
|
|
||||||
|
const REPO_ROOT = path.resolve("/home/meswork/cnc_wams");
|
||||||
|
const QA_ROOT = path.join(REPO_ROOT, "qa/web-rtcp-5axis-site-test");
|
||||||
|
const OUTPUT_DIR = path.join(QA_ROOT, "output");
|
||||||
|
const EVIDENCE_SCOPE = "working7-manual-flow-evidence";
|
||||||
|
const SCREENSHOT_DIR = path.join(OUTPUT_DIR, EVIDENCE_SCOPE);
|
||||||
|
const CHROME_PATH = process.env.CHROME_PATH || process.env.CHROMIUM || "/usr/bin/google-chrome";
|
||||||
|
const TARGET_URL = process.env.TARGET_URL || "";
|
||||||
|
const APP_URL = process.env.APP_URL || "/web-rtcp-5axis-sim-plan/app/index.html";
|
||||||
|
const JOB_ID = process.env.JOB_ID || `w7-${new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${randomUUID().slice(0, 8)}`;
|
||||||
|
const REPORT_ID = process.env.REPORT_ID || `report-${JOB_ID}`;
|
||||||
|
const REPORT_BASENAME = `${EVIDENCE_SCOPE}-report`;
|
||||||
|
|
||||||
|
await fs.mkdir(OUTPUT_DIR, { recursive: true });
|
||||||
|
await fs.mkdir(SCREENSHOT_DIR, { recursive: true });
|
||||||
|
|
||||||
|
let server = null;
|
||||||
|
let targetUrl = TARGET_URL;
|
||||||
|
if (!targetUrl) {
|
||||||
|
server = createStaticServer(REPO_ROOT);
|
||||||
|
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||||
|
const address = server.address();
|
||||||
|
if (!address || typeof address === "string") throw new Error("failed to start static server");
|
||||||
|
targetUrl = `http://127.0.0.1:${address.port}${APP_URL}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch({
|
||||||
|
headless: true,
|
||||||
|
executablePath: CHROME_PATH,
|
||||||
|
defaultViewport: { width: 1500, height: 1050, deviceScaleFactor: 1 },
|
||||||
|
ignoreHTTPSErrors: true,
|
||||||
|
args: [
|
||||||
|
"--ignore-certificate-errors",
|
||||||
|
"--disable-gpu",
|
||||||
|
"--enable-webgl",
|
||||||
|
"--use-angle=swiftshader",
|
||||||
|
"--enable-unsafe-swiftshader",
|
||||||
|
"--no-sandbox",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const page = await browser.newPage();
|
||||||
|
const consoleErrors = [];
|
||||||
|
const pageErrors = [];
|
||||||
|
const networkErrors = [];
|
||||||
|
page.on("console", (msg) => {
|
||||||
|
if (msg.type() === "error") consoleErrors.push(msg.text());
|
||||||
|
});
|
||||||
|
page.on("pageerror", (error) => pageErrors.push(error.message));
|
||||||
|
page.on("response", (response) => {
|
||||||
|
if (response.status() >= 400) {
|
||||||
|
networkErrors.push(`${response.status()} ${response.url()}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const report = {
|
||||||
|
jobId: JOB_ID,
|
||||||
|
reportId: REPORT_ID,
|
||||||
|
evidenceScope: EVIDENCE_SCOPE,
|
||||||
|
generatedAt: new Date().toISOString(),
|
||||||
|
targetUrl,
|
||||||
|
chromePath: CHROME_PATH,
|
||||||
|
screenshotsDir: SCREENSHOT_DIR,
|
||||||
|
steps: [],
|
||||||
|
checks: [],
|
||||||
|
consoleErrors,
|
||||||
|
pageErrors,
|
||||||
|
networkErrors,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await page.goto(targetUrl, { waitUntil: "networkidle2", timeout: 60000 });
|
||||||
|
await page.waitForSelector('[data-shell="gmoccapy-5axis"]', { timeout: 15000 });
|
||||||
|
await page.waitForFunction(() => Boolean(window.webRtcp5AxisSimulation?.getState), { timeout: 15000 });
|
||||||
|
await page.waitForFunction(() => document.querySelector("[data-five-axis-canvas]")?.dataset?.threeReady === "true", { timeout: 20000 });
|
||||||
|
await windowReady();
|
||||||
|
await captureStep("01-loaded", "Main shell loaded", "The gmoccapy shell, canvas, diagnostics, and public store API are ready.");
|
||||||
|
|
||||||
|
await click("power");
|
||||||
|
await waitForState((state) => state.machine.powerOn === true && state.machine.taskState === "on", 10000, "power on");
|
||||||
|
await click("HOME");
|
||||||
|
await waitForState((state) => state.machine.allHomed === true && state.machine.mode === "manual", 10000, "home complete");
|
||||||
|
await captureStep("02-powered-homed-manual", "POWER and HOME", "Machine is powered, homed, and in MANUAL mode.");
|
||||||
|
|
||||||
|
await click("mode-auto");
|
||||||
|
await waitForState((state) => state.machine.mode === "auto" && state.machine.powerOn === true && state.machine.allHomed === true, 10000, "auto mode");
|
||||||
|
await captureStep("03-auto-active", "AUTO mode active", "AUTO mode becomes active while power and home state are preserved.");
|
||||||
|
|
||||||
|
await click("mode-manual");
|
||||||
|
await waitForState((state) => state.machine.mode === "manual" && state.machine.powerOn === true, 10000, "manual mode");
|
||||||
|
const beforeJog = await getState();
|
||||||
|
await click("JOG_X_POS");
|
||||||
|
await waitForState((state) => Number(state.axisPose?.x || 0) > Number(beforeJog.axisPose?.x || 0), 10000, "X+ jog changes position");
|
||||||
|
await captureStep("04-manual-jog-x", "Manual X+ jog", "X+ jog changes the X axis position in MANUAL mode.");
|
||||||
|
|
||||||
|
await click("mode-mdi");
|
||||||
|
await waitForState((state) => state.machine.mode === "mdi", 10000, "mdi mode");
|
||||||
|
await setMdiCommand("G90 X12.5 Y-4 Z1.25 F900");
|
||||||
|
await submitMdiCommand();
|
||||||
|
await waitForState((state) => (
|
||||||
|
state.machine.mode === "mdi" &&
|
||||||
|
/G90 X12\.5 Y-4 Z1\.25 F900/.test(state.machine.mdiCommand || state.operatorMessage || "")
|
||||||
|
), 10000, "MDI command accepted");
|
||||||
|
await captureStep("05-mdi-command", "MDI command", "MDI accepts a coordinate command and records the staged/executed command state.");
|
||||||
|
|
||||||
|
await click("mode-manual");
|
||||||
|
await waitForState((state) => state.machine.mode === "manual", 10000, "manual before overrides");
|
||||||
|
await click("rapid-override-up");
|
||||||
|
await waitForState((state) => Number(state.feed.rapidOverride) === 110, 8000, "rapid override up");
|
||||||
|
await click("rapid-override-reset");
|
||||||
|
await waitForState((state) => Number(state.feed.rapidOverride) === 100, 8000, "rapid override reset");
|
||||||
|
await click("feed-override-down");
|
||||||
|
await waitForState((state) => Number(state.feed.feedOverride) === 90, 8000, "feed override down");
|
||||||
|
await click("feed-override-reset");
|
||||||
|
await waitForState((state) => Number(state.feed.feedOverride) === 100, 8000, "feed override reset");
|
||||||
|
await click("ignore-limits");
|
||||||
|
await waitForState((state) => state.gmoccapyGui.ignoreLimits === true, 8000, "ignore limits on");
|
||||||
|
await click("block-delete");
|
||||||
|
await waitForState((state) => state.gmoccapyGui.optionalBlocks === true, 8000, "block delete on");
|
||||||
|
await click("optional-stop");
|
||||||
|
await waitForState((state) => state.gmoccapyGui.optionalStop === true, 8000, "optional stop on");
|
||||||
|
await captureStep("06-overrides-hal", "Overrides and HAL inputs", "Rapid/feed overrides reset to 100 and HAL input toggles are active.");
|
||||||
|
|
||||||
|
await click("spindle-forward");
|
||||||
|
await waitForState((state) => state.spindle.enabled === true && state.spindle.direction === "forward", 8000, "spindle forward");
|
||||||
|
await click("spindle-override-up");
|
||||||
|
await waitForState((state) => Number(state.spindle.override) === 110, 8000, "spindle override up");
|
||||||
|
await click("spindle-override-reset");
|
||||||
|
await waitForState((state) => Number(state.spindle.override) === 100, 8000, "spindle override reset");
|
||||||
|
await click("toggle-flood");
|
||||||
|
await waitForState((state) => state.coolant.flood === true, 8000, "flood toggled on");
|
||||||
|
await click("toggle-mist");
|
||||||
|
await waitForState((state) => state.coolant.mist === true, 8000, "mist toggled on");
|
||||||
|
await click("spindle-stop");
|
||||||
|
await waitForState((state) => state.spindle.enabled === false && state.spindle.direction === "stop", 8000, "spindle stop");
|
||||||
|
await captureStep("07-spindle-coolant", "Spindle and coolant", "Spindle, spindle override, flood, and mist controls obey the powered machine gate.");
|
||||||
|
|
||||||
|
const savedSnapshot = await page.evaluate(() => window.webRtcp5AxisSimulation.saveSession({
|
||||||
|
sessionId: "working7-manual-flow",
|
||||||
|
filename: "working7-session.json",
|
||||||
|
}));
|
||||||
|
await waitForState((state) => state.sessionPersistence.status === "saved", 10000, "session saved");
|
||||||
|
await page.select('[data-action="select-profile"]', "gmoccapy-xyzab");
|
||||||
|
await waitForState((state) => state.machineProfile === "gmoccapy-xyzab", 15000, "profile changed before restore");
|
||||||
|
await page.evaluate(() => window.webRtcp5AxisSimulation.restoreSession({
|
||||||
|
sessionId: "working7-manual-flow",
|
||||||
|
filename: "working7-session.json",
|
||||||
|
}));
|
||||||
|
await waitForState((state) => state.sessionPersistence.status === "restored" && state.machineProfile === "xyzac-trt", 15000, "session restored");
|
||||||
|
const restoredSession = await getState();
|
||||||
|
report.sessionEvidence = {
|
||||||
|
savedPath: savedSnapshot.path,
|
||||||
|
savedStorageMode: savedSnapshot.storageMode,
|
||||||
|
restoredPath: restoredSession.sessionPersistence.path,
|
||||||
|
restoredStorageMode: restoredSession.sessionPersistence.storageMode,
|
||||||
|
};
|
||||||
|
await captureStep("08-session-restored", "Save and restore session", "Saved profile and control state are restored after switching to another profile.");
|
||||||
|
|
||||||
|
await captureStep("09-diagnostics", "Diagnostics", "Task policy, INI, Task/HAL, full boundary, gmoccapy communication, and HAL diagnostics are visible.");
|
||||||
|
addChecks();
|
||||||
|
report.status = report.checks.every((check) => check.pass) && pageErrors.length === 0 ? "PASS" : "FAIL";
|
||||||
|
|
||||||
|
const jsonPath = path.join(OUTPUT_DIR, `${REPORT_BASENAME}.json`);
|
||||||
|
const pdfPath = path.join(OUTPUT_DIR, `${REPORT_BASENAME}.pdf`);
|
||||||
|
report.jsonPath = jsonPath;
|
||||||
|
report.pdfPath = pdfPath;
|
||||||
|
await fs.writeFile(jsonPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
||||||
|
await writePdfReport(pdfPath, report);
|
||||||
|
|
||||||
|
console.log(`working7_manual_flow_status=${report.status}`);
|
||||||
|
console.log(`working7_manual_flow_job_id=${report.jobId}`);
|
||||||
|
console.log(`working7_manual_flow_report_id=${report.reportId}`);
|
||||||
|
console.log(`working7_manual_flow_json=${jsonPath}`);
|
||||||
|
console.log(`working7_manual_flow_pdf=${pdfPath}`);
|
||||||
|
console.log(`working7_manual_flow_screenshots=${SCREENSHOT_DIR}`);
|
||||||
|
if (report.status !== "PASS") process.exitCode = 1;
|
||||||
|
} finally {
|
||||||
|
await page.close().catch(() => {});
|
||||||
|
await browser.close().catch(() => {});
|
||||||
|
if (server) await new Promise((resolve) => server.close(resolve));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function windowReady() {
|
||||||
|
await waitForState((state) => (
|
||||||
|
state.kinematicsRuntimeReadiness?.loaded === true &&
|
||||||
|
state.interpreterRuntimeReadiness?.loaded === true &&
|
||||||
|
state.taskHalRuntimeReadiness?.loaded === true &&
|
||||||
|
state.machineFileStaging?.status === "staged"
|
||||||
|
), 30000, "runtime and machine files ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function captureStep(name, title, description) {
|
||||||
|
const screenshotPath = path.join(SCREENSHOT_DIR, `${name}.png`);
|
||||||
|
await page.screenshot({ path: screenshotPath, fullPage: true });
|
||||||
|
const [state, dom, pixelStats] = await Promise.all([
|
||||||
|
getState(),
|
||||||
|
getDomEvidence(),
|
||||||
|
analyzePng(screenshotPath),
|
||||||
|
]);
|
||||||
|
const step = {
|
||||||
|
name,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
screenshotPath,
|
||||||
|
pixelStats,
|
||||||
|
state: summarizeState(state),
|
||||||
|
dom,
|
||||||
|
};
|
||||||
|
report.steps.push(step);
|
||||||
|
return step;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDomEvidence() {
|
||||||
|
return page.evaluate(() => {
|
||||||
|
const text = (selector) => document.querySelector(selector)?.textContent?.trim() || "";
|
||||||
|
const button = (action) => {
|
||||||
|
const element = document.querySelector(`[data-action="${action}"]`);
|
||||||
|
return {
|
||||||
|
exists: Boolean(element),
|
||||||
|
disabled: Boolean(element?.disabled),
|
||||||
|
active: element?.dataset?.active || null,
|
||||||
|
commandReady: element?.dataset?.commandReady || null,
|
||||||
|
title: element?.getAttribute("title") || "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
regions: window.webRtcp5AxisSimulation.getRegions?.() || null,
|
||||||
|
canvas: { ...(document.querySelector("[data-five-axis-canvas]")?.dataset || {}) },
|
||||||
|
sidebar: {
|
||||||
|
power: button("power"),
|
||||||
|
manual: button("mode-manual"),
|
||||||
|
auto: button("mode-auto"),
|
||||||
|
mdi: button("mode-mdi"),
|
||||||
|
},
|
||||||
|
bottom: {
|
||||||
|
home: button("HOME"),
|
||||||
|
jogXPlus: button("JOG_X_POS"),
|
||||||
|
mdiRun: button("MDI_RUN"),
|
||||||
|
},
|
||||||
|
values: {
|
||||||
|
rapidOverride: text('[data-value="rapid-override"]'),
|
||||||
|
feedOverride: text('[data-value="feed-override"]'),
|
||||||
|
spindleOverride: text('[data-value="spindle-override"]'),
|
||||||
|
halLast: text('[data-value="gmoccapy-hal-last"]'),
|
||||||
|
session: text('[data-session-persistence="status"]'),
|
||||||
|
taskGates: text('[data-linuxcnc-task-policy="gates"]'),
|
||||||
|
taskHal: text('[data-task-hal-runtime="readiness"]'),
|
||||||
|
gmoccapyHal: text('[data-gmoccapy-hal="boundary"]'),
|
||||||
|
operatorMessage: text("[data-operator-message]"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setMdiCommand(command) {
|
||||||
|
await page.$eval('[data-action="mdi-command"]', (input, value) => {
|
||||||
|
input.value = value;
|
||||||
|
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
input.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
|
}, command);
|
||||||
|
await waitForState((state) => state.machine.mdiCommand === command.toUpperCase(), 5000, "MDI input staged");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitMdiCommand() {
|
||||||
|
await page.$eval('[data-action="mdi-form"]', (form) => {
|
||||||
|
form.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function click(action) {
|
||||||
|
await page.evaluate((selector) => {
|
||||||
|
const element = document.querySelector(selector);
|
||||||
|
if (!element) throw new Error(`missing action ${selector}`);
|
||||||
|
element.click();
|
||||||
|
}, `[data-action="${action}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getState() {
|
||||||
|
return page.evaluate(() => JSON.parse(JSON.stringify(window.webRtcp5AxisSimulation.getState())));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function waitForState(predicate, timeoutMs, label) {
|
||||||
|
const started = Date.now();
|
||||||
|
let lastState = null;
|
||||||
|
while (Date.now() - started < timeoutMs) {
|
||||||
|
lastState = await getState();
|
||||||
|
if (predicate(lastState)) return lastState;
|
||||||
|
await wait(50);
|
||||||
|
}
|
||||||
|
throw new Error(`timeout waiting for ${label}: ${JSON.stringify(summarizeState(lastState || {}))}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addChecks() {
|
||||||
|
const byName = Object.fromEntries(report.steps.map((step) => [step.name, step]));
|
||||||
|
const loaded = byName["01-loaded"]?.state;
|
||||||
|
const manual = byName["02-powered-homed-manual"]?.state;
|
||||||
|
const auto = byName["03-auto-active"]?.state;
|
||||||
|
const jog = byName["04-manual-jog-x"]?.state;
|
||||||
|
const mdi = byName["05-mdi-command"]?.state;
|
||||||
|
const overrides = byName["06-overrides-hal"]?.state;
|
||||||
|
const spindleCoolant = byName["07-spindle-coolant"]?.state;
|
||||||
|
const restored = byName["08-session-restored"]?.state;
|
||||||
|
const diagnostics = byName["09-diagnostics"]?.dom;
|
||||||
|
|
||||||
|
report.checks.push(
|
||||||
|
check("Main shell regions and canvas are ready", Object.values(byName["01-loaded"]?.dom?.regions || {}).every(Boolean) && byName["01-loaded"]?.dom?.canvas?.threeReady === "true", JSON.stringify({ regions: byName["01-loaded"]?.dom?.regions, canvas: byName["01-loaded"]?.dom?.canvas })),
|
||||||
|
check("POWER and HOME leave MANUAL ready", manual?.machine?.powerOn === true && manual?.machine?.allHomed === true && manual?.machine?.mode === "manual", JSON.stringify(manual?.machine)),
|
||||||
|
check("AUTO activates without losing power/home", auto?.machine?.mode === "auto" && auto?.machine?.powerOn === true && auto?.machine?.allHomed === true, JSON.stringify(auto?.machine)),
|
||||||
|
check("Manual jog changes X", Number(jog?.axisPose?.x || 0) > Number(manual?.axisPose?.x || 0), JSON.stringify({ before: manual?.axisPose, after: jog?.axisPose })),
|
||||||
|
check("MDI command is accepted in MDI mode", mdi?.machine?.mode === "mdi" && /G90 X12\.5 Y-4 Z1\.25 F900/.test(`${mdi?.machine?.mdiCommand || ""} ${mdi?.operatorMessage || ""}`), JSON.stringify({ machine: mdi?.machine, operatorMessage: mdi?.operatorMessage })),
|
||||||
|
check("Overrides reset and HAL toggles are active", overrides?.feed?.rapidOverride === 100 && overrides?.feed?.feedOverride === 100 && overrides?.gmoccapyGui?.ignoreLimits === true && overrides?.gmoccapyGui?.optionalBlocks === true && overrides?.gmoccapyGui?.optionalStop === true, JSON.stringify({ feed: overrides?.feed, gmoccapyGui: overrides?.gmoccapyGui })),
|
||||||
|
check("Spindle and coolant controls update state", spindleCoolant?.spindle?.direction === "stop" && spindleCoolant?.spindle?.override === 100 && spindleCoolant?.coolant?.flood === true && spindleCoolant?.coolant?.mist === true, JSON.stringify({ spindle: spindleCoolant?.spindle, coolant: spindleCoolant?.coolant })),
|
||||||
|
check("Session restore returns to saved profile", restored?.machineProfile === "xyzac-trt" && restored?.sessionPersistence?.status === "restored", JSON.stringify({ machineProfile: restored?.machineProfile, sessionPersistence: restored?.sessionPersistence, evidence: report.sessionEvidence })),
|
||||||
|
check("Diagnostics expose Task/HAL and gmoccapy HAL", Boolean(diagnostics?.values?.taskHal) && Boolean(diagnostics?.values?.gmoccapyHal), JSON.stringify(diagnostics?.values)),
|
||||||
|
check("Screenshots are nonblank", report.steps.every((step) => step.pixelStats.nonBlackRatio > 0.1), report.steps.map((step) => `${step.name}:${step.pixelStats.nonBlackRatio}`).join(", ")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writePdfReport(pdfPath, data) {
|
||||||
|
const reportPage = await browser.newPage();
|
||||||
|
const rows = data.checks.map((item) => `
|
||||||
|
<tr>
|
||||||
|
<td>${escapeHtml(item.name)}</td>
|
||||||
|
<td class="${item.pass ? "pass" : "fail"}">${item.pass ? "PASS" : "FAIL"}</td>
|
||||||
|
<td>${escapeHtml(item.detail)}</td>
|
||||||
|
</tr>
|
||||||
|
`).join("");
|
||||||
|
const steps = data.steps.map((step) => `
|
||||||
|
<section>
|
||||||
|
<h2>${escapeHtml(step.name)} - ${escapeHtml(step.title)}</h2>
|
||||||
|
<p>${escapeHtml(step.description)}</p>
|
||||||
|
<p><strong>Screenshot:</strong> ${escapeHtml(step.screenshotPath)}</p>
|
||||||
|
<p><strong>State:</strong> ${escapeHtml(JSON.stringify({
|
||||||
|
machineProfile: step.state.machineProfile,
|
||||||
|
runState: step.state.runState,
|
||||||
|
machine: step.state.machine,
|
||||||
|
axisPose: step.state.axisPose,
|
||||||
|
feed: step.state.feed,
|
||||||
|
spindle: step.state.spindle,
|
||||||
|
coolant: step.state.coolant,
|
||||||
|
sessionPersistence: step.state.sessionPersistence,
|
||||||
|
}))}</p>
|
||||||
|
</section>
|
||||||
|
`).join("");
|
||||||
|
await reportPage.setContent(`<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; margin: 28px; color: #17202a; }
|
||||||
|
h1 { font-size: 22px; margin-bottom: 6px; }
|
||||||
|
h2 { font-size: 16px; margin-top: 18px; }
|
||||||
|
table { border-collapse: collapse; width: 100%; margin-top: 14px; }
|
||||||
|
th, td { border: 1px solid #9aa5b1; padding: 6px; font-size: 11px; vertical-align: top; }
|
||||||
|
th { background: #eef2f7; }
|
||||||
|
.pass { color: #126b37; font-weight: 700; }
|
||||||
|
.fail { color: #a61b1b; font-weight: 700; }
|
||||||
|
.meta { font-size: 12px; line-height: 1.45; }
|
||||||
|
section { break-inside: avoid; border-top: 1px solid #d8dee6; padding-top: 8px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>working7 Manual Flow Evidence</h1>
|
||||||
|
<div class="meta">
|
||||||
|
<div><strong>Status:</strong> ${escapeHtml(data.status)}</div>
|
||||||
|
<div><strong>Job ID:</strong> ${escapeHtml(data.jobId)}</div>
|
||||||
|
<div><strong>Report ID:</strong> ${escapeHtml(data.reportId)}</div>
|
||||||
|
<div><strong>Target:</strong> ${escapeHtml(data.targetUrl)}</div>
|
||||||
|
<div><strong>Generated:</strong> ${escapeHtml(data.generatedAt)}</div>
|
||||||
|
<div><strong>Screenshots:</strong> ${escapeHtml(data.screenshotsDir)}</div>
|
||||||
|
</div>
|
||||||
|
<h2>Checks</h2>
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Check</th><th>Status</th><th>Evidence</th></tr></thead>
|
||||||
|
<tbody>${rows}</tbody>
|
||||||
|
</table>
|
||||||
|
<h2>Steps</h2>
|
||||||
|
${steps}
|
||||||
|
</body>
|
||||||
|
</html>`, { waitUntil: "load" });
|
||||||
|
await reportPage.pdf({
|
||||||
|
path: pdfPath,
|
||||||
|
format: "A4",
|
||||||
|
printBackground: true,
|
||||||
|
margin: { top: "12mm", right: "10mm", bottom: "12mm", left: "10mm" },
|
||||||
|
});
|
||||||
|
await reportPage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarizeState(state = {}) {
|
||||||
|
return {
|
||||||
|
machineProfile: state.machineProfile,
|
||||||
|
activeProgram: state.activeProgram,
|
||||||
|
runState: state.runState,
|
||||||
|
activeLine: state.activeLine,
|
||||||
|
machine: {
|
||||||
|
powerOn: state.machine?.powerOn,
|
||||||
|
taskState: state.machine?.taskState,
|
||||||
|
mode: state.machine?.mode,
|
||||||
|
allHomed: state.machine?.allHomed,
|
||||||
|
interpState: state.machine?.interpState,
|
||||||
|
taskPaused: state.machine?.taskPaused,
|
||||||
|
mdiCommand: state.machine?.mdiCommand,
|
||||||
|
},
|
||||||
|
axisPose: pickAxes(state.axisPose),
|
||||||
|
dro: pickAxes(state.dro),
|
||||||
|
feed: {
|
||||||
|
rapidOverride: Number(state.feed?.rapidOverride),
|
||||||
|
feedOverride: Number(state.feed?.feedOverride),
|
||||||
|
feedRate: Number(state.feed?.feedRate),
|
||||||
|
currentVelocity: Number(state.feed?.currentVelocity),
|
||||||
|
},
|
||||||
|
spindle: {
|
||||||
|
enabled: Boolean(state.spindle?.enabled),
|
||||||
|
direction: state.spindle?.direction,
|
||||||
|
override: Number(state.spindle?.override),
|
||||||
|
rpm: Number(state.spindle?.rpm),
|
||||||
|
},
|
||||||
|
coolant: {
|
||||||
|
flood: Boolean(state.coolant?.flood),
|
||||||
|
mist: Boolean(state.coolant?.mist),
|
||||||
|
},
|
||||||
|
gmoccapyGui: {
|
||||||
|
ignoreLimits: Boolean(state.gmoccapyGui?.ignoreLimits),
|
||||||
|
optionalBlocks: Boolean(state.gmoccapyGui?.optionalBlocks),
|
||||||
|
optionalStop: Boolean(state.gmoccapyGui?.optionalStop),
|
||||||
|
lastHalPinEffect: state.gmoccapyGui?.lastHalPinEffect,
|
||||||
|
},
|
||||||
|
mdiHistory: state.mdiHistory || [],
|
||||||
|
sessionPersistence: {
|
||||||
|
status: state.sessionPersistence?.status,
|
||||||
|
storageMode: state.sessionPersistence?.storageMode,
|
||||||
|
path: state.sessionPersistence?.path,
|
||||||
|
savedAt: state.sessionPersistence?.savedAt,
|
||||||
|
restoredAt: state.sessionPersistence?.restoredAt,
|
||||||
|
},
|
||||||
|
machineFileStaging: {
|
||||||
|
status: state.machineFileStaging?.status,
|
||||||
|
fileCount: state.machineFileStaging?.fileCount,
|
||||||
|
selectedGcodeSourceRel: state.machineFileStaging?.selectedGcodeSourceRel,
|
||||||
|
},
|
||||||
|
taskHalRuntimeReadiness: {
|
||||||
|
loaded: Boolean(state.taskHalRuntimeReadiness?.loaded),
|
||||||
|
halSyncReady: Boolean(state.taskHalRuntimeReadiness?.halSyncReady),
|
||||||
|
},
|
||||||
|
operatorMessage: state.operatorMessage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createStaticServer(rootDir) {
|
||||||
|
return http.createServer(async (request, response) => {
|
||||||
|
try {
|
||||||
|
const requestPath = decodeURIComponent(new URL(request.url || "/", "http://127.0.0.1").pathname);
|
||||||
|
const relativePath = requestPath === "/" ? "/index.html" : requestPath;
|
||||||
|
const targetPath = path.resolve(rootDir, `.${relativePath}`);
|
||||||
|
if (!targetPath.startsWith(rootDir)) {
|
||||||
|
response.writeHead(403);
|
||||||
|
response.end("forbidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let filePath = targetPath;
|
||||||
|
let stat = await fs.stat(filePath).catch(() => null);
|
||||||
|
if (stat?.isDirectory()) {
|
||||||
|
filePath = path.join(filePath, "index.html");
|
||||||
|
stat = await fs.stat(filePath).catch(() => null);
|
||||||
|
}
|
||||||
|
if (!stat?.isFile()) {
|
||||||
|
response.writeHead(404);
|
||||||
|
response.end("not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const body = await fs.readFile(filePath);
|
||||||
|
response.writeHead(200, {
|
||||||
|
"Content-Type": contentTypeFor(filePath),
|
||||||
|
"Content-Length": String(body.byteLength),
|
||||||
|
"Cache-Control": "no-store",
|
||||||
|
});
|
||||||
|
response.end(body);
|
||||||
|
} catch (error) {
|
||||||
|
response.writeHead(500);
|
||||||
|
response.end(error instanceof Error ? error.message : String(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function contentTypeFor(filePath) {
|
||||||
|
const ext = path.extname(filePath).toLowerCase();
|
||||||
|
return {
|
||||||
|
".css": "text/css; charset=utf-8",
|
||||||
|
".html": "text/html; charset=utf-8",
|
||||||
|
".js": "text/javascript; charset=utf-8",
|
||||||
|
".json": "application/json; charset=utf-8",
|
||||||
|
".mjs": "text/javascript; charset=utf-8",
|
||||||
|
".svg": "image/svg+xml",
|
||||||
|
".wasm": "application/wasm",
|
||||||
|
".xml": "application/xml; charset=utf-8",
|
||||||
|
}[ext] || "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function analyzePng(filePath) {
|
||||||
|
const png = PNG.sync.read(await fs.readFile(filePath));
|
||||||
|
let luminanceSum = 0;
|
||||||
|
let nonBlack = 0;
|
||||||
|
for (let index = 0; index < png.data.length; index += 4) {
|
||||||
|
const luminance = png.data[index] * 0.2126 + png.data[index + 1] * 0.7152 + png.data[index + 2] * 0.0722;
|
||||||
|
luminanceSum += luminance;
|
||||||
|
if (luminance > 8) nonBlack += 1;
|
||||||
|
}
|
||||||
|
const total = png.width * png.height;
|
||||||
|
return {
|
||||||
|
width: png.width,
|
||||||
|
height: png.height,
|
||||||
|
averageLuminance: Number((luminanceSum / total).toFixed(2)),
|
||||||
|
nonBlackRatio: Number((nonBlack / total).toFixed(4)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickAxes(value = {}) {
|
||||||
|
return {
|
||||||
|
x: Number(value?.x || 0),
|
||||||
|
y: Number(value?.y || 0),
|
||||||
|
z: Number(value?.z || 0),
|
||||||
|
a: Number(value?.a || 0),
|
||||||
|
b: Number(value?.b || 0),
|
||||||
|
c: Number(value?.c || 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(name, pass, detail) {
|
||||||
|
return { name, pass: Boolean(pass), detail: String(detail ?? "-") };
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(value) {
|
||||||
|
return String(value ?? "")
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function wait(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 257 KiB |
|
After Width: | Height: | Size: 257 KiB |
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 256 KiB |
|
After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 239 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 249 KiB |
|
After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 239 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 241 KiB |
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 229 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 223 KiB |
@@ -4,7 +4,9 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"evidence:working7": "node capture-working7-manual-flow-evidence.mjs",
|
||||||
|
"evidence:working7:full": "node capture-working7-full-functional-evidence.mjs",
|
||||||
|
"test": "node run-site-test.mjs"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
@@ -1,17 +1,72 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
|
import http from "node:http";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import puppeteer from "puppeteer-core";
|
import puppeteer from "puppeteer-core";
|
||||||
import { PNG } from "pngjs";
|
import { PNG } from "pngjs";
|
||||||
|
|
||||||
|
const REPO_ROOT = path.resolve("/home/meswork/cnc_wams");
|
||||||
const ROOT = path.resolve("/home/meswork/cnc_wams/qa/web-rtcp-5axis-site-test");
|
const ROOT = path.resolve("/home/meswork/cnc_wams/qa/web-rtcp-5axis-site-test");
|
||||||
const OUTPUT_DIR = path.join(ROOT, "output");
|
const OUTPUT_DIR = path.join(ROOT, "output");
|
||||||
const SCREENSHOT_DIR = path.join(ROOT, "screenshots");
|
const SCREENSHOT_DIR = path.join(ROOT, "screenshots");
|
||||||
const URL = "https://82.156.24.101:8092/";
|
const TARGET_URL = process.env.TARGET_URL || "";
|
||||||
const CHROME_PATH = "/usr/bin/google-chrome";
|
const APP_URL = process.env.APP_URL || "/web-rtcp-5axis-sim-plan/app/index.html";
|
||||||
|
const CHROME_PATH = process.env.CHROME_PATH || process.env.CHROMIUM || "/usr/bin/google-chrome";
|
||||||
|
|
||||||
const localProgramPath = path.join(ROOT, "fixtures", "test-program.ngc");
|
const localProgramPath = path.join(ROOT, "fixtures", "test-program.ngc");
|
||||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
const MIME_TYPES = {
|
||||||
|
".css": "text/css; charset=utf-8",
|
||||||
|
".html": "text/html; charset=utf-8",
|
||||||
|
".js": "text/javascript; charset=utf-8",
|
||||||
|
".json": "application/json; charset=utf-8",
|
||||||
|
".mjs": "text/javascript; charset=utf-8",
|
||||||
|
".png": "image/png",
|
||||||
|
".svg": "image/svg+xml",
|
||||||
|
".wasm": "application/wasm",
|
||||||
|
".xml": "application/xml; charset=utf-8",
|
||||||
|
};
|
||||||
|
|
||||||
|
function contentTypeFor(filePath) {
|
||||||
|
return MIME_TYPES[path.extname(filePath).toLowerCase()] || "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
function createStaticServer(rootDir) {
|
||||||
|
return http.createServer(async (request, response) => {
|
||||||
|
try {
|
||||||
|
const requestPath = decodeURIComponent(new URL(request.url || "/", "http://127.0.0.1").pathname);
|
||||||
|
const relativePath = requestPath === "/" ? "/index.html" : requestPath;
|
||||||
|
const targetPath = path.resolve(rootDir, `.${relativePath}`);
|
||||||
|
if (!targetPath.startsWith(rootDir)) {
|
||||||
|
response.writeHead(403);
|
||||||
|
response.end("forbidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let stat = await fs.stat(targetPath).catch(() => null);
|
||||||
|
let filePath = targetPath;
|
||||||
|
if (stat?.isDirectory()) {
|
||||||
|
filePath = path.join(targetPath, "index.html");
|
||||||
|
stat = await fs.stat(filePath).catch(() => null);
|
||||||
|
}
|
||||||
|
if (!stat?.isFile()) {
|
||||||
|
response.writeHead(404);
|
||||||
|
response.end("not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const body = await fs.readFile(filePath);
|
||||||
|
response.writeHead(200, {
|
||||||
|
"Content-Type": contentTypeFor(filePath),
|
||||||
|
"Content-Length": String(body.byteLength),
|
||||||
|
"Cache-Control": "no-store",
|
||||||
|
});
|
||||||
|
response.end(body);
|
||||||
|
} catch (error) {
|
||||||
|
response.writeHead(500);
|
||||||
|
response.end(error instanceof Error ? error.message : String(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await fs.mkdir(OUTPUT_DIR, { recursive: true });
|
await fs.mkdir(OUTPUT_DIR, { recursive: true });
|
||||||
await fs.mkdir(SCREENSHOT_DIR, { recursive: true });
|
await fs.mkdir(SCREENSHOT_DIR, { recursive: true });
|
||||||
await fs.mkdir(path.dirname(localProgramPath), { recursive: true });
|
await fs.mkdir(path.dirname(localProgramPath), { recursive: true });
|
||||||
@@ -20,6 +75,17 @@ const findings = [];
|
|||||||
const consoleLogs = [];
|
const consoleLogs = [];
|
||||||
const pageErrors = [];
|
const pageErrors = [];
|
||||||
const requestFailures = [];
|
const requestFailures = [];
|
||||||
|
let server = null;
|
||||||
|
let targetUrl = TARGET_URL;
|
||||||
|
if (!targetUrl) {
|
||||||
|
server = createStaticServer(REPO_ROOT);
|
||||||
|
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||||
|
const address = server.address();
|
||||||
|
if (!address || typeof address === "string") {
|
||||||
|
throw new Error("failed to start local static server");
|
||||||
|
}
|
||||||
|
targetUrl = `http://127.0.0.1:${address.port}${APP_URL}`;
|
||||||
|
}
|
||||||
|
|
||||||
const browser = await puppeteer.launch({
|
const browser = await puppeteer.launch({
|
||||||
headless: true,
|
headless: true,
|
||||||
@@ -83,7 +149,7 @@ try {
|
|||||||
"",
|
"",
|
||||||
].join("\n"), "utf8");
|
].join("\n"), "utf8");
|
||||||
|
|
||||||
await page.goto(URL, { waitUntil: "networkidle2", timeout: 60000 });
|
await page.goto(targetUrl, { waitUntil: "networkidle2", timeout: 60000 });
|
||||||
await page.waitForSelector('[data-shell="gmoccapy-5axis"]', { timeout: 15000 });
|
await page.waitForSelector('[data-shell="gmoccapy-5axis"]', { timeout: 15000 });
|
||||||
await page.waitForFunction(() => Boolean(window.webRtcp5AxisSimulation?.getState), { timeout: 15000 });
|
await page.waitForFunction(() => Boolean(window.webRtcp5AxisSimulation?.getState), { timeout: 15000 });
|
||||||
await page.waitForFunction(() => {
|
await page.waitForFunction(() => {
|
||||||
@@ -140,6 +206,34 @@ try {
|
|||||||
).catch(() => null);
|
).catch(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitForTaskHalReady(timeoutMs = 30000) {
|
||||||
|
return waitForState((state) => (
|
||||||
|
state.taskHalRuntimeReadiness?.loaded === true &&
|
||||||
|
state.taskHalRuntimeReadiness?.taskRuntimeReady === true &&
|
||||||
|
state.taskHalRuntimeReadiness?.motionRuntimeReady === true &&
|
||||||
|
state.taskHalRuntimeReadiness?.halRuntimeReady === true &&
|
||||||
|
state.taskHalRuntimeReadiness?.halSyncReady === true &&
|
||||||
|
!state.taskHalExecutionPending &&
|
||||||
|
!state.interpreterExecutionPending
|
||||||
|
), timeoutMs, "Task/HAL ready and app idle").catch(() => null);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clickJogAndWait(action, axis, direction, beforeValue) {
|
||||||
|
const selector = `[data-action="${action}"]`;
|
||||||
|
await waitForTaskHalReady(30000);
|
||||||
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
||||||
|
await clickAndWait(selector, 900);
|
||||||
|
const changed = await waitForState((nextState) => {
|
||||||
|
const nextValue = Number(nextState.axisPose?.[axis]);
|
||||||
|
return direction > 0
|
||||||
|
? nextValue > Number(beforeValue)
|
||||||
|
: nextValue < Number(beforeValue);
|
||||||
|
}, 6000, `${action} axis change`).catch(() => null);
|
||||||
|
if (changed) return changed;
|
||||||
|
}
|
||||||
|
return getState();
|
||||||
|
}
|
||||||
|
|
||||||
async function getSummary() {
|
async function getSummary() {
|
||||||
return page.evaluate(() => {
|
return page.evaluate(() => {
|
||||||
const state = window.webRtcp5AxisSimulation.getState();
|
const state = window.webRtcp5AxisSimulation.getState();
|
||||||
@@ -179,12 +273,31 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function clickAndWait(selector, waitMs = 600) {
|
async function clickAndWait(selector, waitMs = 600) {
|
||||||
await page.click(selector);
|
await page.waitForSelector(selector, { timeout: 10000 });
|
||||||
|
await page.waitForFunction((targetSelector) => {
|
||||||
|
const element = document.querySelector(targetSelector);
|
||||||
|
if (!element) return false;
|
||||||
|
const ariaDisabled = element.getAttribute("aria-disabled") === "true";
|
||||||
|
const commandReady = element.getAttribute("data-command-ready") === "false";
|
||||||
|
return !element.disabled && !ariaDisabled && !commandReady;
|
||||||
|
}, { timeout: 15000 }, selector);
|
||||||
|
await page.evaluate((targetSelector) => {
|
||||||
|
const element = document.querySelector(targetSelector);
|
||||||
|
if (!element) {
|
||||||
|
throw new Error(`missing click target ${targetSelector}`);
|
||||||
|
}
|
||||||
|
if (element.disabled || element.getAttribute("aria-disabled") === "true") {
|
||||||
|
throw new Error(`disabled click target ${targetSelector}`);
|
||||||
|
}
|
||||||
|
element.scrollIntoView({ block: "center", inline: "center" });
|
||||||
|
element.click();
|
||||||
|
}, selector);
|
||||||
await sleep(waitMs);
|
await sleep(waitMs);
|
||||||
await waitForAppIdle();
|
await waitForAppIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setInputValue(selector, value) {
|
async function setInputValue(selector, value) {
|
||||||
|
await page.waitForSelector(selector, { timeout: 10000 });
|
||||||
await page.$eval(selector, (el, nextValue) => {
|
await page.$eval(selector, (el, nextValue) => {
|
||||||
el.value = nextValue;
|
el.value = nextValue;
|
||||||
el.dispatchEvent(new Event("input", { bubbles: true }));
|
el.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
@@ -193,6 +306,21 @@ try {
|
|||||||
await sleep(400);
|
await sleep(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function selectValue(selector, value) {
|
||||||
|
await page.waitForSelector(selector, { timeout: 10000 });
|
||||||
|
await page.evaluate((targetSelector, nextValue) => {
|
||||||
|
const element = document.querySelector(targetSelector);
|
||||||
|
if (!element) {
|
||||||
|
throw new Error(`missing select target ${targetSelector}`);
|
||||||
|
}
|
||||||
|
element.value = nextValue;
|
||||||
|
element.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
element.dispatchEvent(new Event("change", { bubbles: true }));
|
||||||
|
}, selector, value);
|
||||||
|
await sleep(400);
|
||||||
|
await waitForAppIdle();
|
||||||
|
}
|
||||||
|
|
||||||
function classify(condition, passText, failText) {
|
function classify(condition, passText, failText) {
|
||||||
return condition ? { status: "PASS", text: passText } : { status: "FAIL", text: failText };
|
return condition ? { status: "PASS", text: passText } : { status: "FAIL", text: failText };
|
||||||
}
|
}
|
||||||
@@ -253,10 +381,11 @@ try {
|
|||||||
initialBoundaryStatus.status,
|
initialBoundaryStatus.status,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const taskHalReadyState = await waitForTaskHalReady(30000);
|
||||||
const initialTaskHalStatus = classifyWarn(
|
const initialTaskHalStatus = classifyWarn(
|
||||||
!/pending|blocked/i.test(initial.taskHal),
|
Boolean(taskHalReadyState),
|
||||||
`Task/HAL 已就绪:${initial.taskHal}`,
|
`Task/HAL 已就绪:${taskHalReadyState?.taskHalRuntimeReadiness?.semanticBoundary || initial.taskHal}`,
|
||||||
`Task/HAL 未完成就绪:${initial.taskHal}`,
|
`Task/HAL 冷启动时尚未完成就绪:${initial.taskHal}`,
|
||||||
);
|
);
|
||||||
await recordResult(
|
await recordResult(
|
||||||
"initial-task-hal",
|
"initial-task-hal",
|
||||||
@@ -290,6 +419,7 @@ try {
|
|||||||
|
|
||||||
await clickAndWait('[data-action="HOME"]');
|
await clickAndWait('[data-action="HOME"]');
|
||||||
await waitForState((nextState) => nextState.machine.allHomed === true, 8000, "machine homed").catch(() => null);
|
await waitForState((nextState) => nextState.machine.allHomed === true, 8000, "machine homed").catch(() => null);
|
||||||
|
await waitForTaskHalReady(30000);
|
||||||
state = await getState();
|
state = await getState();
|
||||||
await recordResult(
|
await recordResult(
|
||||||
"home",
|
"home",
|
||||||
@@ -300,8 +430,7 @@ try {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const beforeJogX = state.axisPose.x;
|
const beforeJogX = state.axisPose.x;
|
||||||
await clickAndWait('[data-action="JOG_X_POS"]');
|
state = await clickJogAndWait("JOG_X_POS", "x", 1, beforeJogX);
|
||||||
state = await getState();
|
|
||||||
await recordResult(
|
await recordResult(
|
||||||
"jog-x-plus",
|
"jog-x-plus",
|
||||||
"JOG X+",
|
"JOG X+",
|
||||||
@@ -311,8 +440,7 @@ try {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const beforeJogY = state.axisPose.y;
|
const beforeJogY = state.axisPose.y;
|
||||||
await clickAndWait('[data-action="JOG_Y_NEG"]');
|
state = await clickJogAndWait("JOG_Y_NEG", "y", -1, beforeJogY);
|
||||||
state = await getState();
|
|
||||||
await recordResult(
|
await recordResult(
|
||||||
"jog-y-minus",
|
"jog-y-minus",
|
||||||
"JOG Y-",
|
"JOG Y-",
|
||||||
@@ -332,6 +460,9 @@ try {
|
|||||||
state.machine.mode === "auto" ? "PASS" : "FAIL",
|
state.machine.mode === "auto" ? "PASS" : "FAIL",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await clickAndWait('[data-action="mode-manual"]');
|
||||||
|
await waitForState((nextState) => nextState.machine.mode === "manual" && nextState.machine.interpState === "idle", 10000, "MANUAL idle before MDI").catch(() => null);
|
||||||
|
await waitForTaskHalReady(30000);
|
||||||
await clickAndWait('[data-action="mode-mdi"]');
|
await clickAndWait('[data-action="mode-mdi"]');
|
||||||
await waitForState((nextState) => nextState.machine.mode === "mdi", 8000, "MDI mode").catch(() => null);
|
await waitForState((nextState) => nextState.machine.mode === "mdi", 8000, "MDI mode").catch(() => null);
|
||||||
state = await getState();
|
state = await getState();
|
||||||
@@ -343,7 +474,17 @@ try {
|
|||||||
state.machine.mode === "mdi" ? "PASS" : "FAIL",
|
state.machine.mode === "mdi" ? "PASS" : "FAIL",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (state.machine.mode !== "mdi") {
|
||||||
|
await clickAndWait('[data-action="mode-manual"]');
|
||||||
|
await waitForState((nextState) => nextState.machine.mode === "manual", 10000, "retry manual before MDI").catch(() => null);
|
||||||
|
await waitForTaskHalReady(30000);
|
||||||
|
await clickAndWait('[data-action="mode-mdi"]');
|
||||||
|
await waitForState((nextState) => nextState.machine.mode === "mdi", 10000, "retry MDI mode").catch(() => null);
|
||||||
|
state = await getState();
|
||||||
|
}
|
||||||
|
|
||||||
await setInputValue('[data-action="mdi-command"]', "M428");
|
await setInputValue('[data-action="mdi-command"]', "M428");
|
||||||
|
await waitForState((nextState) => nextState.machine.mode === "mdi", 10000, "MDI active before M428");
|
||||||
await clickAndWait('[data-action="mdi-submit"]', 800);
|
await clickAndWait('[data-action="mdi-submit"]', 800);
|
||||||
state = await getState();
|
state = await getState();
|
||||||
await recordResult(
|
await recordResult(
|
||||||
@@ -493,7 +634,7 @@ try {
|
|||||||
fullOn && stateAfterFullOff.preview.fullscreen === false ? "PASS" : "FAIL",
|
fullOn && stateAfterFullOff.preview.fullscreen === false ? "PASS" : "FAIL",
|
||||||
);
|
);
|
||||||
|
|
||||||
await page.select('[data-action="select-profile"]', "xyzbc-trt");
|
await selectValue('[data-action="select-profile"]', "xyzbc-trt");
|
||||||
await sleep(2000);
|
await sleep(2000);
|
||||||
state = await getState();
|
state = await getState();
|
||||||
await recordResult(
|
await recordResult(
|
||||||
@@ -505,7 +646,7 @@ try {
|
|||||||
);
|
);
|
||||||
await capture("02-profile-xyzbc");
|
await capture("02-profile-xyzbc");
|
||||||
|
|
||||||
await page.select('[data-action="select-profile"]', "xyzac-trt");
|
await selectValue('[data-action="select-profile"]', "xyzac-trt");
|
||||||
await sleep(2000);
|
await sleep(2000);
|
||||||
state = await getState();
|
state = await getState();
|
||||||
await recordResult(
|
await recordResult(
|
||||||
@@ -534,7 +675,7 @@ try {
|
|||||||
|
|
||||||
if (stagedCount > 0) {
|
if (stagedCount > 0) {
|
||||||
const sourceRel = summary.state.machineFileStaging.gcodeSources[0].sourceRel;
|
const sourceRel = summary.state.machineFileStaging.gcodeSources[0].sourceRel;
|
||||||
await page.select('[data-action="select-linuxcnc-gcode-source"]', sourceRel);
|
await selectValue('[data-action="select-linuxcnc-gcode-source"]', sourceRel);
|
||||||
await sleep(4000);
|
await sleep(4000);
|
||||||
summary = await getSummary();
|
summary = await getSummary();
|
||||||
const loadedVendored = summary.state.programSource === "linuxcnc-vendored-5axis-gcode";
|
const loadedVendored = summary.state.programSource === "linuxcnc-vendored-5axis-gcode";
|
||||||
@@ -604,9 +745,13 @@ try {
|
|||||||
await recordResult(
|
await recordResult(
|
||||||
"step-program",
|
"step-program",
|
||||||
"Step 单步执行",
|
"Step 单步执行",
|
||||||
"点击 Step 后 runState=stepping,activeLine 前进或保持受控",
|
"点击 Step 后 Task/HAL 记录 singleStepping=true,并保持暂停态等待后续操作",
|
||||||
`runState=${state.runState}, activeLine=${state.activeLine}`,
|
`runState=${state.runState}, activeLine=${state.activeLine}, taskPaused=${state.machine.taskPaused}, singleStepping=${state.taskHalStatus?.task?.singleStepping}`,
|
||||||
state.runState === "stepping" || state.activeLine !== beforeStepLine ? "PASS" : "FAIL",
|
state.machine.taskPaused === true
|
||||||
|
&& state.taskHalStatus?.task?.singleStepping === true
|
||||||
|
&& Number(state.activeLine) >= Number(beforeStepLine)
|
||||||
|
? "PASS"
|
||||||
|
: "FAIL",
|
||||||
);
|
);
|
||||||
|
|
||||||
await clickAndWait('[data-action="STOP"]', 1200);
|
await clickAndWait('[data-action="STOP"]', 1200);
|
||||||
@@ -657,7 +802,10 @@ try {
|
|||||||
saveSessionStatus.status,
|
saveSessionStatus.status,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await clickAndWait('[data-action="mode-manual"]');
|
||||||
|
await waitForState((nextState) => nextState.machine.mode === "manual", 8000, "manual mode before session mutation").catch(() => null);
|
||||||
await clickAndWait('[data-action="JOG_X_POS"]');
|
await clickAndWait('[data-action="JOG_X_POS"]');
|
||||||
|
await waitForState((nextState) => Number(nextState.axisPose.x) !== Number(state.axisPose.x), 8000, "axis changed before restore").catch(() => null);
|
||||||
const modifiedState = await getState();
|
const modifiedState = await getState();
|
||||||
await clickAndWait('[data-action="RESTORE_SESSION"]', 2500);
|
await clickAndWait('[data-action="RESTORE_SESSION"]', 2500);
|
||||||
state = await getState();
|
state = await getState();
|
||||||
@@ -712,7 +860,7 @@ try {
|
|||||||
|
|
||||||
const report = {
|
const report = {
|
||||||
generatedAt: new Date().toISOString(),
|
generatedAt: new Date().toISOString(),
|
||||||
targetUrl: URL,
|
targetUrl,
|
||||||
chromePath: CHROME_PATH,
|
chromePath: CHROME_PATH,
|
||||||
screenshots,
|
screenshots,
|
||||||
screenshotAnalysis,
|
screenshotAnalysis,
|
||||||
@@ -722,10 +870,20 @@ try {
|
|||||||
pageErrors,
|
pageErrors,
|
||||||
requestFailures,
|
requestFailures,
|
||||||
};
|
};
|
||||||
|
report.status = findings.some((finding) => finding.status === "FAIL") || pageErrors.length > 0
|
||||||
|
? "FAIL"
|
||||||
|
: "PASS";
|
||||||
|
|
||||||
await fs.writeFile(path.join(OUTPUT_DIR, "site-test-report.json"), `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
await fs.writeFile(path.join(OUTPUT_DIR, "site-test-report.json"), `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
||||||
|
console.log(`site_test_status=${report.status}`);
|
||||||
|
console.log(`site_test_target_url=${targetUrl}`);
|
||||||
|
console.log(`site_test_report=/home/meswork/cnc_wams/qa/web-rtcp-5axis-site-test/output/site-test-report.json`);
|
||||||
|
if (report.status !== "PASS") {
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await browser.close();
|
await browser.close().catch(() => {});
|
||||||
|
if (server) await new Promise((resolve) => server.close(resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function analyzeScreenshot(filePath) {
|
async function analyzeScreenshot(filePath) {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 165 KiB After Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 247 KiB |
@@ -23,10 +23,27 @@ static std::vector<std::string> g_canon_events;
|
|||||||
static double g_external_feed_rate = 0.0;
|
static double g_external_feed_rate = 0.0;
|
||||||
static int g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
|
static int g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
|
||||||
static std::string g_parameter_file_name;
|
static std::string g_parameter_file_name;
|
||||||
|
static CANON_PLANE g_external_plane = CANON_PLANE::XY;
|
||||||
|
|
||||||
|
struct ExternalPosition {
|
||||||
|
double x = 0.0;
|
||||||
|
double y = 0.0;
|
||||||
|
double z = 0.0;
|
||||||
|
double a = 0.0;
|
||||||
|
double b = 0.0;
|
||||||
|
double c = 0.0;
|
||||||
|
double u = 0.0;
|
||||||
|
double v = 0.0;
|
||||||
|
double w = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ExternalPosition g_external_position;
|
||||||
|
|
||||||
void reset_canon_events()
|
void reset_canon_events()
|
||||||
{
|
{
|
||||||
g_canon_events.clear();
|
g_canon_events.clear();
|
||||||
|
g_external_position = ExternalPosition{};
|
||||||
|
g_external_plane = CANON_PLANE::XY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_canon_event(const std::string &event)
|
void push_canon_event(const std::string &event)
|
||||||
@@ -56,6 +73,65 @@ int external_axis_mask()
|
|||||||
return g_external_axis_mask;
|
return g_external_axis_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_external_position(double x, double y, double z,
|
||||||
|
double a, double b, double c,
|
||||||
|
double u, double v, double w)
|
||||||
|
{
|
||||||
|
g_external_position.x = x;
|
||||||
|
g_external_position.y = y;
|
||||||
|
g_external_position.z = z;
|
||||||
|
g_external_position.a = a;
|
||||||
|
g_external_position.b = b;
|
||||||
|
g_external_position.c = c;
|
||||||
|
g_external_position.u = u;
|
||||||
|
g_external_position.v = v;
|
||||||
|
g_external_position.w = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_external_arc_position(double first_end, double second_end,
|
||||||
|
double axis_end_point,
|
||||||
|
double a, double b, double c,
|
||||||
|
double u, double v, double w)
|
||||||
|
{
|
||||||
|
ExternalPosition next = g_external_position;
|
||||||
|
switch (g_external_plane) {
|
||||||
|
case CANON_PLANE::XY:
|
||||||
|
next.x = first_end;
|
||||||
|
next.y = second_end;
|
||||||
|
next.z = axis_end_point;
|
||||||
|
break;
|
||||||
|
case CANON_PLANE::YZ:
|
||||||
|
next.x = axis_end_point;
|
||||||
|
next.y = first_end;
|
||||||
|
next.z = second_end;
|
||||||
|
break;
|
||||||
|
case CANON_PLANE::XZ:
|
||||||
|
next.x = second_end;
|
||||||
|
next.y = axis_end_point;
|
||||||
|
next.z = first_end;
|
||||||
|
break;
|
||||||
|
case CANON_PLANE::UV:
|
||||||
|
next.u = first_end;
|
||||||
|
next.v = second_end;
|
||||||
|
next.w = axis_end_point;
|
||||||
|
break;
|
||||||
|
case CANON_PLANE::VW:
|
||||||
|
next.u = axis_end_point;
|
||||||
|
next.v = first_end;
|
||||||
|
next.w = second_end;
|
||||||
|
break;
|
||||||
|
case CANON_PLANE::UW:
|
||||||
|
next.u = second_end;
|
||||||
|
next.v = axis_end_point;
|
||||||
|
next.w = first_end;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next.a = a;
|
||||||
|
next.b = b;
|
||||||
|
next.c = c;
|
||||||
|
g_external_position = next;
|
||||||
|
}
|
||||||
|
|
||||||
void reset_parameter_file_name()
|
void reset_parameter_file_name()
|
||||||
{
|
{
|
||||||
g_parameter_file_name.clear();
|
g_parameter_file_name.clear();
|
||||||
@@ -138,6 +214,7 @@ void CANON_UPDATE_END_POINT(double x, double y, double z,
|
|||||||
double a, double b, double c,
|
double a, double b, double c,
|
||||||
double u, double v, double w)
|
double u, double v, double w)
|
||||||
{
|
{
|
||||||
|
standalone::set_external_position(x, y, z, a, b, c, u, v, w);
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "CANON_UPDATE_END_POINT"
|
oss << "CANON_UPDATE_END_POINT"
|
||||||
<< " x=" << x
|
<< " x=" << x
|
||||||
@@ -159,6 +236,7 @@ void USE_LENGTH_UNITS(CANON_UNITS units)
|
|||||||
}
|
}
|
||||||
void SELECT_PLANE(CANON_PLANE plane)
|
void SELECT_PLANE(CANON_PLANE plane)
|
||||||
{
|
{
|
||||||
|
standalone::g_external_plane = plane;
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "SELECT_PLANE plane=" << static_cast<int>(plane);
|
oss << "SELECT_PLANE plane=" << static_cast<int>(plane);
|
||||||
standalone::push_canon_event(oss.str());
|
standalone::push_canon_event(oss.str());
|
||||||
@@ -515,16 +593,16 @@ void SET_PARAMETER_FILE_NAME(const char *filename)
|
|||||||
{
|
{
|
||||||
standalone::set_parameter_file_name(filename);
|
standalone::set_parameter_file_name(filename);
|
||||||
}
|
}
|
||||||
CANON_PLANE GET_EXTERNAL_PLANE() { return CANON_PLANE::XY; }
|
CANON_PLANE GET_EXTERNAL_PLANE() { return standalone::g_external_plane; }
|
||||||
double GET_EXTERNAL_POSITION_A() { return 0.0; }
|
double GET_EXTERNAL_POSITION_A() { return standalone::g_external_position.a; }
|
||||||
double GET_EXTERNAL_POSITION_B() { return 0.0; }
|
double GET_EXTERNAL_POSITION_B() { return standalone::g_external_position.b; }
|
||||||
double GET_EXTERNAL_POSITION_C() { return 0.0; }
|
double GET_EXTERNAL_POSITION_C() { return standalone::g_external_position.c; }
|
||||||
double GET_EXTERNAL_POSITION_X() { return 0.0; }
|
double GET_EXTERNAL_POSITION_X() { return standalone::g_external_position.x; }
|
||||||
double GET_EXTERNAL_POSITION_Y() { return 0.0; }
|
double GET_EXTERNAL_POSITION_Y() { return standalone::g_external_position.y; }
|
||||||
double GET_EXTERNAL_POSITION_Z() { return 0.0; }
|
double GET_EXTERNAL_POSITION_Z() { return standalone::g_external_position.z; }
|
||||||
double GET_EXTERNAL_POSITION_U() { return 0.0; }
|
double GET_EXTERNAL_POSITION_U() { return standalone::g_external_position.u; }
|
||||||
double GET_EXTERNAL_POSITION_V() { return 0.0; }
|
double GET_EXTERNAL_POSITION_V() { return standalone::g_external_position.v; }
|
||||||
double GET_EXTERNAL_POSITION_W() { return 0.0; }
|
double GET_EXTERNAL_POSITION_W() { return standalone::g_external_position.w; }
|
||||||
double GET_EXTERNAL_PROBE_POSITION_A() { return 0.0; }
|
double GET_EXTERNAL_PROBE_POSITION_A() { return 0.0; }
|
||||||
double GET_EXTERNAL_PROBE_POSITION_B() { return 0.0; }
|
double GET_EXTERNAL_PROBE_POSITION_B() { return 0.0; }
|
||||||
double GET_EXTERNAL_PROBE_POSITION_C() { return 0.0; }
|
double GET_EXTERNAL_PROBE_POSITION_C() { return 0.0; }
|
||||||
@@ -629,6 +707,7 @@ void STRAIGHT_TRAVERSE(int lineno,
|
|||||||
double a, double b, double c,
|
double a, double b, double c,
|
||||||
double u, double v, double w)
|
double u, double v, double w)
|
||||||
{
|
{
|
||||||
|
standalone::set_external_position(x, y, z, a, b, c, u, v, w);
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "STRAIGHT_TRAVERSE line=" << lineno
|
oss << "STRAIGHT_TRAVERSE line=" << lineno
|
||||||
<< " x=" << x
|
<< " x=" << x
|
||||||
@@ -648,6 +727,7 @@ void STRAIGHT_FEED(int lineno,
|
|||||||
double a, double b, double c,
|
double a, double b, double c,
|
||||||
double u, double v, double w)
|
double u, double v, double w)
|
||||||
{
|
{
|
||||||
|
standalone::set_external_position(x, y, z, a, b, c, u, v, w);
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "STRAIGHT_FEED line=" << lineno
|
oss << "STRAIGHT_FEED line=" << lineno
|
||||||
<< " x=" << x
|
<< " x=" << x
|
||||||
@@ -669,6 +749,8 @@ void ARC_FEED(int lineno,
|
|||||||
double a, double b, double c,
|
double a, double b, double c,
|
||||||
double u, double v, double w)
|
double u, double v, double w)
|
||||||
{
|
{
|
||||||
|
standalone::set_external_arc_position(first_end, second_end, axis_end_point,
|
||||||
|
a, b, c, u, v, w);
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "ARC_FEED line=" << lineno
|
oss << "ARC_FEED line=" << lineno
|
||||||
<< " first_end=" << first_end
|
<< " first_end=" << first_end
|
||||||
|
|||||||
@@ -870,6 +870,7 @@ char *lcinterp_run_fiveaxis_remap_file(const char *path, const char *ini_path)
|
|||||||
ok &= file_execute_count < max_file_steps;
|
ok &= file_execute_count < max_file_steps;
|
||||||
ok &= hal_rc == INTERP_OK && hal_status == 1;
|
ok &= hal_rc == INTERP_OK && hal_status == 1;
|
||||||
|
|
||||||
|
append_events_and_state(output, interp);
|
||||||
output << "fiveaxis_linuxcnc_remap_file_execute=" << (ok ? 1 : 0) << "\n";
|
output << "fiveaxis_linuxcnc_remap_file_execute=" << (ok ? 1 : 0) << "\n";
|
||||||
unsetenv("INI_FILE_NAME");
|
unsetenv("INI_FILE_NAME");
|
||||||
return copy_result(output.str());
|
return copy_result(output.str());
|
||||||
|
|||||||
@@ -603,3 +603,45 @@ nc_files/arcspiral.ngc
|
|||||||
nc_files/factorial.ngc
|
nc_files/factorial.ngc
|
||||||
nc_files/hole-circle.ngc
|
nc_files/hole-circle.ngc
|
||||||
nc_files/m6demo.ngc
|
nc_files/m6demo.ngc
|
||||||
|
configs/sim/gmoccapy/macros/change.ngc
|
||||||
|
configs/sim/gmoccapy/macros/change_g43.ngc
|
||||||
|
configs/sim/gmoccapy/macros/go_to_position.ngc
|
||||||
|
configs/sim/gmoccapy/macros/halo_world.ngc
|
||||||
|
configs/sim/gmoccapy/macros/i_am_lost.ngc
|
||||||
|
configs/sim/gmoccapy/macros/images/goto_x_y_z.png
|
||||||
|
configs/sim/gmoccapy/macros/images/i_am_lost.png
|
||||||
|
configs/sim/gmoccapy/macros/images/macro_8.png
|
||||||
|
configs/sim/gmoccapy/macros/increment.ngc
|
||||||
|
configs/sim/gmoccapy/macros/jog_around.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_0.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_1.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_10.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_11.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_12.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_13.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_14.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_15.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_2.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_3.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_4.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_5.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_6.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_7.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_8.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_9.ngc
|
||||||
|
configs/sim/gmoccapy/macros/macro_Instructions.txt
|
||||||
|
configs/sim/gmoccapy/macros/on_abort.ngc
|
||||||
|
configs/sim/gmoccapy/macros/settool_g43.ngc
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/README
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzac.ngc
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/test-xyzac.ngc
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/test-xyzbc.ngc
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/postgui.hal
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/sim-xyzac-trt.pref
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac-trt.tbl
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac-trt_cmds.hal
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac.var
|
||||||
|
configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac.var.bak
|
||||||
|
|||||||
75
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/change.ngc
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
o<change> sub
|
||||||
|
;(debug, in change tool_in_spindle=#<tool_in_spindle> current_pocket=#<current_pocket>)
|
||||||
|
;(debug, selected_tool=#<selected_tool> selected_pocket=#<selected_pocket>)
|
||||||
|
|
||||||
|
;otherwise after the M6 this information is gone!
|
||||||
|
#<tool> = #<selected_tool>
|
||||||
|
#<pocket> = #<selected_pocket>
|
||||||
|
|
||||||
|
; we must execute this only in the milltask interpreter
|
||||||
|
; or preview will break, so test for '#<_task>' which is 1 for
|
||||||
|
; the milltask interpreter and 0 in the UI's
|
||||||
|
O100 if [#<_task> EQ 0]
|
||||||
|
(debug, Task ist Null)
|
||||||
|
O100 return [999]
|
||||||
|
O100 endif
|
||||||
|
|
||||||
|
;first go up
|
||||||
|
G53 G0 Z[#<_ini[CHANGE_POSITION]Z>]
|
||||||
|
; then move to change position
|
||||||
|
G53 G0 X[#<_ini[CHANGE_POSITION]X>] Y[#<_ini[CHANGE_POSITION]Y>]
|
||||||
|
|
||||||
|
; cancel tool offset
|
||||||
|
G49
|
||||||
|
|
||||||
|
; using the code being remapped here means 'use builtin behaviour'
|
||||||
|
M6
|
||||||
|
|
||||||
|
O200 if [#<_hal[gmoccapy.toolmeasurement]> EQ 0]
|
||||||
|
O200 return [3] ; indicate no tool measurement
|
||||||
|
O200 endif
|
||||||
|
|
||||||
|
G53 G0 X[#<_ini[TOOLSENSOR]X>] Y[#<_ini[TOOLSENSOR]Y>]
|
||||||
|
G53 G0 Z[#<_ini[TOOLSENSOR]Z>]
|
||||||
|
|
||||||
|
O300 if [#<_hal[gmoccapy.searchvel]> LE 0]
|
||||||
|
O300 return [-1] ; indicate searchvel <= 0
|
||||||
|
O300 endif
|
||||||
|
|
||||||
|
O400 if [#<_hal[gmoccapy.probevel]> LE 0]
|
||||||
|
O400 return [-2] ; indicate probevel <= 0
|
||||||
|
O400 endif
|
||||||
|
|
||||||
|
F #<_hal[gmoccapy.searchvel]>
|
||||||
|
G91
|
||||||
|
G38.2 Z #<_ini[TOOLSENSOR]MAXPROBE>
|
||||||
|
G0 Z2
|
||||||
|
; This is commented out only for sim.
|
||||||
|
;F #<_hal[gmoccapy.probevel]>
|
||||||
|
;G38.2 Z-4
|
||||||
|
|
||||||
|
O500 if [#5070 EQ 0]
|
||||||
|
G90
|
||||||
|
O500 return [-3] ; indicate probe contact failure to epilog
|
||||||
|
O500 endif
|
||||||
|
|
||||||
|
G90
|
||||||
|
G53 G0 Z[#<_ini[CHANGE_POSITION]Z>]
|
||||||
|
|
||||||
|
#<touch_result> = #5063
|
||||||
|
#<probeheight> = #<_hal[gmoccapy.probeheight]>
|
||||||
|
#<blockheight> = #<_hal[gmoccapy.blockheight]>
|
||||||
|
|
||||||
|
;(DEBUG, #<touch_result> #<probeheight> #<blockheight>)
|
||||||
|
|
||||||
|
G10 L1 P#<tool> Z[#<touch_result> - #<_hal[gmoccapy.probeheight]> + #<_hal[gmoccapy.blockheight]>]
|
||||||
|
G43
|
||||||
|
|
||||||
|
;G10 L1 P#<tool> Z#<touch_result>
|
||||||
|
;G10 L2 P0 Z[#<workpieceheight> + #<probeheight> + #<touch_result>]
|
||||||
|
|
||||||
|
; signal success be returning a value > 0:
|
||||||
|
o<change> endsub [1]
|
||||||
|
|
||||||
|
|
||||||
21
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/change_g43.ngc
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
o<change_g43> sub
|
||||||
|
;(debug, in change tool_in_spindle=#<tool_in_spindle> current_pocket=#<current_pocket>)
|
||||||
|
;(debug, selected_tool=#<selected_tool> selected_pocket=#<selected_pocket>)
|
||||||
|
|
||||||
|
; we must execute this only in the milltask interpreter
|
||||||
|
; or preview will break, so test for '#<_task>' which is 1 for
|
||||||
|
; the milltask interpreter and 0 in the UI's
|
||||||
|
O100 if [#<_task> EQ 0]
|
||||||
|
(debug, Task ist Null)
|
||||||
|
O100 return [999]
|
||||||
|
O100 endif
|
||||||
|
|
||||||
|
; using the code being remapped here means 'use builtin behaviour'
|
||||||
|
M6
|
||||||
|
|
||||||
|
; set tool offset
|
||||||
|
G43
|
||||||
|
|
||||||
|
; signal success be returning a value > 0:
|
||||||
|
o<change_g43> endsub [1]
|
||||||
|
M2
|
||||||
26
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/go_to_position.ngc
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a given position
|
||||||
|
; the image path must be relative from your config dir or absolute, "~" is allowed
|
||||||
|
(IMAGE, ./macros/images/goto_x_y_z.png)
|
||||||
|
|
||||||
|
O<go_to_position> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<go_to_position> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
25
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/halo_world.ngc
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
; Testfile "hello world"
|
||||||
|
; will just give messages
|
||||||
|
|
||||||
|
O<halo_world> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
G0 X10
|
||||||
|
|
||||||
|
(MSG, Hallo Welt)
|
||||||
|
(MSG, hello world)
|
||||||
|
|
||||||
|
G0X-10
|
||||||
|
|
||||||
|
O<halo_world> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
|
|
||||||
27
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/i_am_lost.ngc
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
; Testfile I am Lost
|
||||||
|
; will jog to machine zero and set all axis to zero
|
||||||
|
|
||||||
|
; the image path must be relative from your config dir or absolute, "~" is allowed
|
||||||
|
(IMAGE, ./macros/images/i_am_lost.png)
|
||||||
|
|
||||||
|
O<i_am_lost> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
(MSG, Will now move to machine zero)
|
||||||
|
G53 G0 X0 Y0 Z0
|
||||||
|
(MSG, will now set all axis to zero)
|
||||||
|
G10 L20 P0 X0 Y0 Z0
|
||||||
|
(MSG, all done)
|
||||||
|
|
||||||
|
|
||||||
|
O<i_am_lost> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
BIN
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/images/goto_x_y_z.png
vendored
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/images/i_am_lost.png
vendored
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/images/macro_8.png
vendored
Normal file
|
After Width: | Height: | Size: 217 KiB |
22
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/increment.ngc
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
; Testfile "increment"
|
||||||
|
; will move the machine in relative coordinates
|
||||||
|
|
||||||
|
O<increment> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
G91 G0 X#1 Y#2
|
||||||
|
G90
|
||||||
|
|
||||||
|
(DEBUG, X was [#1] and Y was [#2])
|
||||||
|
|
||||||
|
O<increment> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
29
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/jog_around.ngc
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
; Testfile "Jog around"
|
||||||
|
; will just jog a little bit around
|
||||||
|
|
||||||
|
O<jog_around> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
G91 G0 X 25
|
||||||
|
Y-25
|
||||||
|
Z-25
|
||||||
|
Y25
|
||||||
|
X-25
|
||||||
|
Z25
|
||||||
|
F250
|
||||||
|
G2 I 25
|
||||||
|
|
||||||
|
(MSG, It is done!)
|
||||||
|
|
||||||
|
O<jog_around> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
|
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_0.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_0> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_0> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_1.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_1> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_1> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_10.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_10> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_10> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_11.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_11> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_11> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_12.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_12> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_12> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_13.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_13> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_13> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_14.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_14> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_14> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_15.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_15> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_15> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_2.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_2> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_2> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_3.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_3> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_3> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_4.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_4> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_4> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_5.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_5> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_5> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_6.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_6> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_6> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_7.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_7> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_7> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
27
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_8.ngc
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
; the image path must be relative from your config dir or absolute, "~" is allowed
|
||||||
|
(IMAGE, ./macros/images/macro_8.png)
|
||||||
|
|
||||||
|
O<macro_8> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_8> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
24
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_9.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
; Testfile go to position
|
||||||
|
; will jog the machine to a position to give
|
||||||
|
|
||||||
|
O<macro_9> sub
|
||||||
|
|
||||||
|
G17
|
||||||
|
G21
|
||||||
|
G54
|
||||||
|
G61
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
G80
|
||||||
|
G90
|
||||||
|
|
||||||
|
;#1 = <X-Pos>
|
||||||
|
;#2 = <Y-Pos>
|
||||||
|
;#3 = <Z-Pos>
|
||||||
|
|
||||||
|
(DEBUG, Will now move machine to X = #1 , Y = #2 , Z = #3)
|
||||||
|
G0 X #1 Y #2 Z #3
|
||||||
|
|
||||||
|
O<macro_9> endsub
|
||||||
|
|
||||||
|
M2
|
||||||
44
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/macro_Instructions.txt
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
This is a small instruction to include macros in gmoccapy.
|
||||||
|
|
||||||
|
In your INI File you need to introduce a section called [MACROS]
|
||||||
|
and for every macro you'll need to include a one-liner like so:
|
||||||
|
|
||||||
|
MACRO = jog_around
|
||||||
|
or
|
||||||
|
MACRO = increment xinc yinc
|
||||||
|
|
||||||
|
where xinc and yinc are placeholders
|
||||||
|
|
||||||
|
During execution of the macro, you will be asked to enter the values.
|
||||||
|
|
||||||
|
You are allowed to introduce 9 macros!
|
||||||
|
If you enter more macros, only the first 9 will appear with button in gmoccapy.
|
||||||
|
|
||||||
|
In the [RS274NGC] section you may want to give a path to your macros like so:
|
||||||
|
|
||||||
|
[RS274NGC]
|
||||||
|
SUBROUTINE_PATH = nc_files/subroutines
|
||||||
|
|
||||||
|
or you place your macros in the nc_files folder.
|
||||||
|
|
||||||
|
Each macro must have it's own file in one of the mentioned folders and they are normal subroutines, so the must begin with:
|
||||||
|
|
||||||
|
O<jog_around> sub
|
||||||
|
|
||||||
|
and end with
|
||||||
|
|
||||||
|
O<jog_around> endsub
|
||||||
|
M2
|
||||||
|
|
||||||
|
The name of the file must be jog_around.ngc.
|
||||||
|
And an macro must contain at least one movement of one axis.
|
||||||
|
|
||||||
|
macro name in INI file have to be the same as the file name and the sub must have also the same name (case sensitive!)!
|
||||||
|
|
||||||
|
BE CAREFUL:
|
||||||
|
At this development step, the macros can only be interrupted by pressing the machine off button or the emergency exit button! I will check how to make this more secure!
|
||||||
|
|
||||||
|
Hope this does help.
|
||||||
|
|
||||||
|
Norbert
|
||||||
|
|
||||||
9
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/on_abort.ngc
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
%
|
||||||
|
o<on_abort> sub
|
||||||
|
|
||||||
|
G90
|
||||||
|
G40
|
||||||
|
G49
|
||||||
|
|
||||||
|
o<on_abort> endsub
|
||||||
|
%
|
||||||
20
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/macros/settool_g43.ngc
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
o<settool_g43> sub
|
||||||
|
;(debug, tool=#<tool> pocket=#<pocket>)
|
||||||
|
|
||||||
|
; we must execute this only in the milltask interpreter
|
||||||
|
; or preview will break, so test for '#<_task>' which is 1 for
|
||||||
|
; the milltask interpreter and 0 in the UI's
|
||||||
|
O100 if [#<_task> EQ 0]
|
||||||
|
(debug, Task ist Null)
|
||||||
|
O100 return [999]
|
||||||
|
O100 endif
|
||||||
|
|
||||||
|
; using the code being remapped here means 'use builtin behaviour'
|
||||||
|
m61 q#<tool>
|
||||||
|
|
||||||
|
; set tool offset
|
||||||
|
G43
|
||||||
|
|
||||||
|
; signal success be returning a value > 0:
|
||||||
|
o<settool_g43> endsub [1]
|
||||||
|
M2
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Table Rotary/Tilting (trt) Sim configs
|
||||||
|
|
||||||
|
xyzac-trt
|
||||||
|
xyzbc-trt
|
||||||
|
|
||||||
|
Example ngc files:
|
||||||
|
examples/boat-xyzac.ngc (!!! TOUCHOFF table Z to -13.25 !!!)
|
||||||
|
examples/boat-xyzbc.ngc (!!! TOUCHOFF table Z to - 8.30 !!!)
|
||||||
|
|
||||||
|
Example ngc files (ngcgui compatible subroutines):
|
||||||
|
examples/test-xyzac.ngc
|
||||||
|
examples/test-xyzbc.ngc
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
o<test-xyzac> sub
|
||||||
|
(test-xyzac-kins with offsets and tool change)
|
||||||
|
( 5 AXIS XYZAC MILLING )
|
||||||
|
|
||||||
|
#<a_angle> = #1 (=20)
|
||||||
|
#<c_angle> = #2 (=30)
|
||||||
|
#<xy_max> = #3 (=30)
|
||||||
|
#<z_max> = #4 (=10)
|
||||||
|
#<feedrate> = #5 (=200)
|
||||||
|
#<tool1> = #6 (=1 1st Tool)
|
||||||
|
#<tool2> = #7 (=2 2nd Tool)
|
||||||
|
#<speed1> = #8 (=500)
|
||||||
|
#<speed2> = #9 (=600)
|
||||||
|
|
||||||
|
; 'square with 2 rounded corners'
|
||||||
|
#<x_max> = #<xy_max>
|
||||||
|
#<y_max> = #<xy_max>
|
||||||
|
#<g3_j> = #<xy_max>
|
||||||
|
|
||||||
|
N40 G00 G17 G40 G90 G94 Z10
|
||||||
|
(first tool - no z-offset)
|
||||||
|
T#<tool1> M6
|
||||||
|
S#<speed1> M3
|
||||||
|
G00 A#<a_angle> C#<c_angle>
|
||||||
|
G43 H#<tool1> Z0
|
||||||
|
G01 X#<x_max> F#<feedrate>
|
||||||
|
G01 Y#<y_max>
|
||||||
|
G01 X0
|
||||||
|
G03 X-#<x_max> Y0 I0 J-#<g3_j>
|
||||||
|
G00 Z#<z_max>
|
||||||
|
G00 X0
|
||||||
|
M5
|
||||||
|
(next tool- with z-offset)
|
||||||
|
T#<tool2> M6
|
||||||
|
S#<speed2> M3
|
||||||
|
G00 A#<a_angle> C#<c_angle>
|
||||||
|
G43 H#<tool2> Z0
|
||||||
|
G01 X-#<x_max> F#<feedrate>
|
||||||
|
G01 Y-#<y_max>
|
||||||
|
G01 X0
|
||||||
|
G03 X#<x_max> Y0 I0 J#<g3_j>
|
||||||
|
G0 Z#<z_max>
|
||||||
|
G0 X0 Y0
|
||||||
|
M5
|
||||||
|
A0 C0
|
||||||
|
|
||||||
|
o<test-xyzac> endsub
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
o<test-xyzbc> sub
|
||||||
|
(test-xyzbc-trt-kins with offsets and tool change)
|
||||||
|
( 5 AXIS XYZBC MILLING )
|
||||||
|
|
||||||
|
#<b_angle> = #1 (=20)
|
||||||
|
#<c_angle> = #2 (=30)
|
||||||
|
#<xy_max> = #3 (=30)
|
||||||
|
#<z_max> = #4 (=10)
|
||||||
|
#<feedrate> = #5 (=200)
|
||||||
|
#<tool1> = #6 (=1 1st Tool)
|
||||||
|
#<tool2> = #7 (=2 2nd Tool)
|
||||||
|
#<speed1> = #8 (=500)
|
||||||
|
#<speed2> = #9 (=600)
|
||||||
|
|
||||||
|
;'square with 2 rounded corners'
|
||||||
|
#<x_max> = #<xy_max>
|
||||||
|
#<y_max> = #<xy_max>
|
||||||
|
#<g3_j> = #<xy_max>
|
||||||
|
|
||||||
|
G00 G17 G40 G90 G94 Z10
|
||||||
|
(first tool - no z-offset)
|
||||||
|
T#<tool1> M6
|
||||||
|
S#<speed1> M3
|
||||||
|
G00 B#<b_angle> C#<c_angle>
|
||||||
|
G43 H#<tool1> Z0
|
||||||
|
G01 X#<x_max> F#<feedrate>
|
||||||
|
G01 Y#<y_max>
|
||||||
|
G01 X0
|
||||||
|
G03 X-#<x_max> Y0 I0 J-#<g3_j>
|
||||||
|
G00 Z#<z_max>
|
||||||
|
G00 X0
|
||||||
|
M5
|
||||||
|
(next tool- with z-offset)
|
||||||
|
T#<tool2> M6
|
||||||
|
S#<speed2> M3
|
||||||
|
G00 B#<b_angle> C#<c_angle>
|
||||||
|
G43 H#<tool2> Z0
|
||||||
|
G01 X-#<x_max> F#<feedrate>
|
||||||
|
G01 Y-#<y_max>
|
||||||
|
G01 X0
|
||||||
|
G03 X#<x_max> Y0 I0 J#<g3_j>
|
||||||
|
G0 Z#<z_max>
|
||||||
|
G0 X0 Y0
|
||||||
|
M5
|
||||||
|
B0 C0
|
||||||
|
|
||||||
|
o<test-xyzbc> endsub
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
###################################################################
|
||||||
|
# moccapy_postgui.hal file from Norbert Schechner #
|
||||||
|
###################################################################
|
||||||
|
|
||||||
|
loadrt abs names=abs_spindle_feedback
|
||||||
|
addf abs_spindle_feedback servo-thread
|
||||||
|
|
||||||
|
net spindle-speed-limited => abs_spindle_feedback.in
|
||||||
|
net spindle-abs abs_spindle_feedback.out => gmoccapy.spindle_feedback_bar
|
||||||
|
net spindle-at-speed gmoccapy.spindle_at_speed_led
|
||||||
|
|
||||||
|
# the unlink pin commands are only used, because they are connected
|
||||||
|
# in core_sim.hal and we use this file to simulate
|
||||||
|
unlinkp iocontrol.0.tool-change
|
||||||
|
unlinkp iocontrol.0.tool-changed
|
||||||
|
|
||||||
|
net tool-change gmoccapy.toolchange-change <= iocontrol.0.tool-change
|
||||||
|
net tool-changed gmoccapy.toolchange-changed => iocontrol.0.tool-changed
|
||||||
|
net tool-prep-number gmoccapy.toolchange-number <= iocontrol.0.tool-prep-number
|
||||||
|
|
||||||
|
net tool-offset gmoccapy.tooloffset-z
|
||||||
|
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
spindle_start_rpm = 300
|
||||||
|
scale_jog_vel = 21.0
|
||||||
|
scale_spindle_override = 1
|
||||||
|
scale_feed_override = 1
|
||||||
|
scale_rapid_override = 1
|
||||||
|
hide_turtle_jog_button = False
|
||||||
|
turtle_jog_factor = 10
|
||||||
|
dro_size = 28
|
||||||
|
open_file =
|
||||||
|
screen1 = window
|
||||||
|
x_pos = 40
|
||||||
|
y_pos = 30
|
||||||
|
width = 979
|
||||||
|
height = 750
|
||||||
|
gtk_theme = Follow System Theme
|
||||||
|
audio_alert = /usr/share/sounds/freedesktop/stereo/dialog-warning.oga
|
||||||
|
audio_error = /usr/share/sounds/freedesktop/stereo/dialog-error.oga
|
||||||
|
grid_size = 1.0
|
||||||
|
view = p
|
||||||
|
mouse_btn_mode = 4
|
||||||
|
hide_cursor = False
|
||||||
|
system_name_tool = Working Offset
|
||||||
|
system_name_g5x = G5x
|
||||||
|
system_name_rot = Rot
|
||||||
|
system_name_g92 = G92
|
||||||
|
system_name_g54 = G54
|
||||||
|
system_name_g55 = G55
|
||||||
|
system_name_g56 = G56
|
||||||
|
system_name_g57 = G57
|
||||||
|
system_name_g58 = G58
|
||||||
|
system_name_g59 = G59
|
||||||
|
system_name_g59.1 = G59.1
|
||||||
|
system_name_g59.2 = G59.2
|
||||||
|
system_name_g59.3 = G59.3
|
||||||
|
jump_to_dir = /home/gmoccapy
|
||||||
|
show_keyboard_on_offset = False
|
||||||
|
show_keyboard_on_tooledit = False
|
||||||
|
show_keyboard_on_edit = False
|
||||||
|
show_keyboard_on_mdi = False
|
||||||
|
show_keyboard_on_file_selection = False
|
||||||
|
spindle_bar_min = 0.0
|
||||||
|
spindle_bar_max = 6000.0
|
||||||
|
x_pos_popup = 45.0
|
||||||
|
y_pos_popup = 55
|
||||||
|
width_popup = 250.0
|
||||||
|
max_messages = 10
|
||||||
|
message_font = sans 10
|
||||||
|
use_frames = True
|
||||||
|
show_dro_btn = False
|
||||||
|
use_auto_units = True
|
||||||
|
blockdel = False
|
||||||
|
opstop = False
|
||||||
|
enable_dro = False
|
||||||
|
show_offsets = False
|
||||||
|
show_dtg = False
|
||||||
|
view_tool_path = True
|
||||||
|
view_dimension = True
|
||||||
|
gremlin_view = rbt_view_p
|
||||||
|
run_from_line = no_run
|
||||||
|
unlock_way = no
|
||||||
|
unlock_code = 123
|
||||||
|
show_preview_on_offset = False
|
||||||
|
use_keyboard_shortcuts = True
|
||||||
|
abs_color = #0000FF
|
||||||
|
rel_color = #000000
|
||||||
|
dtg_color = #FFFF00
|
||||||
|
homed_color = #00FF00
|
||||||
|
unhomed_color = #FF0000
|
||||||
|
dro_digits = 3
|
||||||
|
toggle_readout = True
|
||||||
|
reload_tool = True
|
||||||
|
tool_in_spindle = 0
|
||||||
|
blockheight = 0.0
|
||||||
|
use_toolmeasurement = False
|
||||||
|
kbd_height = 250
|
||||||
|
kbd_width = 880
|
||||||
|
kbd_set_height = False
|
||||||
|
kbd_set_width = False
|
||||||
|
info_tab_page = 0
|
||||||
|
jog_btn_size = 48
|
||||||
|
jog_box_width = 360
|
||||||
|
toolpage_use_calc = True
|
||||||
|
offsetpage_use_calc = True
|
||||||
|
gcodeview_font = monospace 10
|
||||||
|
hide_titlebar = False
|
||||||
|
icon_theme = classic
|
||||||
|
gcode_theme = classic
|
||||||
|
audio_enabled = True
|
||||||
|
hide_tooltips = False
|
||||||
|
system_name_g28 = G28
|
||||||
|
system_name_g30 = G30
|
||||||
|
iconview_sortorder = 0
|
||||||
|
iconview_sortbydate = 1
|
||||||
|
iconview_folderfirst = 0
|
||||||
|
sort_by_date = False
|
||||||
|
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
[EMC]
|
||||||
|
VERSION = 1.1
|
||||||
|
MACHINE = sim-xyzac-trt
|
||||||
|
DEBUG = 0
|
||||||
|
|
||||||
|
[DISPLAY]
|
||||||
|
OPEN_FILE = ./examples/impeller-7bl-xyzac.ngc
|
||||||
|
JOG_AXES = XYZC
|
||||||
|
DISPLAY = gmoccapy
|
||||||
|
POSITION_OFFSET = RELATIVE
|
||||||
|
POSITION_FEEDBACK = ACTUAL
|
||||||
|
MAX_FEED_OVERRIDE = 2
|
||||||
|
PROGRAM_PREFIX = ../../../nc_files
|
||||||
|
INTRO_GRAPHIC = emc2.gif
|
||||||
|
INTRO_TIME = 1
|
||||||
|
|
||||||
|
TKPKG = Ngcgui 1.0
|
||||||
|
NGCGUI_FONT = Helvetica -12 normal
|
||||||
|
NGCGUI_SUBFILE = test-xyzac.ngc
|
||||||
|
|
||||||
|
[RS274NGC]
|
||||||
|
PARAMETER_FILE = xyzac.var
|
||||||
|
SUBROUTINE_PATH = ./examples:../../macros
|
||||||
|
REMAP=M6 modalgroup=6 prolog=change_prolog ngc=change_g43 epilog=change_epilog
|
||||||
|
REMAP=M61 modalgroup=6 prolog=settool_prolog ngc=settool_g43 epilog=settool_epilog
|
||||||
|
|
||||||
|
# the Python plugins serves interpreter and task
|
||||||
|
[PYTHON]
|
||||||
|
PATH_PREPEND = ../../python
|
||||||
|
TOPLEVEL = ../../python/toplevel.py
|
||||||
|
LOG_LEVEL = 0
|
||||||
|
|
||||||
|
[HAL]
|
||||||
|
HALUI = halui
|
||||||
|
HALFILE = LIB:basic_sim.tcl -no_use_hal_manualtoolchange
|
||||||
|
|
||||||
|
# vismach xyzac-trt-gui items
|
||||||
|
HALCMD = loadusr -W xyzac-trt-gui
|
||||||
|
HALCMD = net :table-x joint.0.pos-fb xyzac-trt-gui.table-x
|
||||||
|
HALCMD = net :saddle-y joint.1.pos-fb xyzac-trt-gui.saddle-y
|
||||||
|
HALCMD = net :spindle-z joint.2.pos-fb xyzac-trt-gui.spindle-z
|
||||||
|
HALCMD = net :tilt-a joint.3.pos-fb xyzac-trt-gui.tilt-a
|
||||||
|
HALCMD = net :rotate-c joint.4.pos-fb xyzac-trt-gui.rotate-c
|
||||||
|
HALCMD = net :tool-offset motion.tooloffset.z
|
||||||
|
HALCMD = net :tool-offset xyzac-trt-kins.tool-offset xyzac-trt-gui.tool-offset
|
||||||
|
HALCMD = net :y-offset xyzac-trt-kins.y-offset xyzac-trt-gui.y-offset
|
||||||
|
HALCMD = net :z-offset xyzac-trt-kins.z-offset xyzac-trt-gui.z-offset
|
||||||
|
HALCMD = sets :y-offset 20
|
||||||
|
HALCMD = sets :z-offset 10
|
||||||
|
|
||||||
|
POSTGUI_HALFILE = postgui.hal
|
||||||
|
|
||||||
|
[KINS]
|
||||||
|
KINEMATICS = xyzac-trt-kins
|
||||||
|
JOINTS = 5
|
||||||
|
|
||||||
|
[TRAJ]
|
||||||
|
COORDINATES = XYZAC
|
||||||
|
LINEAR_UNITS = mm
|
||||||
|
ANGULAR_UNITS = deg
|
||||||
|
DEFAULT_LINEAR_VELOCITY = 20
|
||||||
|
MAX_LINEAR_VELOCITY = 35
|
||||||
|
MAX_LINEAR_ACCELERATION = 400
|
||||||
|
DEFAULT_LINEAR_ACCELERATION = 300
|
||||||
|
|
||||||
|
[EMCMOT]
|
||||||
|
EMCMOT = motmod
|
||||||
|
SERVO_PERIOD = 1000000
|
||||||
|
COMM_TIMEOUT = 1
|
||||||
|
|
||||||
|
[TASK]
|
||||||
|
TASK = milltask
|
||||||
|
CYCLE_TIME = 0.010
|
||||||
|
|
||||||
|
[EMCIO]
|
||||||
|
TOOL_TABLE = xyzac-trt.tbl
|
||||||
|
|
||||||
|
[AXIS_X]
|
||||||
|
MIN_LIMIT = -200
|
||||||
|
MAX_LIMIT = 200
|
||||||
|
MAX_VELOCITY = 20
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
|
||||||
|
[JOINT_0]
|
||||||
|
TYPE = LINEAR
|
||||||
|
HOME = 0
|
||||||
|
MAX_VELOCITY = 20
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
MIN_LIMIT = -200
|
||||||
|
MAX_LIMIT = 200
|
||||||
|
HOME_SEARCH_VEL = 0
|
||||||
|
HOME_SEQUENCE = 0
|
||||||
|
|
||||||
|
[AXIS_Y]
|
||||||
|
MIN_LIMIT = -100
|
||||||
|
MAX_LIMIT = 100
|
||||||
|
MAX_VELOCITY = 20
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
|
||||||
|
[JOINT_1]
|
||||||
|
TYPE = LINEAR
|
||||||
|
HOME = 0
|
||||||
|
MAX_VELOCITY = 20
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
MIN_LIMIT = -100
|
||||||
|
MAX_LIMIT = 100
|
||||||
|
HOME_SEARCH_VEL = 0
|
||||||
|
HOME_SEQUENCE = 0
|
||||||
|
|
||||||
|
[AXIS_Z]
|
||||||
|
MIN_LIMIT = -120
|
||||||
|
MAX_LIMIT = 120
|
||||||
|
MAX_VELOCITY = 20
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
|
||||||
|
[JOINT_2]
|
||||||
|
TYPE = LINEAR
|
||||||
|
HOME = 0
|
||||||
|
MAX_VELOCITY = 20
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
MIN_LIMIT = -120
|
||||||
|
MAX_LIMIT = 120
|
||||||
|
HOME_SEARCH_VEL = 0
|
||||||
|
HOME_SEQUENCE = 0
|
||||||
|
|
||||||
|
[AXIS_A]
|
||||||
|
MIN_LIMIT = -100
|
||||||
|
MAX_LIMIT = 50
|
||||||
|
MAX_VELOCITY = 30
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
|
||||||
|
[JOINT_3]
|
||||||
|
TYPE = ANGULAR
|
||||||
|
HOME = 0
|
||||||
|
MAX_VELOCITY = 30
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
MIN_LIMIT = -100
|
||||||
|
MAX_LIMIT = 50
|
||||||
|
HOME_SEARCH_VEL = 0
|
||||||
|
HOME_SEQUENCE = 0
|
||||||
|
|
||||||
|
[AXIS_C]
|
||||||
|
MIN_LIMIT = -36000
|
||||||
|
MAX_LIMIT = 36000
|
||||||
|
MAX_VELOCITY = 30
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
|
||||||
|
[JOINT_4]
|
||||||
|
TYPE = ANGULAR
|
||||||
|
HOME = 0
|
||||||
|
MAX_VELOCITY = 30
|
||||||
|
MAX_ACCELERATION = 300
|
||||||
|
MIN_LIMIT = -36000
|
||||||
|
MAX_LIMIT = 36000
|
||||||
|
HOME_SEARCH_VEL = 0
|
||||||
|
HOME_SEQUENCE = 0
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
T1 P1 Z0 D6 ;end mill
|
||||||
|
T2 P2 Z15 D8 ;end mill
|
||||||
|
T3 P3 Z0 D4.2 ;#7 tap drill
|
||||||
|
T4 P4 Z0 D10
|
||||||
|
T5 P5 Z30 D10
|
||||||
|
T6 P6 Z30 D10
|
||||||
|
T7 P7 Z30 D10
|
||||||
|
T8 P8 Z30 D10
|
||||||
|
T9 P9 Z30 D10
|
||||||
|
T10 P10 D0.5
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
# Fri Jun 26 05:57:43 EDT 2026
|
||||||
|
#
|
||||||
|
# This file: ./xyzac-trt_cmds.hal
|
||||||
|
# Created by: /home/meswork/cnc_wams/linuxcnc/lib/hallib/basic_sim.tcl
|
||||||
|
# With options: -no_use_hal_manualtoolchange
|
||||||
|
# From inifile: /home/meswork/cnc_wams/linuxcnc/configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
# Halfiles: {LIB:basic_sim.tcl -no_use_hal_manualtoolchange}
|
||||||
|
#
|
||||||
|
# This file contains the hal commands produced by basic_sim.tcl
|
||||||
|
# (and any hal commands executed prior to its execution).
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# To use ./xyzac-trt_cmds.hal in the original inifile (or a copy of it),
|
||||||
|
# edit to change:
|
||||||
|
# [HAL]
|
||||||
|
# HALFILE = LIB:basic_sim.tcl parameters
|
||||||
|
# to:
|
||||||
|
# [HAL]
|
||||||
|
# HALFILE = ./xyzac-trt_cmds.hal
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# 1) Inifile Variables substitutions specified in the inifile
|
||||||
|
# and interpreted by halcmd are automatically substituted
|
||||||
|
# in the created halfile (./xyzac-trt_cmds.hal).
|
||||||
|
# 2) Input pins connected to a signal with no writer are
|
||||||
|
# not included in the setp listings herein so must be added
|
||||||
|
# manually
|
||||||
|
#
|
||||||
|
|
||||||
|
# components
|
||||||
|
#preloaded module: loadrt tpmod
|
||||||
|
#preloaded module: loadrt homemod
|
||||||
|
loadrt xyzac-trt-kins
|
||||||
|
loadrt motmod base_period_nsec=0 servo_period_nsec=1000000 num_joints=5
|
||||||
|
#loadrt __servo-thread (not loaded by loadrt, no args saved)
|
||||||
|
loadrt pid names=J0_pid,J1_pid,J2_pid,J3_pid,J4_pid
|
||||||
|
loadrt mux2 names=J0_mux,J1_mux,J2_mux,J3_mux,J4_mux
|
||||||
|
loadrt ddt names=J0_vel,J0_accel,J1_vel,J1_accel,J2_vel,J2_accel,J3_vel,J3_accel,J4_vel,J4_accel
|
||||||
|
loadrt sim_home_switch names=J0_switch,J1_switch,J2_switch,J3_switch,J4_switch
|
||||||
|
loadrt sim_spindle names=sim_spindle
|
||||||
|
loadrt limit2 names=limit_speed
|
||||||
|
loadrt lowpass names=spindle_mass
|
||||||
|
loadrt near names=near_speed
|
||||||
|
loadrt scale names=rpm_rps
|
||||||
|
# pin aliases
|
||||||
|
# param aliases
|
||||||
|
# signals
|
||||||
|
# nets
|
||||||
|
net J0:acc J0_accel.out
|
||||||
|
net J0:enable joint.0.amp-enable-out => J0_pid.enable
|
||||||
|
net J0:homesw J0_switch.home-sw => joint.0.home-sw-in
|
||||||
|
net J0:on-pos J0_pid.output => J0_mux.in1
|
||||||
|
net J0:pos-cmd joint.0.motor-pos-cmd => J0_pid.command
|
||||||
|
net J0:pos-fb J0_mux.out => J0_mux.in0 J0_switch.cur-pos J0_vel.in joint.0.motor-pos-fb
|
||||||
|
net J0:vel J0_vel.out => J0_accel.in
|
||||||
|
net J1:acc J1_accel.out
|
||||||
|
net J1:enable joint.1.amp-enable-out => J1_pid.enable
|
||||||
|
net J1:homesw J1_switch.home-sw => joint.1.home-sw-in
|
||||||
|
net J1:on-pos J1_pid.output => J1_mux.in1
|
||||||
|
net J1:pos-cmd joint.1.motor-pos-cmd => J1_pid.command
|
||||||
|
net J1:pos-fb J1_mux.out => J1_mux.in0 J1_switch.cur-pos J1_vel.in joint.1.motor-pos-fb
|
||||||
|
net J1:vel J1_vel.out => J1_accel.in
|
||||||
|
net J2:acc J2_accel.out
|
||||||
|
net J2:enable joint.2.amp-enable-out => J2_pid.enable
|
||||||
|
net J2:homesw J2_switch.home-sw => joint.2.home-sw-in
|
||||||
|
net J2:on-pos J2_pid.output => J2_mux.in1
|
||||||
|
net J2:pos-cmd joint.2.motor-pos-cmd => J2_pid.command
|
||||||
|
net J2:pos-fb J2_mux.out => J2_mux.in0 J2_switch.cur-pos J2_vel.in joint.2.motor-pos-fb
|
||||||
|
net J2:vel J2_vel.out => J2_accel.in
|
||||||
|
net J3:acc J3_accel.out
|
||||||
|
net J3:enable joint.3.amp-enable-out => J3_pid.enable
|
||||||
|
net J3:homesw J3_switch.home-sw => joint.3.home-sw-in
|
||||||
|
net J3:on-pos J3_pid.output => J3_mux.in1
|
||||||
|
net J3:pos-cmd joint.3.motor-pos-cmd => J3_pid.command
|
||||||
|
net J3:pos-fb J3_mux.out => J3_mux.in0 J3_switch.cur-pos J3_vel.in joint.3.motor-pos-fb
|
||||||
|
net J3:vel J3_vel.out => J3_accel.in
|
||||||
|
net J4:acc J4_accel.out
|
||||||
|
net J4:enable joint.4.amp-enable-out => J4_pid.enable
|
||||||
|
net J4:homesw J4_switch.home-sw => joint.4.home-sw-in
|
||||||
|
net J4:on-pos J4_pid.output => J4_mux.in1
|
||||||
|
net J4:pos-cmd joint.4.motor-pos-cmd => J4_pid.command
|
||||||
|
net J4:pos-fb J4_mux.out => J4_mux.in0 J4_switch.cur-pos J4_vel.in joint.4.motor-pos-fb
|
||||||
|
net J4:vel J4_vel.out => J4_accel.in
|
||||||
|
net estop:loop iocontrol.0.user-enable-out => iocontrol.0.emc-enable-in
|
||||||
|
net sample:enable motion.motion-enabled => J0_mux.sel J1_mux.sel J2_mux.sel J3_mux.sel J4_mux.sel
|
||||||
|
net spindle-at-speed near_speed.out => spindle.0.at-speed
|
||||||
|
net spindle-index-enable sim_spindle.index-enable <=> spindle.0.index-enable
|
||||||
|
net spindle-orient spindle.0.orient => spindle.0.is-oriented
|
||||||
|
net spindle-pos sim_spindle.position-fb => spindle.0.revs
|
||||||
|
net spindle-rpm-filtered spindle_mass.out => near_speed.in2 rpm_rps.in
|
||||||
|
net spindle-rps-filtered rpm_rps.out => spindle.0.speed-in
|
||||||
|
net spindle-speed-cmd spindle.0.speed-out => limit_speed.in near_speed.in1
|
||||||
|
net spindle-speed-limited limit_speed.out => sim_spindle.velocity-cmd spindle_mass.in
|
||||||
|
net tool:change-loop iocontrol.0.tool-change => iocontrol.0.tool-changed
|
||||||
|
net tool:prep-loop iocontrol.0.tool-prepare => iocontrol.0.tool-prepared
|
||||||
|
# parameter values
|
||||||
|
setp J0_accel.tmax 0
|
||||||
|
setp J0_mux.tmax 0
|
||||||
|
setp J0_pid.do-pid-calcs.tmax 0
|
||||||
|
setp J0_switch.tmax 0
|
||||||
|
setp J0_vel.tmax 0
|
||||||
|
setp J1_accel.tmax 0
|
||||||
|
setp J1_mux.tmax 0
|
||||||
|
setp J1_pid.do-pid-calcs.tmax 0
|
||||||
|
setp J1_switch.tmax 0
|
||||||
|
setp J1_vel.tmax 0
|
||||||
|
setp J2_accel.tmax 0
|
||||||
|
setp J2_mux.tmax 0
|
||||||
|
setp J2_pid.do-pid-calcs.tmax 0
|
||||||
|
setp J2_switch.tmax 0
|
||||||
|
setp J2_vel.tmax 0
|
||||||
|
setp J3_accel.tmax 0
|
||||||
|
setp J3_mux.tmax 0
|
||||||
|
setp J3_pid.do-pid-calcs.tmax 0
|
||||||
|
setp J3_switch.tmax 0
|
||||||
|
setp J3_vel.tmax 0
|
||||||
|
setp J4_accel.tmax 0
|
||||||
|
setp J4_mux.tmax 0
|
||||||
|
setp J4_pid.do-pid-calcs.tmax 0
|
||||||
|
setp J4_switch.tmax 0
|
||||||
|
setp J4_vel.tmax 0
|
||||||
|
setp limit_speed.tmax 0
|
||||||
|
setp motion-command-handler.tmax 0
|
||||||
|
setp motion-controller.tmax 0
|
||||||
|
setp near_speed.difference 10
|
||||||
|
setp near_speed.scale 1.1
|
||||||
|
setp near_speed.tmax 0
|
||||||
|
setp rpm_rps.tmax 0
|
||||||
|
setp servo-thread.tmax 0
|
||||||
|
setp sim_spindle.scale 0.01666667
|
||||||
|
setp sim_spindle.tmax 0
|
||||||
|
setp spindle_mass.gain 0.07
|
||||||
|
setp spindle_mass.tmax 0
|
||||||
|
# realtime thread/function links
|
||||||
|
addf motion-command-handler servo-thread
|
||||||
|
addf motion-controller servo-thread
|
||||||
|
addf J0_pid.do-pid-calcs servo-thread
|
||||||
|
addf J1_pid.do-pid-calcs servo-thread
|
||||||
|
addf J2_pid.do-pid-calcs servo-thread
|
||||||
|
addf J3_pid.do-pid-calcs servo-thread
|
||||||
|
addf J4_pid.do-pid-calcs servo-thread
|
||||||
|
addf J0_mux servo-thread
|
||||||
|
addf J1_mux servo-thread
|
||||||
|
addf J2_mux servo-thread
|
||||||
|
addf J3_mux servo-thread
|
||||||
|
addf J4_mux servo-thread
|
||||||
|
addf J0_vel servo-thread
|
||||||
|
addf J0_accel servo-thread
|
||||||
|
addf J1_vel servo-thread
|
||||||
|
addf J1_accel servo-thread
|
||||||
|
addf J2_vel servo-thread
|
||||||
|
addf J2_accel servo-thread
|
||||||
|
addf J3_vel servo-thread
|
||||||
|
addf J3_accel servo-thread
|
||||||
|
addf J4_vel servo-thread
|
||||||
|
addf J4_accel servo-thread
|
||||||
|
addf J0_switch servo-thread
|
||||||
|
addf J1_switch servo-thread
|
||||||
|
addf J2_switch servo-thread
|
||||||
|
addf J3_switch servo-thread
|
||||||
|
addf J4_switch servo-thread
|
||||||
|
addf limit_speed servo-thread
|
||||||
|
addf spindle_mass servo-thread
|
||||||
|
addf rpm_rps servo-thread
|
||||||
|
addf near_speed servo-thread
|
||||||
|
addf sim_spindle servo-thread
|
||||||
|
|
||||||
|
# setp commands for unconnected input pins
|
||||||
|
setp J0_pid.FF0 1.0
|
||||||
|
setp J0_pid.Pgain 0
|
||||||
|
setp J0_pid.Dgain 0
|
||||||
|
setp J0_pid.Igain 0
|
||||||
|
setp J0_pid.FF1 0
|
||||||
|
setp J0_pid.FF2 0
|
||||||
|
setp J1_pid.FF0 1.0
|
||||||
|
setp J1_pid.Pgain 0
|
||||||
|
setp J1_pid.Dgain 0
|
||||||
|
setp J1_pid.Igain 0
|
||||||
|
setp J1_pid.FF1 0
|
||||||
|
setp J1_pid.FF2 0
|
||||||
|
setp J2_pid.FF0 1.0
|
||||||
|
setp J2_pid.Pgain 0
|
||||||
|
setp J2_pid.Dgain 0
|
||||||
|
setp J2_pid.Igain 0
|
||||||
|
setp J2_pid.FF1 0
|
||||||
|
setp J2_pid.FF2 0
|
||||||
|
setp J3_pid.FF0 1.0
|
||||||
|
setp J3_pid.Pgain 0
|
||||||
|
setp J3_pid.Dgain 0
|
||||||
|
setp J3_pid.Igain 0
|
||||||
|
setp J3_pid.FF1 0
|
||||||
|
setp J3_pid.FF2 0
|
||||||
|
setp J4_pid.FF0 1.0
|
||||||
|
setp J4_pid.Pgain 0
|
||||||
|
setp J4_pid.Dgain 0
|
||||||
|
setp J4_pid.Igain 0
|
||||||
|
setp J4_pid.FF1 0
|
||||||
|
setp J4_pid.FF2 0
|
||||||
|
setp sim_spindle.scale 0.01666667
|
||||||
|
setp limit_speed.maxv 5000.0
|
||||||
|
setp spindle_mass.gain .07
|
||||||
|
setp near_speed.scale 1.1
|
||||||
|
setp near_speed.difference 10
|
||||||
119
wasm-port/vendor/linuxcnc/configs/sim/gmoccapy/non_trivial_kinematics/table-rotary-tilting/xyzac.var
vendored
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
5161 0.000000
|
||||||
|
5162 0.000000
|
||||||
|
5163 0.000000
|
||||||
|
5164 0.000000
|
||||||
|
5165 0.000000
|
||||||
|
5166 0.000000
|
||||||
|
5167 0.000000
|
||||||
|
5168 0.000000
|
||||||
|
5169 0.000000
|
||||||
|
5181 0.000000
|
||||||
|
5182 0.000000
|
||||||
|
5183 0.000000
|
||||||
|
5184 0.000000
|
||||||
|
5185 0.000000
|
||||||
|
5186 0.000000
|
||||||
|
5187 0.000000
|
||||||
|
5188 0.000000
|
||||||
|
5189 0.000000
|
||||||
|
5210 0.000000
|
||||||
|
5211 0.000000
|
||||||
|
5212 0.000000
|
||||||
|
5213 0.000000
|
||||||
|
5214 0.000000
|
||||||
|
5215 0.000000
|
||||||
|
5216 0.000000
|
||||||
|
5217 0.000000
|
||||||
|
5218 0.000000
|
||||||
|
5219 0.000000
|
||||||
|
5220 1.000000
|
||||||
|
5221 0.000000
|
||||||
|
5222 0.000000
|
||||||
|
5223 0.000000
|
||||||
|
5224 0.000000
|
||||||
|
5225 0.000000
|
||||||
|
5226 0.000000
|
||||||
|
5227 0.000000
|
||||||
|
5228 0.000000
|
||||||
|
5229 0.000000
|
||||||
|
5230 0.000000
|
||||||
|
5241 0.000000
|
||||||
|
5242 0.000000
|
||||||
|
5243 0.000000
|
||||||
|
5244 0.000000
|
||||||
|
5245 0.000000
|
||||||
|
5246 0.000000
|
||||||
|
5247 0.000000
|
||||||
|
5248 0.000000
|
||||||
|
5249 0.000000
|
||||||
|
5250 0.000000
|
||||||
|
5261 0.000000
|
||||||
|
5262 0.000000
|
||||||
|
5263 0.000000
|
||||||
|
5264 0.000000
|
||||||
|
5265 0.000000
|
||||||
|
5266 0.000000
|
||||||
|
5267 0.000000
|
||||||
|
5268 0.000000
|
||||||
|
5269 0.000000
|
||||||
|
5270 0.000000
|
||||||
|
5281 0.000000
|
||||||
|
5282 0.000000
|
||||||
|
5283 0.000000
|
||||||
|
5284 0.000000
|
||||||
|
5285 0.000000
|
||||||
|
5286 0.000000
|
||||||
|
5287 0.000000
|
||||||
|
5288 0.000000
|
||||||
|
5289 0.000000
|
||||||
|
5290 0.000000
|
||||||
|
5301 0.000000
|
||||||
|
5302 0.000000
|
||||||
|
5303 0.000000
|
||||||
|
5304 0.000000
|
||||||
|
5305 0.000000
|
||||||
|
5306 0.000000
|
||||||
|
5307 0.000000
|
||||||
|
5308 0.000000
|
||||||
|
5309 0.000000
|
||||||
|
5310 0.000000
|
||||||
|
5321 0.000000
|
||||||
|
5322 0.000000
|
||||||
|
5323 0.000000
|
||||||
|
5324 0.000000
|
||||||
|
5325 0.000000
|
||||||
|
5326 0.000000
|
||||||
|
5327 0.000000
|
||||||
|
5328 0.000000
|
||||||
|
5329 0.000000
|
||||||
|
5330 0.000000
|
||||||
|
5341 0.000000
|
||||||
|
5342 0.000000
|
||||||
|
5343 0.000000
|
||||||
|
5344 0.000000
|
||||||
|
5345 0.000000
|
||||||
|
5346 0.000000
|
||||||
|
5347 0.000000
|
||||||
|
5348 0.000000
|
||||||
|
5349 0.000000
|
||||||
|
5350 0.000000
|
||||||
|
5361 0.000000
|
||||||
|
5362 0.000000
|
||||||
|
5363 0.000000
|
||||||
|
5364 0.000000
|
||||||
|
5365 0.000000
|
||||||
|
5366 0.000000
|
||||||
|
5367 0.000000
|
||||||
|
5368 0.000000
|
||||||
|
5369 0.000000
|
||||||
|
5370 0.000000
|
||||||
|
5381 0.000000
|
||||||
|
5382 0.000000
|
||||||
|
5383 0.000000
|
||||||
|
5384 0.000000
|
||||||
|
5385 0.000000
|
||||||
|
5386 0.000000
|
||||||
|
5387 0.000000
|
||||||
|
5388 0.000000
|
||||||
|
5389 0.000000
|
||||||
|
5390 0.000000
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
5161 0.000000
|
||||||
|
5162 0.000000
|
||||||
|
5163 0.000000
|
||||||
|
5164 0.000000
|
||||||
|
5165 0.000000
|
||||||
|
5166 0.000000
|
||||||
|
5167 0.000000
|
||||||
|
5168 0.000000
|
||||||
|
5169 0.000000
|
||||||
|
5181 0.000000
|
||||||
|
5182 0.000000
|
||||||
|
5183 0.000000
|
||||||
|
5184 0.000000
|
||||||
|
5185 0.000000
|
||||||
|
5186 0.000000
|
||||||
|
5187 0.000000
|
||||||
|
5188 0.000000
|
||||||
|
5189 0.000000
|
||||||
|
5210 0.000000
|
||||||
|
5211 0.000000
|
||||||
|
5212 0.000000
|
||||||
|
5213 0.000000
|
||||||
|
5214 0.000000
|
||||||
|
5215 0.000000
|
||||||
|
5216 0.000000
|
||||||
|
5217 0.000000
|
||||||
|
5218 0.000000
|
||||||
|
5219 0.000000
|
||||||
|
5220 1.000000
|
||||||
|
5221 0.000000
|
||||||
|
5222 0.000000
|
||||||
|
5223 0.000000
|
||||||
|
5224 0.000000
|
||||||
|
5225 0.000000
|
||||||
|
5226 0.000000
|
||||||
|
5227 0.000000
|
||||||
|
5228 0.000000
|
||||||
|
5229 0.000000
|
||||||
|
5230 0.000000
|
||||||
|
5241 0.000000
|
||||||
|
5242 0.000000
|
||||||
|
5243 0.000000
|
||||||
|
5244 0.000000
|
||||||
|
5245 0.000000
|
||||||
|
5246 0.000000
|
||||||
|
5247 0.000000
|
||||||
|
5248 0.000000
|
||||||
|
5249 0.000000
|
||||||
|
5250 0.000000
|
||||||
|
5261 0.000000
|
||||||
|
5262 0.000000
|
||||||
|
5263 0.000000
|
||||||
|
5264 0.000000
|
||||||
|
5265 0.000000
|
||||||
|
5266 0.000000
|
||||||
|
5267 0.000000
|
||||||
|
5268 0.000000
|
||||||
|
5269 0.000000
|
||||||
|
5270 0.000000
|
||||||
|
5281 0.000000
|
||||||
|
5282 0.000000
|
||||||
|
5283 0.000000
|
||||||
|
5284 0.000000
|
||||||
|
5285 0.000000
|
||||||
|
5286 0.000000
|
||||||
|
5287 0.000000
|
||||||
|
5288 0.000000
|
||||||
|
5289 0.000000
|
||||||
|
5290 0.000000
|
||||||
|
5301 0.000000
|
||||||
|
5302 0.000000
|
||||||
|
5303 0.000000
|
||||||
|
5304 0.000000
|
||||||
|
5305 0.000000
|
||||||
|
5306 0.000000
|
||||||
|
5307 0.000000
|
||||||
|
5308 0.000000
|
||||||
|
5309 0.000000
|
||||||
|
5310 0.000000
|
||||||
|
5321 0.000000
|
||||||
|
5322 0.000000
|
||||||
|
5323 0.000000
|
||||||
|
5324 0.000000
|
||||||
|
5325 0.000000
|
||||||
|
5326 0.000000
|
||||||
|
5327 0.000000
|
||||||
|
5328 0.000000
|
||||||
|
5329 0.000000
|
||||||
|
5330 0.000000
|
||||||
|
5341 0.000000
|
||||||
|
5342 0.000000
|
||||||
|
5343 0.000000
|
||||||
|
5344 0.000000
|
||||||
|
5345 0.000000
|
||||||
|
5346 0.000000
|
||||||
|
5347 0.000000
|
||||||
|
5348 0.000000
|
||||||
|
5349 0.000000
|
||||||
|
5350 0.000000
|
||||||
|
5361 0.000000
|
||||||
|
5362 0.000000
|
||||||
|
5363 0.000000
|
||||||
|
5364 0.000000
|
||||||
|
5365 0.000000
|
||||||
|
5366 0.000000
|
||||||
|
5367 0.000000
|
||||||
|
5368 0.000000
|
||||||
|
5369 0.000000
|
||||||
|
5370 0.000000
|
||||||
|
5381 0.000000
|
||||||
|
5382 0.000000
|
||||||
|
5383 0.000000
|
||||||
|
5384 0.000000
|
||||||
|
5385 0.000000
|
||||||
|
5386 0.000000
|
||||||
|
5387 0.000000
|
||||||
|
5388 0.000000
|
||||||
|
5389 0.000000
|
||||||
|
5390 0.000000
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
"build": "node scripts/build-static.mjs",
|
"build": "node scripts/build-static.mjs",
|
||||||
"dev": "python3 -m http.server 4173",
|
"dev": "python3 -m http.server 4173",
|
||||||
"smoke": "bash ../tests/browser/verify_gmoccapy_shell_browser.sh && bash ../tests/browser/verify_gmoccapy_dist_browser.sh",
|
"smoke": "bash ../tests/browser/verify_gmoccapy_shell_browser.sh && bash ../tests/browser/verify_gmoccapy_dist_browser.sh",
|
||||||
"smoke:node": "node ../tests/node/verify_linuxcnc_kinematics_runtime.mjs && node ../tests/node/verify_linuxcnc_interpreter_runtime.mjs && node ../tests/node/verify_linuxcnc_ini_runtime.mjs && node ../tests/node/verify_run_preconditions.mjs && node ../tests/node/verify_run_feedback_loop.mjs && node ../tests/node/verify_linuxcnc_task_hal_runtime.mjs && node ../tests/node/verify_native_task_hal_audit.mjs && node ../tests/node/verify_full_linuxcnc_5axis_source.mjs && node ../tests/node/verify_real_linuxcnc_5axis_program_cases.mjs && node ../tests/node/verify_full_execution_boundary.mjs && node ../tests/node/verify_machine_file_staging.mjs && node ../tests/node/verify_five_axis_session.mjs && node ../tests/node/verify_rtcp_store.mjs && node ../tests/node/verify_profile_boundary.mjs && node ../tests/node/verify_gmoccapy_xyzab_profile.mjs && node ../tests/node/verify_gmoccapy_icon_manifest.mjs && node ../tests/node/verify_gmoccapy_icon_registry.mjs && node ../tests/node/verify_gmoccapy_communication_model.mjs && node ../tests/node/verify_gmoccapy_hal_model.mjs && node ../tests/node/verify_gmoccapy_xyzab_gates.mjs && node ../tests/node/verify_linear_unit_conversion.mjs && node ../tests/node/verify_gmoccapy_trt_project_sidebar.mjs && node ../tests/node/verify_linuxcnc_parity_matrix.mjs"
|
"smoke:node": "node ../tests/node/verify_linuxcnc_kinematics_runtime.mjs && node ../tests/node/verify_linuxcnc_interpreter_runtime.mjs && node ../tests/node/verify_linuxcnc_ini_runtime.mjs && node ../tests/node/verify_run_preconditions.mjs && node ../tests/node/verify_run_feedback_loop.mjs && node ../tests/node/verify_linuxcnc_task_hal_runtime.mjs && node ../tests/node/verify_native_task_hal_audit.mjs && node ../tests/node/verify_full_linuxcnc_5axis_source.mjs && node ../tests/node/verify_real_linuxcnc_5axis_program_cases.mjs && node ../tests/node/verify_tool_db_web_simulation.mjs && node ../tests/node/verify_tool_db_user_m_simulation.mjs && node ../tests/node/verify_full_execution_boundary.mjs && node ../tests/node/verify_machine_file_staging.mjs && node ../tests/node/verify_five_axis_session.mjs && node ../tests/node/verify_rtcp_store.mjs && node ../tests/node/verify_profile_boundary.mjs && node ../tests/node/verify_gmoccapy_xyzac_trt_parity.mjs && node ../tests/node/verify_gmoccapy_xyzab_profile.mjs && node ../tests/node/verify_gmoccapy_icon_manifest.mjs && node ../tests/node/verify_gmoccapy_icon_registry.mjs && node ../tests/node/verify_gmoccapy_communication_model.mjs && node ../tests/node/verify_gmoccapy_hal_model.mjs && node ../tests/node/verify_gmoccapy_xyzab_gates.mjs && node ../tests/node/verify_linear_unit_conversion.mjs && node ../tests/node/verify_gmoccapy_trt_project_sidebar.mjs && node ../tests/node/verify_linuxcnc_parity_matrix.mjs"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"playwright": "^1.61.1"
|
"playwright": "^1.61.1"
|
||||||
|
|||||||
@@ -64,10 +64,17 @@ async function copyLinuxCncConfigAssets() {
|
|||||||
const configSrcDir = join(repoRoot, "wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting");
|
const configSrcDir = join(repoRoot, "wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting");
|
||||||
const configDistDir = join(distDir, "configs/sim/axis/vismach/5axis/table-rotary-tilting");
|
const configDistDir = join(distDir, "configs/sim/axis/vismach/5axis/table-rotary-tilting");
|
||||||
const vendorConfigDistDir = join(distDir, "wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting");
|
const vendorConfigDistDir = join(distDir, "wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting");
|
||||||
|
const gmoccapyConfigSrcDir = join(repoRoot, "wasm-port/vendor/linuxcnc/configs/sim/gmoccapy");
|
||||||
|
const gmoccapyConfigDistDir = join(distDir, "configs/sim/gmoccapy");
|
||||||
|
const gmoccapyVendorConfigDistDir = join(distDir, "wasm-port/vendor/linuxcnc/configs/sim/gmoccapy");
|
||||||
await mkdir(configDistDir, { recursive: true });
|
await mkdir(configDistDir, { recursive: true });
|
||||||
await mkdir(vendorConfigDistDir, { recursive: true });
|
await mkdir(vendorConfigDistDir, { recursive: true });
|
||||||
|
await mkdir(gmoccapyConfigDistDir, { recursive: true });
|
||||||
|
await mkdir(gmoccapyVendorConfigDistDir, { recursive: true });
|
||||||
await cp(configSrcDir, configDistDir, { recursive: true });
|
await cp(configSrcDir, configDistDir, { recursive: true });
|
||||||
await cp(configSrcDir, vendorConfigDistDir, { recursive: true });
|
await cp(configSrcDir, vendorConfigDistDir, { recursive: true });
|
||||||
|
await cp(gmoccapyConfigSrcDir, gmoccapyConfigDistDir, { recursive: true });
|
||||||
|
await cp(gmoccapyConfigSrcDir, gmoccapyVendorConfigDistDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copyLinuxCncReferenceAssets() {
|
async function copyLinuxCncReferenceAssets() {
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ window.webRtcp5AxisSimulation = {
|
|||||||
saveSession: store.saveSession,
|
saveSession: store.saveSession,
|
||||||
restoreSession: store.restoreSession,
|
restoreSession: store.restoreSession,
|
||||||
stageMachineFiles: store.stageMachineFiles,
|
stageMachineFiles: store.stageMachineFiles,
|
||||||
|
queryToolDb: store.queryToolDb,
|
||||||
|
editToolDb: store.editToolDb,
|
||||||
|
saveToolDb: store.saveToolDb,
|
||||||
runFullBoundaryAudit: store.runFullBoundaryAudit,
|
runFullBoundaryAudit: store.runFullBoundaryAudit,
|
||||||
getRegions: shell.getRegions,
|
getRegions: shell.getRegions,
|
||||||
iniConfigReady,
|
iniConfigReady,
|
||||||
@@ -126,6 +129,11 @@ async function ensureDefaultLinuxCncProgramPreview(store) {
|
|||||||
|
|
||||||
function selectDefaultLinuxCncSource(state) {
|
function selectDefaultLinuxCncSource(state) {
|
||||||
const sources = state.machineFileStaging?.gcodeSources || [];
|
const sources = state.machineFileStaging?.gcodeSources || [];
|
||||||
|
const profileDefault = state.profile?.machineFileStaging?.defaultProgramFilename;
|
||||||
|
if (profileDefault) {
|
||||||
|
const match = sources.find((source) => source.filename === profileDefault);
|
||||||
|
if (match) return match;
|
||||||
|
}
|
||||||
const preferredFilename = `${state.machineProfile}_switchkins.ngc`;
|
const preferredFilename = `${state.machineProfile}_switchkins.ngc`;
|
||||||
return sources.find((source) => source.filename === preferredFilename)
|
return sources.find((source) => source.filename === preferredFilename)
|
||||||
|| sources.find((source) => source.filename.includes(state.machineProfile))
|
|| sources.find((source) => source.filename.includes(state.machineProfile))
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import { xyzacTrtProfile } from "./xyzac-trt.js";
|
import { xyzacTrtProfile } from "./xyzac-trt.js";
|
||||||
import { xyzbcTrtProfile } from "./xyzbc-trt.js";
|
import { xyzbcTrtProfile } from "./xyzbc-trt.js";
|
||||||
|
import { gmoccapyXyzacTrtProfile } from "./gmoccapy-xyzac-trt.js";
|
||||||
import { gmoccapyXyzabProfile } from "./gmoccapy-xyzab.js";
|
import { gmoccapyXyzabProfile } from "./gmoccapy-xyzab.js";
|
||||||
|
|
||||||
export const fiveAxisProfiles = [xyzacTrtProfile, xyzbcTrtProfile, gmoccapyXyzabProfile];
|
export const fiveAxisProfiles = [
|
||||||
|
xyzacTrtProfile,
|
||||||
|
xyzbcTrtProfile,
|
||||||
|
gmoccapyXyzacTrtProfile,
|
||||||
|
gmoccapyXyzabProfile,
|
||||||
|
];
|
||||||
|
|
||||||
export function getFiveAxisProfile(profileId = "xyzac-trt") {
|
export function getFiveAxisProfile(profileId = "xyzac-trt") {
|
||||||
const profile = fiveAxisProfiles.find((entry) => entry.id === profileId);
|
const profile = fiveAxisProfiles.find((entry) => entry.id === profileId);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const xyzacTrtProfile = {
|
|||||||
coordinates: ["X", "Y", "Z", "A", "C"],
|
coordinates: ["X", "Y", "Z", "A", "C"],
|
||||||
joints: ["joint.0", "joint.1", "joint.2", "joint.3", "joint.4"],
|
joints: ["joint.0", "joint.1", "joint.2", "joint.3", "joint.4"],
|
||||||
kinematics: "xyzac-trt-kins",
|
kinematics: "xyzac-trt-kins",
|
||||||
|
kinematicsModuleId: "xyzac-trt",
|
||||||
kinematicsParameters: {
|
kinematicsParameters: {
|
||||||
sparm: "identityfirst",
|
sparm: "identityfirst",
|
||||||
defaultSwitchkinsType: 0,
|
defaultSwitchkinsType: 0,
|
||||||
|
|||||||
@@ -0,0 +1,219 @@
|
|||||||
|
const DEFAULT_ALLOWED_USER_M_CODES = {
|
||||||
|
M428: {
|
||||||
|
code: "M428",
|
||||||
|
label: "TCP kinematics",
|
||||||
|
kinsType: "tcp",
|
||||||
|
switchkinsType: 1,
|
||||||
|
rtcpState: "on",
|
||||||
|
sourceRel: "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc",
|
||||||
|
},
|
||||||
|
M429: {
|
||||||
|
code: "M429",
|
||||||
|
label: "Identity kinematics",
|
||||||
|
kinsType: "identity",
|
||||||
|
switchkinsType: 0,
|
||||||
|
rtcpState: "off",
|
||||||
|
sourceRel: "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc",
|
||||||
|
},
|
||||||
|
M430: {
|
||||||
|
code: "M430",
|
||||||
|
label: "User kinematics",
|
||||||
|
kinsType: "userk",
|
||||||
|
switchkinsType: 2,
|
||||||
|
rtcpState: "on",
|
||||||
|
sourceRel: "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc",
|
||||||
|
},
|
||||||
|
M128: {
|
||||||
|
code: "M128",
|
||||||
|
label: "controlled millturn mill-mode user-M",
|
||||||
|
kinsType: "mill",
|
||||||
|
switchkinsType: 0,
|
||||||
|
rtcpState: "off",
|
||||||
|
sourceRel: "linuxcnc millturn user-M reference",
|
||||||
|
},
|
||||||
|
M129: {
|
||||||
|
code: "M129",
|
||||||
|
label: "controlled millturn turn-mode user-M",
|
||||||
|
kinsType: "turn",
|
||||||
|
switchkinsType: 1,
|
||||||
|
rtcpState: "off",
|
||||||
|
sourceRel: "linuxcnc millturn user-M reference",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createControlledUserMSimulation({ allowedCodes = DEFAULT_ALLOWED_USER_M_CODES } = {}) {
|
||||||
|
return {
|
||||||
|
apiName: "web-rtcp-5axis-controlled-user-m-simulation",
|
||||||
|
ready: true,
|
||||||
|
processReady: true,
|
||||||
|
processScope: "web_simulation_only",
|
||||||
|
hostProcessReady: false,
|
||||||
|
hostProcessExecution: false,
|
||||||
|
arbitraryUserMExecution: false,
|
||||||
|
allowedCodes: Object.fromEntries(
|
||||||
|
Object.entries(allowedCodes).map(([code, definition]) => [normalizeMCode(code), {
|
||||||
|
...definition,
|
||||||
|
code: normalizeMCode(definition.code || code),
|
||||||
|
}]),
|
||||||
|
),
|
||||||
|
events: [],
|
||||||
|
blockedEvents: [],
|
||||||
|
semanticBoundary: "controlled_user_m_web_simulation_whitelist_not_host_process",
|
||||||
|
linuxCncReferences: [
|
||||||
|
"linuxcnc/src/emc/rs274ngc",
|
||||||
|
"linuxcnc/src/emc/task/emctask.cc",
|
||||||
|
"linuxcnc/src/emc/task/emccanon.cc",
|
||||||
|
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc",
|
||||||
|
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc",
|
||||||
|
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runControlledUserM(simulation, code, context = {}) {
|
||||||
|
const state = cloneSimulation(simulation);
|
||||||
|
const normalizedCode = normalizeMCode(code);
|
||||||
|
const definition = state.allowedCodes[normalizedCode] || null;
|
||||||
|
if (!definition) {
|
||||||
|
const event = createUserMEvent({
|
||||||
|
code: normalizedCode,
|
||||||
|
allowed: false,
|
||||||
|
reason: "not_in_controlled_user_m_whitelist",
|
||||||
|
context,
|
||||||
|
});
|
||||||
|
state.blockedEvents.push(event);
|
||||||
|
return {
|
||||||
|
simulation: state,
|
||||||
|
event,
|
||||||
|
statePatch: {},
|
||||||
|
halPatch: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const profileKinsType = definition.kinsType === "tcp"
|
||||||
|
? tcpKinsTypeForProfile(context.profile)
|
||||||
|
: definition.kinsType;
|
||||||
|
const halPatch = {
|
||||||
|
"motion.switchkins-type": definition.switchkinsType,
|
||||||
|
"motion.analog-out-03": definition.switchkinsType,
|
||||||
|
};
|
||||||
|
const statePatch = {
|
||||||
|
kinsType: profileKinsType,
|
||||||
|
rtcpState: definition.rtcpState,
|
||||||
|
};
|
||||||
|
const event = createUserMEvent({
|
||||||
|
code: normalizedCode,
|
||||||
|
allowed: true,
|
||||||
|
definition,
|
||||||
|
context,
|
||||||
|
halPatch,
|
||||||
|
statePatch,
|
||||||
|
});
|
||||||
|
state.events.push(event);
|
||||||
|
return {
|
||||||
|
simulation: state,
|
||||||
|
event,
|
||||||
|
statePatch,
|
||||||
|
halPatch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createControlledUserMReadiness(simulation) {
|
||||||
|
const ready = simulation?.processReady === true && simulation?.processScope === "web_simulation_only";
|
||||||
|
return {
|
||||||
|
apiName: "web-rtcp-5axis-controlled-user-m-readiness",
|
||||||
|
ready,
|
||||||
|
externalUserMProcessReady: ready,
|
||||||
|
externalUserMProcessScope: ready ? "web_simulation_only" : "not_ready",
|
||||||
|
hostExternalUserMProcessReady: false,
|
||||||
|
hostProcessExecution: false,
|
||||||
|
arbitraryUserMExecution: false,
|
||||||
|
allowedCodeCount: Object.keys(simulation?.allowedCodes || {}).length,
|
||||||
|
blockedEventCount: simulation?.blockedEvents?.length || 0,
|
||||||
|
semanticBoundary: "external_user_m_process_ready_for_web_simulation_only",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractControlledUserMCodesFromProgram(programText = "") {
|
||||||
|
const matches = [];
|
||||||
|
const lines = String(programText).split(/\r?\n/);
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
const stripped = line.replace(/\([^)]*\)/g, " ");
|
||||||
|
for (const match of stripped.matchAll(/\bM\s*(\d{2,3})\b/gi)) {
|
||||||
|
const code = normalizeMCode(`M${match[1]}`);
|
||||||
|
if (Number(match[1]) >= 100 || ["M428", "M429", "M430"].includes(code)) {
|
||||||
|
matches.push({
|
||||||
|
code,
|
||||||
|
line: index + 1,
|
||||||
|
rawLine: line,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runControlledUserMProgramScan(simulation, programText = "", context = {}) {
|
||||||
|
let state = simulation;
|
||||||
|
const events = [];
|
||||||
|
for (const occurrence of extractControlledUserMCodesFromProgram(programText)) {
|
||||||
|
const result = runControlledUserM(state, occurrence.code, {
|
||||||
|
...context,
|
||||||
|
line: occurrence.line,
|
||||||
|
rawLine: occurrence.rawLine,
|
||||||
|
});
|
||||||
|
state = result.simulation;
|
||||||
|
events.push(result.event);
|
||||||
|
}
|
||||||
|
return { simulation: state, events };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createUserMEvent({
|
||||||
|
code,
|
||||||
|
allowed,
|
||||||
|
definition = null,
|
||||||
|
context = {},
|
||||||
|
halPatch = {},
|
||||||
|
statePatch = {},
|
||||||
|
reason = null,
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
apiName: "web-rtcp-5axis-controlled-user-m-event",
|
||||||
|
code,
|
||||||
|
allowed,
|
||||||
|
reason,
|
||||||
|
label: definition?.label || null,
|
||||||
|
sourceRel: definition?.sourceRel || context.sourceRel || null,
|
||||||
|
line: context.line || null,
|
||||||
|
halPatch,
|
||||||
|
statePatch,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
semanticBoundary: allowed
|
||||||
|
? "vendored_or_whitelisted_user_m_web_simulation_event"
|
||||||
|
: "arbitrary_external_user_m_blocked",
|
||||||
|
promotionScope: "web_simulation_only",
|
||||||
|
hostProcessExecution: false,
|
||||||
|
arbitraryUserMExecution: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function tcpKinsTypeForProfile(profile) {
|
||||||
|
if (profile?.id === "xyzbc-trt") return "tcp-xyzbc";
|
||||||
|
return "tcp-xyzac";
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeMCode(code) {
|
||||||
|
const normalized = String(code || "").trim().toUpperCase().replace(/\s+/g, "");
|
||||||
|
const match = normalized.match(/^M0*(\d+)$/);
|
||||||
|
if (!match) return normalized;
|
||||||
|
return `M${Number(match[1])}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneSimulation(simulation) {
|
||||||
|
return {
|
||||||
|
...simulation,
|
||||||
|
allowedCodes: { ...(simulation.allowedCodes || {}) },
|
||||||
|
events: [...(simulation.events || [])],
|
||||||
|
blockedEvents: [...(simulation.blockedEvents || [])],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -27,7 +27,10 @@ export function createFullLinuxCncExecutionBoundary(state = {}) {
|
|||||||
&& state.machineFileStaging?.fileCount > 0;
|
&& state.machineFileStaging?.fileCount > 0;
|
||||||
const machineFileRemapReady = Boolean(
|
const machineFileRemapReady = Boolean(
|
||||||
machineFileExecution?.sourceMode === "linuxcnc-machine-file-remap-wasm" &&
|
machineFileExecution?.sourceMode === "linuxcnc-machine-file-remap-wasm" &&
|
||||||
machineFileExecution?.summary?.machineFileExecutionReady === true &&
|
machineFileExecution?.summary?.machineFileExecutionReady === true,
|
||||||
|
);
|
||||||
|
const remapRuntimeReady = Boolean(
|
||||||
|
machineFileExecution?.summary?.remapRuntimeReady === true ||
|
||||||
MACHINE_FILE_FLAGS.every((flag) => machineFileText.includes(flag)),
|
MACHINE_FILE_FLAGS.every((flag) => machineFileText.includes(flag)),
|
||||||
);
|
);
|
||||||
const plannerRuntimeReady = Boolean(
|
const plannerRuntimeReady = Boolean(
|
||||||
@@ -55,6 +58,10 @@ export function createFullLinuxCncExecutionBoundary(state = {}) {
|
|||||||
const taskHalComparisonReady = Boolean(taskHalSummary.taskHalComparisonReady === true);
|
const taskHalComparisonReady = Boolean(taskHalSummary.taskHalComparisonReady === true);
|
||||||
const nativeTaskReady = taskRuntimeReady;
|
const nativeTaskReady = taskRuntimeReady;
|
||||||
const nativeHalSyncReady = halRuntimeReady && motionRuntimeReady && halSyncReady;
|
const nativeHalSyncReady = halRuntimeReady && motionRuntimeReady && halSyncReady;
|
||||||
|
const toolDbReadiness = state.toolDbReadiness || {};
|
||||||
|
const controlledUserMReadiness = state.controlledUserMReadiness || {};
|
||||||
|
const toolDbProcessReady = toolDbReadiness.toolDbProcessReady === true;
|
||||||
|
const externalUserMProcessReady = controlledUserMReadiness.externalUserMProcessReady === true;
|
||||||
const fullLinuxCncProgramExecutionReady = Boolean(
|
const fullLinuxCncProgramExecutionReady = Boolean(
|
||||||
kinematicsReady &&
|
kinematicsReady &&
|
||||||
interpreterReady &&
|
interpreterReady &&
|
||||||
@@ -64,7 +71,9 @@ export function createFullLinuxCncExecutionBoundary(state = {}) {
|
|||||||
plannerRuntimeReady &&
|
plannerRuntimeReady &&
|
||||||
nativeTaskReady &&
|
nativeTaskReady &&
|
||||||
nativeHalSyncReady &&
|
nativeHalSyncReady &&
|
||||||
taskHalComparisonReady
|
taskHalComparisonReady &&
|
||||||
|
toolDbProcessReady &&
|
||||||
|
externalUserMProcessReady
|
||||||
);
|
);
|
||||||
|
|
||||||
const satisfied = [
|
const satisfied = [
|
||||||
@@ -80,6 +89,8 @@ export function createFullLinuxCncExecutionBoundary(state = {}) {
|
|||||||
halRuntimeReady ? "linuxcnc-hal-runtime" : null,
|
halRuntimeReady ? "linuxcnc-hal-runtime" : null,
|
||||||
halSyncReady ? "task-motion-hal-sync" : null,
|
halSyncReady ? "task-motion-hal-sync" : null,
|
||||||
taskHalComparisonReady ? "task-hal-cycle-artifact" : null,
|
taskHalComparisonReady ? "task-hal-cycle-artifact" : null,
|
||||||
|
toolDbProcessReady ? "tool-db-web-simulation" : null,
|
||||||
|
externalUserMProcessReady ? "controlled-user-m-web-simulation" : null,
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
const missing = [];
|
const missing = [];
|
||||||
@@ -95,11 +106,15 @@ export function createFullLinuxCncExecutionBoundary(state = {}) {
|
|||||||
if (!halRuntimeReady) missing.push("LinuxCNC HAL runtime");
|
if (!halRuntimeReady) missing.push("LinuxCNC HAL runtime");
|
||||||
if (!halSyncReady) missing.push("task/motion/HAL synchronization");
|
if (!halSyncReady) missing.push("task/motion/HAL synchronization");
|
||||||
if (!taskHalComparisonReady) missing.push("task cycle and HAL servo cycle artifact");
|
if (!taskHalComparisonReady) missing.push("task cycle and HAL servo cycle artifact");
|
||||||
|
if (!toolDbProcessReady) missing.push("tool DB Web/WASM simulation process");
|
||||||
|
if (!externalUserMProcessReady) missing.push("controlled user-M Web/WASM simulation process");
|
||||||
|
|
||||||
const blockers = [];
|
const blockers = [];
|
||||||
if (!nativeTaskReady) blockers.push("LinuxCNC task runtime is not promoted");
|
if (!nativeTaskReady) blockers.push("LinuxCNC task runtime is not promoted");
|
||||||
if (!nativeHalSyncReady) blockers.push("realtime HAL synchronization is not promoted");
|
if (!nativeHalSyncReady) blockers.push("realtime HAL synchronization is not promoted");
|
||||||
blockers.push("external user-M process and full tool DB process are not promoted");
|
if (!toolDbProcessReady) blockers.push("tool DB Web simulation process is not ready");
|
||||||
|
if (!externalUserMProcessReady) blockers.push("controlled user-M Web simulation process is not ready");
|
||||||
|
blockers.push("host external user-M process and host tool DB process remain disabled; Web simulation boundary only");
|
||||||
if (!plannerRuntimeReady) {
|
if (!plannerRuntimeReady) {
|
||||||
blockers.push("LinuxCNC trajectory planner queue is not promoted as browser runtime");
|
blockers.push("LinuxCNC trajectory planner queue is not promoted as browser runtime");
|
||||||
}
|
}
|
||||||
@@ -126,7 +141,7 @@ export function createFullLinuxCncExecutionBoundary(state = {}) {
|
|||||||
: programExecution?.sourceMode || state.programExecutionSourceMode || "fixture-line-playback",
|
: programExecution?.sourceMode || state.programExecutionSourceMode || "fixture-line-playback",
|
||||||
readyForUiSimulation: kinematicsReady && interpreterReady && canonicalProgramReady,
|
readyForUiSimulation: kinematicsReady && interpreterReady && canonicalProgramReady,
|
||||||
machineFileBackedRemapReady: machineFileRemapReady,
|
machineFileBackedRemapReady: machineFileRemapReady,
|
||||||
remapRuntimeReady: machineFileRemapReady,
|
remapRuntimeReady,
|
||||||
halSwitchkinsEvidenceReady,
|
halSwitchkinsEvidenceReady,
|
||||||
plannerRuntimeReady,
|
plannerRuntimeReady,
|
||||||
taskRuntimeReady,
|
taskRuntimeReady,
|
||||||
@@ -140,8 +155,13 @@ export function createFullLinuxCncExecutionBoundary(state = {}) {
|
|||||||
promotionAllowed: fullLinuxCncProgramExecutionReady,
|
promotionAllowed: fullLinuxCncProgramExecutionReady,
|
||||||
hardwareDrive: false,
|
hardwareDrive: false,
|
||||||
hostRealtimeKernel: false,
|
hostRealtimeKernel: false,
|
||||||
externalUserMProcessReady: false,
|
externalUserMProcessReady,
|
||||||
toolDbProcessReady: false,
|
externalUserMProcessScope: externalUserMProcessReady ? "web_simulation_only" : "not_ready",
|
||||||
|
toolDbProcessReady,
|
||||||
|
toolDbProcessScope: toolDbProcessReady ? "web_simulation_only" : "not_ready",
|
||||||
|
hostExternalUserMProcessReady: false,
|
||||||
|
hostToolDbProcessReady: false,
|
||||||
|
arbitraryUserMExecution: false,
|
||||||
satisfied,
|
satisfied,
|
||||||
missing,
|
missing,
|
||||||
blockers,
|
blockers,
|
||||||
@@ -153,12 +173,15 @@ export function createFullLinuxCncExecutionBoundary(state = {}) {
|
|||||||
plannerTiming: plannerRuntimeReady ? programExecution.plannerTiming?.semanticBoundary : null,
|
plannerTiming: plannerRuntimeReady ? programExecution.plannerTiming?.semanticBoundary : null,
|
||||||
machineFileFlags: MACHINE_FILE_FLAGS.filter((flag) => machineFileText.includes(flag)),
|
machineFileFlags: MACHINE_FILE_FLAGS.filter((flag) => machineFileText.includes(flag)),
|
||||||
machineFileExecutionReady: machineFileExecution?.summary?.machineFileExecutionReady === true,
|
machineFileExecutionReady: machineFileExecution?.summary?.machineFileExecutionReady === true,
|
||||||
|
remapRuntimeReady,
|
||||||
stagedFileCount: state.machineFileStaging?.fileCount || 0,
|
stagedFileCount: state.machineFileStaging?.fileCount || 0,
|
||||||
taskHal: taskHalSummary,
|
taskHal: taskHalSummary,
|
||||||
taskCycle: state.taskHalStatus?.ui?.taskCycle || 0,
|
taskCycle: state.taskHalStatus?.ui?.taskCycle || 0,
|
||||||
servoCycle: state.taskHalStatus?.ui?.servoCycle || 0,
|
servoCycle: state.taskHalStatus?.ui?.servoCycle || 0,
|
||||||
motionQueueDepth: state.taskHalStatus?.ui?.motionQueueDepth || 0,
|
motionQueueDepth: state.taskHalStatus?.ui?.motionQueueDepth || 0,
|
||||||
halChangedPinCount: state.taskHalStatus?.ui?.halChangedPinCount || 0,
|
halChangedPinCount: state.taskHalStatus?.ui?.halChangedPinCount || 0,
|
||||||
|
toolDb: toolDbReadiness,
|
||||||
|
controlledUserM: controlledUserMReadiness,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,9 @@ export function applyIniConfigToProfile(profile, iniConfig) {
|
|||||||
...iniConfig.kinematicsParameters,
|
...iniConfig.kinematicsParameters,
|
||||||
switchkinsTypes: mergeSwitchkinsTypes(
|
switchkinsTypes: mergeSwitchkinsTypes(
|
||||||
profile.kinematicsParameters?.switchkinsTypes || [],
|
profile.kinematicsParameters?.switchkinsTypes || [],
|
||||||
iniConfig.kinematicsParameters.switchkinsTypes,
|
iniConfig.halui.mdiCommands.length > 0
|
||||||
|
? iniConfig.kinematicsParameters.switchkinsTypes
|
||||||
|
: [],
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
display: {
|
display: {
|
||||||
@@ -363,18 +365,25 @@ function validateIniConfig({
|
|||||||
emcio,
|
emcio,
|
||||||
}) {
|
}) {
|
||||||
const missing = [];
|
const missing = [];
|
||||||
|
const displayName = getFirstValue(sections, "DISPLAY", "DISPLAY") || "";
|
||||||
|
const isSwitchkinsTrt = String(kinsText || "").includes("sparm=identityfirst");
|
||||||
|
const isGmoccapyFixedTrt = String(displayName).toLowerCase() === "gmoccapy"
|
||||||
|
&& /^xyz[ab]c-trt-kins\b/i.test(String(kinsText || ""))
|
||||||
|
&& !isSwitchkinsTrt;
|
||||||
const requiredSections = [
|
const requiredSections = [
|
||||||
"EMC",
|
"EMC",
|
||||||
"DISPLAY",
|
"DISPLAY",
|
||||||
"RS274NGC",
|
"RS274NGC",
|
||||||
"KINS",
|
"KINS",
|
||||||
"HAL",
|
"HAL",
|
||||||
"HALUI",
|
|
||||||
"TRAJ",
|
"TRAJ",
|
||||||
"EMCMOT",
|
"EMCMOT",
|
||||||
"TASK",
|
"TASK",
|
||||||
"EMCIO",
|
"EMCIO",
|
||||||
];
|
];
|
||||||
|
if (isSwitchkinsTrt) {
|
||||||
|
requiredSections.push("HALUI");
|
||||||
|
}
|
||||||
for (const section of requiredSections) {
|
for (const section of requiredSections) {
|
||||||
if (!sections.has(section)) missing.push(`[${section}]`);
|
if (!sections.has(section)) missing.push(`[${section}]`);
|
||||||
}
|
}
|
||||||
@@ -383,7 +392,7 @@ function validateIniConfig({
|
|||||||
if (!jointCount) missing.push("KINS.JOINTS");
|
if (!jointCount) missing.push("KINS.JOINTS");
|
||||||
if (jointCount !== 5) missing.push("KINS.JOINTS=5");
|
if (jointCount !== 5) missing.push("KINS.JOINTS=5");
|
||||||
if (!kinsText) missing.push("KINS.KINEMATICS");
|
if (!kinsText) missing.push("KINS.KINEMATICS");
|
||||||
if (!String(kinsText || "").includes("sparm=identityfirst")) {
|
if (!isSwitchkinsTrt && !isGmoccapyFixedTrt) {
|
||||||
missing.push("KINS.KINEMATICS sparm=identityfirst");
|
missing.push("KINS.KINEMATICS sparm=identityfirst");
|
||||||
}
|
}
|
||||||
for (const axis of String(coordinates || "").split("")) {
|
for (const axis of String(coordinates || "").split("")) {
|
||||||
@@ -395,20 +404,38 @@ function validateIniConfig({
|
|||||||
for (let joint = 0; joint < 5; joint += 1) {
|
for (let joint = 0; joint < 5; joint += 1) {
|
||||||
if (!sections.has(`JOINT_${joint}`)) missing.push(`JOINT_${joint}`);
|
if (!sections.has(`JOINT_${joint}`)) missing.push(`JOINT_${joint}`);
|
||||||
}
|
}
|
||||||
|
if (isSwitchkinsTrt) {
|
||||||
for (const code of ["M428", "M429", "M430"]) {
|
for (const code of ["M428", "M429", "M430"]) {
|
||||||
if (!remaps.some((remap) => remap.code === code && remap.ngc)) {
|
if (!remaps.some((remap) => remap.code === code && remap.ngc)) {
|
||||||
missing.push(`RS274NGC.REMAP ${code}`);
|
missing.push(`RS274NGC.REMAP ${code}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rs274ngc.halPinVars !== true) missing.push("RS274NGC.HAL_PIN_VARS=1");
|
if (rs274ngc.halPinVars !== true) missing.push("RS274NGC.HAL_PIN_VARS=1");
|
||||||
|
} else if (isGmoccapyFixedTrt) {
|
||||||
|
for (const code of ["M6", "M61"]) {
|
||||||
|
if (!remaps.some((remap) => remap.code === code && remap.ngc)) {
|
||||||
|
missing.push(`RS274NGC.REMAP ${code}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (rs274ngc.halPinVars !== true) missing.push("RS274NGC.HAL_PIN_VARS=1");
|
||||||
|
}
|
||||||
if (!rs274ngc.parameterFile) missing.push("RS274NGC.PARAMETER_FILE");
|
if (!rs274ngc.parameterFile) missing.push("RS274NGC.PARAMETER_FILE");
|
||||||
if (!hal.halui) missing.push("HAL.HALUI");
|
if (!hal.halui) missing.push("HAL.HALUI");
|
||||||
if (!hal.halFiles.length) missing.push("HAL.HALFILE");
|
if (!hal.halFiles.length) missing.push("HAL.HALFILE");
|
||||||
if (!hal.postguiHalFiles.length) missing.push("HAL.POSTGUI_HALFILE");
|
if (!hal.postguiHalFiles.length) missing.push("HAL.POSTGUI_HALFILE");
|
||||||
if (!hal.halcmd.some((line) => line.includes("motion.analog-out-03") && line.includes("motion.switchkins-type"))) {
|
if (isSwitchkinsTrt && !hal.halcmd.some((line) => line.includes("motion.analog-out-03") && line.includes("motion.switchkins-type"))) {
|
||||||
missing.push("HAL.HALCMD motion.analog-out-03=>motion.switchkins-type");
|
missing.push("HAL.HALCMD motion.analog-out-03=>motion.switchkins-type");
|
||||||
}
|
}
|
||||||
if (halui.mdiCommands.length < 3) missing.push("HALUI.MDI_COMMAND M428/M429/M430");
|
if (isGmoccapyFixedTrt) {
|
||||||
|
for (const token of ["xyzac-trt-gui", "xyzac-trt-kins.tool-offset", "xyzac-trt-kins.y-offset", "xyzac-trt-kins.z-offset"]) {
|
||||||
|
if (!hal.halcmd.some((line) => line.includes(token))) {
|
||||||
|
missing.push(`HAL.HALCMD ${token}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (isSwitchkinsTrt && halui.mdiCommands.length < 3) {
|
||||||
|
missing.push("HALUI.MDI_COMMAND M428/M429/M430");
|
||||||
|
}
|
||||||
if (!emcmot.module) missing.push("EMCMOT.EMCMOT");
|
if (!emcmot.module) missing.push("EMCMOT.EMCMOT");
|
||||||
if (!emcmot.servoPeriodNs) missing.push("EMCMOT.SERVO_PERIOD");
|
if (!emcmot.servoPeriodNs) missing.push("EMCMOT.SERVO_PERIOD");
|
||||||
if (!task.module) missing.push("TASK.TASK");
|
if (!task.module) missing.push("TASK.TASK");
|
||||||
|
|||||||
@@ -129,10 +129,15 @@ function createProgramExecutionResult({
|
|||||||
semanticBoundary = SEMANTIC_BOUNDARY,
|
semanticBoundary = SEMANTIC_BOUNDARY,
|
||||||
plannerTiming = null,
|
plannerTiming = null,
|
||||||
}) {
|
}) {
|
||||||
const canonicalEventCount = String(resultText).split("\n").filter((line) => line.startsWith("canon_event=")).length;
|
const resultTextString = String(resultText);
|
||||||
const machineFileExecutionReady = Boolean(
|
const canonicalEventCount = resultTextString.split("\n").filter((line) => line.startsWith("canon_event=")).length;
|
||||||
machineFilePlan && FIVE_AXIS_REMAP_FLAGS.every((flag) => String(resultText).includes(flag)),
|
const remapRuntimeReady = Boolean(machineFilePlan && resultTextString.includes("fiveaxis_remaps_ready=1"));
|
||||||
);
|
const machineFileRunCompleted = Boolean(machineFilePlan && (
|
||||||
|
FIVE_AXIS_REMAP_FLAGS.every((flag) => resultTextString.includes(flag)) ||
|
||||||
|
resultTextString.includes("fiveaxis_file_reached_exit=1") ||
|
||||||
|
resultTextString.includes("fiveaxis_linuxcnc_remap_file_execute=0")
|
||||||
|
));
|
||||||
|
const machineFileExecutionReady = Boolean(machineFileRunCompleted && motion.length > 0);
|
||||||
const plannerRuntimeReady = plannerTiming?.plannerRuntimeReady === true
|
const plannerRuntimeReady = plannerTiming?.plannerRuntimeReady === true
|
||||||
&& plannerTiming.motionCount === motion.length;
|
&& plannerTiming.motionCount === motion.length;
|
||||||
return {
|
return {
|
||||||
@@ -167,7 +172,7 @@ function createProgramExecutionResult({
|
|||||||
switchkinsEventCount: switchkinsEvents.length,
|
switchkinsEventCount: switchkinsEvents.length,
|
||||||
switchkinsCodes: [...new Set(switchkinsEvents.map((event) => event.code))],
|
switchkinsCodes: [...new Set(switchkinsEvents.map((event) => event.code))],
|
||||||
switchkinsRemapBoundary: switchkinsEvents.length > 0 ? SWITCHKINS_REMAP_BOUNDARY : null,
|
switchkinsRemapBoundary: switchkinsEvents.length > 0 ? SWITCHKINS_REMAP_BOUNDARY : null,
|
||||||
remapRuntimeReady: machineFileExecutionReady,
|
remapRuntimeReady,
|
||||||
plannerRuntimeReady,
|
plannerRuntimeReady,
|
||||||
plannerSemanticBoundary: plannerRuntimeReady ? PLANNER_TIMING_BOUNDARY : null,
|
plannerSemanticBoundary: plannerRuntimeReady ? PLANNER_TIMING_BOUNDARY : null,
|
||||||
machineFileExecutionReady,
|
machineFileExecutionReady,
|
||||||
@@ -245,7 +250,7 @@ export function parseLinuxCncCanonicalMotion(resultText, programText = "", switc
|
|||||||
let activeLinearUnits = "mm";
|
let activeLinearUnits = "mm";
|
||||||
|
|
||||||
for (const line of String(resultText).split("\n")) {
|
for (const line of String(resultText).split("\n")) {
|
||||||
const feedRate = readCanonicalNumber(line, "feed_rate");
|
const feedRate = readCanonicalFeedRate(line);
|
||||||
if (Number.isFinite(feedRate) && feedRate > 0) {
|
if (Number.isFinite(feedRate) && feedRate > 0) {
|
||||||
activeFeedRate = feedRate;
|
activeFeedRate = feedRate;
|
||||||
}
|
}
|
||||||
@@ -290,7 +295,8 @@ export function parseLinuxCncCanonicalMotion(resultText, programText = "", switc
|
|||||||
const event = latestSwitchkinsEventAtOrBeforeLine(switchkinsByLine, sourceLine);
|
const event = latestSwitchkinsEventAtOrBeforeLine(switchkinsByLine, sourceLine);
|
||||||
if (event) activeSwitchkinsEvent = event;
|
if (event) activeSwitchkinsEvent = event;
|
||||||
const sourceFeedRate = latestFeedRateAtOrBeforeLine(feedRatesByLine, sourceLine);
|
const sourceFeedRate = latestFeedRateAtOrBeforeLine(feedRatesByLine, sourceLine);
|
||||||
if (Number.isFinite(sourceFeedRate) && sourceFeedRate > 0) {
|
if ((!Number.isFinite(activeFeedRate) || activeFeedRate <= 0)
|
||||||
|
&& Number.isFinite(sourceFeedRate) && sourceFeedRate > 0) {
|
||||||
activeFeedRate = sourceFeedRate;
|
activeFeedRate = sourceFeedRate;
|
||||||
}
|
}
|
||||||
activeFeedMode = latestFeedModeAtOrBeforeLine(feedModesByLine, sourceLine) || activeFeedMode;
|
activeFeedMode = latestFeedModeAtOrBeforeLine(feedModesByLine, sourceLine) || activeFeedMode;
|
||||||
@@ -485,6 +491,17 @@ function readCanonicalNumber(line, field) {
|
|||||||
return match ? Number(match[1]) : null;
|
return match ? Number(match[1]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readCanonicalFeedRate(line) {
|
||||||
|
const text = String(line);
|
||||||
|
if (text.startsWith("canon_event=SET_FEED_RATE")) {
|
||||||
|
return readCanonicalNumber(text, "rate");
|
||||||
|
}
|
||||||
|
if (text.startsWith("canon_event=UPDATE_TAG")) {
|
||||||
|
return readCanonicalNumber(text, "feed");
|
||||||
|
}
|
||||||
|
return readCanonicalNumber(text, "feed_rate");
|
||||||
|
}
|
||||||
|
|
||||||
async function createDefaultModuleOptions({ wasmRoot }) {
|
async function createDefaultModuleOptions({ wasmRoot }) {
|
||||||
const quietOptions = { print() {}, printErr() {} };
|
const quietOptions = { print() {}, printErr() {} };
|
||||||
if (!isNodeRuntime()) return quietOptions;
|
if (!isNodeRuntime()) return quietOptions;
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ const DEFAULT_TEST_SOURCE_ROOT_URLS = [
|
|||||||
];
|
];
|
||||||
const TRT_MACHINE_REL = "axis/vismach/5axis/table-rotary-tilting";
|
const TRT_MACHINE_REL = "axis/vismach/5axis/table-rotary-tilting";
|
||||||
const TRT_DEMO_SOURCE_PREFIX = `configs/sim/${TRT_MACHINE_REL}/demos/`;
|
const TRT_DEMO_SOURCE_PREFIX = `configs/sim/${TRT_MACHINE_REL}/demos/`;
|
||||||
|
const GMOCAPY_TRT_MACHINE_REL = "gmoccapy/non_trivial_kinematics/table-rotary-tilting";
|
||||||
|
const GMOCAPY_TRT_EXAMPLE_SOURCE_PREFIX = `configs/sim/${GMOCAPY_TRT_MACHINE_REL}/examples/`;
|
||||||
const OPFS_ROOT = "web-rtcp-5axis-sim-plan/machines";
|
const OPFS_ROOT = "web-rtcp-5axis-sim-plan/machines";
|
||||||
let browserMemoryMachineFileStorage = null;
|
let browserMemoryMachineFileStorage = null;
|
||||||
|
|
||||||
@@ -38,20 +40,26 @@ export async function createMachineFileStagingPlan({
|
|||||||
);
|
);
|
||||||
const resolvedIniText = iniText ?? await readTextFromCandidateUrls(sourceUrlsFor(profile.iniPath));
|
const resolvedIniText = iniText ?? await readTextFromCandidateUrls(sourceUrlsFor(profile.iniPath));
|
||||||
const iniFile = basename(profile.iniPath);
|
const iniFile = basename(profile.iniPath);
|
||||||
|
const machineRel = machineRelForProfile(profile);
|
||||||
|
const demoDirectory = demoDirectoryForProfile(profile);
|
||||||
const plan = planSimConfigStaging({
|
const plan = planSimConfigStaging({
|
||||||
manifestText: resolvedManifestText,
|
manifestText: resolvedManifestText,
|
||||||
machineRel: TRT_MACHINE_REL,
|
machineRel,
|
||||||
iniFile,
|
iniFile,
|
||||||
iniText: resolvedIniText,
|
iniText: resolvedIniText,
|
||||||
wasmDir: wasmDir || `/work/sim/${TRT_MACHINE_REL}/${profile.id}`,
|
wasmDir: wasmDir || profile.machineFileStaging?.wasmDir || `/work/sim/${machineRel}/${profile.id}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const files = addVendoredDemoSources(plan.files, resolvedManifestText, plan.wasmDir);
|
const files = addVendoredDemoSources(plan.files, resolvedManifestText, plan.wasmDir, {
|
||||||
|
sourcePrefix: sourcePrefixForProfile(profile),
|
||||||
|
demoDirectory,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
apiName: "web-rtcp-5axis-machine-file-staging-plan",
|
apiName: "web-rtcp-5axis-machine-file-staging-plan",
|
||||||
profileId: profile.id,
|
profileId: profile.id,
|
||||||
machineRel: TRT_MACHINE_REL,
|
machineRel,
|
||||||
|
demoDirectory,
|
||||||
iniPath: profile.iniPath,
|
iniPath: profile.iniPath,
|
||||||
wasmDir: plan.wasmDir,
|
wasmDir: plan.wasmDir,
|
||||||
wasmIniPath: plan.iniPath,
|
wasmIniPath: plan.iniPath,
|
||||||
@@ -75,7 +83,7 @@ export async function createMachineFileStagingPlan({
|
|||||||
|
|
||||||
export function listLinuxCncGcodeSources(save) {
|
export function listLinuxCncGcodeSources(save) {
|
||||||
return [...(save?.files || [])]
|
return [...(save?.files || [])]
|
||||||
.filter((file) => file.kind === "demo" && isLinuxCncFiveAxisGcodeSourceRel(file.sourceRel))
|
.filter((file) => file.kind === "demo" && isLinuxCncFiveAxisGcodeSourceRel(file.sourceRel, save?.plan || save))
|
||||||
.map((file) => ({
|
.map((file) => ({
|
||||||
sourceRel: file.sourceRel,
|
sourceRel: file.sourceRel,
|
||||||
wasmPath: file.wasmPath,
|
wasmPath: file.wasmPath,
|
||||||
@@ -107,7 +115,7 @@ export function listProjectGcodeFiles(save) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function selectMachineFileProgram(plan, save, sourceRel) {
|
export function selectMachineFileProgram(plan, save, sourceRel) {
|
||||||
if (!isLinuxCncFiveAxisGcodeSourceRel(sourceRel)) {
|
if (!isLinuxCncFiveAxisGcodeSourceRel(sourceRel, plan)) {
|
||||||
throw new Error(`5-axis G-code source must come from LinuxCNC source demos: ${sourceRel}`);
|
throw new Error(`5-axis G-code source must come from LinuxCNC source demos: ${sourceRel}`);
|
||||||
}
|
}
|
||||||
const selectedFile = (save?.files || []).find((file) => file.sourceRel === sourceRel);
|
const selectedFile = (save?.files || []).find((file) => file.sourceRel === sourceRel);
|
||||||
@@ -163,7 +171,8 @@ export async function saveMachineFileStagingPlan(plan, options = {}) {
|
|||||||
storageMode: storage.mode,
|
storageMode: storage.mode,
|
||||||
storageCapability: storage.capability,
|
storageCapability: storage.capability,
|
||||||
files: savedFiles,
|
files: savedFiles,
|
||||||
gcodeSources: listLinuxCncGcodeSources({ files: savedFiles }),
|
plan,
|
||||||
|
gcodeSources: listLinuxCncGcodeSources({ ...plan, files: savedFiles }),
|
||||||
gcodeFiles: listProjectGcodeFiles({ files: savedFiles }),
|
gcodeFiles: listProjectGcodeFiles({ files: savedFiles }),
|
||||||
summary: summarizeSavedFiles(savedFiles),
|
summary: summarizeSavedFiles(savedFiles),
|
||||||
taskHalSession: {
|
taskHalSession: {
|
||||||
@@ -279,7 +288,11 @@ function summarizePlan(files) {
|
|||||||
const kinds = countKinds(files.map((file) => classifySourceRel(file.sourceRel)));
|
const kinds = countKinds(files.map((file) => classifySourceRel(file.sourceRel)));
|
||||||
return {
|
return {
|
||||||
fileCount: files.length,
|
fileCount: files.length,
|
||||||
requiredFileCount: files.filter((file) => file.sourceRel.endsWith(".ini") || file.sourceRel.includes("/demos/")).length,
|
requiredFileCount: files.filter((file) => (
|
||||||
|
file.sourceRel.endsWith(".ini")
|
||||||
|
|| file.sourceRel.includes("/demos/")
|
||||||
|
|| file.sourceRel.includes("/examples/")
|
||||||
|
)).length,
|
||||||
gcodeFileCount: (kinds.demo || 0) + (kinds.remap || 0),
|
gcodeFileCount: (kinds.demo || 0) + (kinds.remap || 0),
|
||||||
remapFileCount: kinds.remap || 0,
|
remapFileCount: kinds.remap || 0,
|
||||||
demoFileCount: kinds.demo || 0,
|
demoFileCount: kinds.demo || 0,
|
||||||
@@ -303,25 +316,29 @@ function createTaskHalSession({ profileId, wasmDir, iniPath, programPath, files
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function addVendoredDemoSources(files, manifestText, wasmDir) {
|
function addVendoredDemoSources(files, manifestText, wasmDir, {
|
||||||
|
sourcePrefix = TRT_DEMO_SOURCE_PREFIX,
|
||||||
|
demoDirectory = "demos",
|
||||||
|
} = {}) {
|
||||||
const bySourceRel = new Map(files.map((file) => [file.sourceRel, file]));
|
const bySourceRel = new Map(files.map((file) => [file.sourceRel, file]));
|
||||||
for (const sourceRel of String(manifestText).split(/\r?\n/)) {
|
for (const sourceRel of String(manifestText).split(/\r?\n/)) {
|
||||||
if (!isLinuxCncFiveAxisGcodeSourceRel(sourceRel)) continue;
|
if (!isLinuxCncFiveAxisGcodeSourceRel(sourceRel, { gcodeSourcePrefix: sourcePrefix })) continue;
|
||||||
if (bySourceRel.has(sourceRel)) continue;
|
if (bySourceRel.has(sourceRel)) continue;
|
||||||
bySourceRel.set(sourceRel, {
|
bySourceRel.set(sourceRel, {
|
||||||
sourceRel,
|
sourceRel,
|
||||||
wasmPath: `${wasmDir}/demos/${basename(sourceRel)}`,
|
wasmPath: `${wasmDir}/${demoDirectory}/${basename(sourceRel)}`,
|
||||||
executable: false,
|
executable: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return [...bySourceRel.values()];
|
return [...bySourceRel.values()];
|
||||||
}
|
}
|
||||||
|
|
||||||
function isLinuxCncFiveAxisGcodeSourceRel(sourceRel) {
|
function isLinuxCncFiveAxisGcodeSourceRel(sourceRel, context = {}) {
|
||||||
const value = String(sourceRel || "");
|
const value = String(sourceRel || "");
|
||||||
return value.startsWith(TRT_DEMO_SOURCE_PREFIX)
|
const sourcePrefix = gcodeSourcePrefixForContext(context);
|
||||||
|
return value.startsWith(sourcePrefix)
|
||||||
&& value.endsWith(".ngc")
|
&& value.endsWith(".ngc")
|
||||||
&& !value.slice(TRT_DEMO_SOURCE_PREFIX.length).includes("/");
|
&& !value.slice(sourcePrefix.length).includes("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
function summarizeSavedFiles(files) {
|
function summarizeSavedFiles(files) {
|
||||||
@@ -347,12 +364,35 @@ function classifySourceRel(sourceRel) {
|
|||||||
if (sourceRel.endsWith(".tbl")) return "toolTable";
|
if (sourceRel.endsWith(".tbl")) return "toolTable";
|
||||||
if (sourceRel.endsWith(".hal")) return "hal";
|
if (sourceRel.endsWith(".hal")) return "hal";
|
||||||
if (sourceRel.includes("/remap_subs/")) return "remap";
|
if (sourceRel.includes("/remap_subs/")) return "remap";
|
||||||
if (sourceRel.includes("/demos/")) return "demo";
|
if (sourceRel.includes("/demos/") || sourceRel.includes("/examples/")) return "demo";
|
||||||
if (sourceRel.endsWith(".xml")) return "pyvcp";
|
if (sourceRel.endsWith(".xml")) return "pyvcp";
|
||||||
if (sourceRel.endsWith(".var")) return "parameters";
|
if (sourceRel.endsWith(".var")) return "parameters";
|
||||||
return "asset";
|
return "asset";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function machineRelForProfile(profile = {}) {
|
||||||
|
return profile.machineFileStaging?.machineRel || TRT_MACHINE_REL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function demoDirectoryForProfile(profile = {}) {
|
||||||
|
return profile.machineFileStaging?.demoDirectory || "demos";
|
||||||
|
}
|
||||||
|
|
||||||
|
function sourcePrefixForProfile(profile = {}) {
|
||||||
|
if (machineRelForProfile(profile) === GMOCAPY_TRT_MACHINE_REL) {
|
||||||
|
return GMOCAPY_TRT_EXAMPLE_SOURCE_PREFIX;
|
||||||
|
}
|
||||||
|
return `configs/sim/${machineRelForProfile(profile)}/${demoDirectoryForProfile(profile)}/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gcodeSourcePrefixForContext(context = {}) {
|
||||||
|
if (context.gcodeSourcePrefix) return context.gcodeSourcePrefix;
|
||||||
|
if (context.machineRel === GMOCAPY_TRT_MACHINE_REL || context.demoDirectory === "examples") {
|
||||||
|
return GMOCAPY_TRT_EXAMPLE_SOURCE_PREFIX;
|
||||||
|
}
|
||||||
|
return TRT_DEMO_SOURCE_PREFIX;
|
||||||
|
}
|
||||||
|
|
||||||
function opfsPathFor(profileId, sourceRel) {
|
function opfsPathFor(profileId, sourceRel) {
|
||||||
return `${OPFS_ROOT}/${assertPathSegment(profileId)}/${String(sourceRel).replaceAll("\\", "/")}`;
|
return `${OPFS_ROOT}/${assertPathSegment(profileId)}/${String(sourceRel).replaceAll("\\", "/")}`;
|
||||||
}
|
}
|
||||||
|
|||||||