Make machine configuration authoritative and restore native checkpoints exactly

This commit is contained in:
2026-09-18 09:09:41 +08:00
parent 6a69511276
commit 4a568f1f4c
94 changed files with 10882 additions and 301 deletions

View File

@@ -0,0 +1,65 @@
import assert from "node:assert/strict";
import { readFile, writeFile, mkdir } from "node:fs/promises";
import { pathToFileURL } from "node:url";
const { chromium } = await import(pathToFileURL(process.env.PLAYWRIGHT_MODULE).href);
const root = new URL("../", import.meta.url);
const settings = JSON.parse(await readFile(new URL("web/workspace/web-settings.json", root), "utf8"));
const catalog = JSON.parse(await readFile(new URL("web/workspace/machine-catalog.json", root), "utf8"));
settings.ui.jog_increments = ["0.25", "2.5"];
settings.viewer.colors.background = "#550055";
settings.viewer.colors.path = "#00ff00";
settings.assets.axis_images = "configured-icons";
catalog.default_machine = "xyzbc-trt";
const sourceIni = catalog.machines[1].entrypoint;
catalog.machines[1].entrypoint = sourceIni.replace(/[^/]+$/, "configured-machine.ini");
catalog.machines[1].program = catalog.machines[1].program.replace(/[^/]+$/, "configured-preview.ngc");
const browser = await chromium.launch({ executablePath: process.env.BROWSER_EXECUTABLE_PATH ?? "/usr/bin/google-chrome", headless: true, args: ["--no-sandbox", "--use-angle=swiftshader", "--enable-unsafe-swiftshader"] });
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
const errors = []; page.on("pageerror", (error) => errors.push(error.message));
const configRequests = [], iconRequests = [];
await page.route(/web-settings\.json\?import|machine-catalog\.json\?import/, async (route) => {
configRequests.push(route.request().url());
const value = route.request().url().includes("web-settings.json") ? settings : catalog;
await route.fulfill({ contentType: "text/javascript", body: `export default ${JSON.stringify(value)};` });
});
await page.route("**/configured-icons/*", async (route) => {
iconRequests.push(route.request().url());
const name = new URL(route.request().url()).pathname.split("/").at(-1);
const response = await route.fetch({ url: new URL(`/linuxcnc/share/axis/images/${name}`, route.request().url()).href });
await route.fulfill({ response });
});
await page.route("**/configured-machine.ini", async (route) => {
const response = await route.fetch({ url: new URL(`/${sourceIni}`, route.request().url()).href });
const content = (await response.text()).replace(/MAX_FEED_OVERRIDE\s*=\s*[^\r\n]+/, "MAX_FEED_OVERRIDE = 1.3");
await route.fulfill({ response, body: content });
});
await page.route("**/configured-preview.ngc", (route) => route.fulfill({ contentType: "text/plain", body: "G21 G90\nG0 X-30 Y-30 Z20\nG1 X30 Y-30 F600\nY30\nX-30\nY-30\nM2\n" }));
try {
await page.goto(process.env.XYZAC_PANEL_URL ?? "http://127.0.0.1:5174/");
await page.waitForFunction(() => document.querySelector("main")?.dataset.axisProgramLoading === "false");
assert.equal(await page.locator(".xyzac-machine-view").getAttribute("data-xyzac-model-id"), "xyzbc-trt");
assert.equal(await page.locator("main").getAttribute("data-axis-program-path"), catalog.machines[1].program);
assert.deepEqual(await page.locator('[data-axis-control="manual-jog-increment"] option').allTextContents(), ["Continuous", "0.25", "2.5"]);
assert.equal(await page.locator('[data-axis-control="override-feed"]').getAttribute("max"), "130");
assert.ok(configRequests.length >= 2); assert.ok(iconRequests.length > 10);
const colors = await page.locator('.xyzac-machine-view canvas[data-engine]').evaluate(async (canvas) => {
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
const gl = canvas.getContext("webgl2"); const data = new Uint8Array(canvas.width * canvas.height * 4);
gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, data);
let purple = 0, green = 0;
for (let i = 0; i < data.length; i += 4) {
if (data[i] > 30 && data[i + 2] > 30 && data[i + 1] < 10) purple++;
if (data[i + 1] > 150 && data[i + 1] > data[i] + 30 && data[i + 1] > data[i + 2] + 30) green++;
}
return { purple, green };
});
assert.ok(colors.purple > 1000, JSON.stringify(colors));
await page.screenshot({ path: new URL("coverage/configuration/configured-ui.png", root).pathname, fullPage: true });
assert.ok(colors.green > 0, JSON.stringify(colors));
assert.deepEqual(errors, []);
const output = new URL("coverage/configuration/", root); await mkdir(output, { recursive: true });
await page.screenshot({ path: new URL("configured-ui.png", output).pathname, fullPage: true });
const report = { report_id: "configurable-ui-browser-v1", status: "verified", default_machine: catalog.default_machine, entrypoint: catalog.machines[1].entrypoint, program: catalog.machines[1].program, increments: settings.ui.jog_increments, feed_max: 1.3, colors, configured_icons: iconRequests.length, browser_errors: errors };
await writeFile(new URL("browser.report.json", output), JSON.stringify(report, null, 2) + "\n");
console.log(JSON.stringify(report));
} finally { await browser.close(); }