90 lines
6.5 KiB
JavaScript
90 lines
6.5 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { writeFile } from 'node:fs/promises';
|
|
import { pathToFileURL } from 'node:url';
|
|
const { chromium } = await import(pathToFileURL(process.env.PLAYWRIGHT_MODULE).href);
|
|
const output = new URL('../coverage/glb-machine/jog-limits/', import.meta.url);
|
|
const browser = await chromium.launch({ executablePath: '/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 } });
|
|
await page.addInitScript(() => {
|
|
window.jogErrors = [];
|
|
const OriginalWorker = window.Worker;
|
|
window.Worker = class extends OriginalWorker {
|
|
constructor(...args) {
|
|
super(...args);
|
|
this.addEventListener('message', ({ data }) => {
|
|
if (data.kind === 'ERROR') window.jogErrors.push({ seq: data.seq, ...data.payload });
|
|
window.jogLastResponse = data;
|
|
});
|
|
}
|
|
};
|
|
});
|
|
const errors = []; page.on('pageerror', (e) => errors.push(e.message));
|
|
const control = (id) => page.locator(`[data-axis-control="${id}"]`);
|
|
const sample = () => page.locator('.xyzac-machine-view').evaluate((e) => ({ joints: JSON.parse(e.dataset.xyzacJoints), tip: JSON.parse(e.dataset.xyzacToolTip), tick: Number(e.dataset.xyzacTick) }));
|
|
const queueEmpty = () => page.waitForFunction(() => window.jogLastResponse?.payload?.wasm_motion?.state?.queue_depth === 0 || window.jogLastResponse?.payload?.panel_snapshot?.wasm_motion?.state?.queue_depth === 0);
|
|
async function seed(block, angle) {
|
|
await control('tab-mdi').click(); await control('mdi-command').fill(block); await control('mdi-submit').click();
|
|
await page.waitForFunction((angle) => Math.abs(JSON.parse(document.querySelector('.xyzac-machine-view').dataset.xyzacJoints)[3] - angle) < 1e-7, angle);
|
|
await queueEmpty(); await control('tab-manual').click();
|
|
}
|
|
async function hold(axis, direction, until) {
|
|
await control(`manual-axis-${axis}`).check();
|
|
const box = await control(direction > 0 ? 'manual-jog-positive' : 'manual-jog-negative').boundingBox();
|
|
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); await page.mouse.down();
|
|
try { await until(); }
|
|
finally { await page.mouse.up(); }
|
|
await page.waitForFunction(() => document.querySelector('.command-status')?.textContent?.includes('JOG_STOP acknowledged'));
|
|
}
|
|
const report = { report_id: 'jog-limit-browser-v1', status: 'running', machines: [] };
|
|
try {
|
|
await page.goto('http://127.0.0.1:5174/');
|
|
await page.waitForFunction(() => document.querySelector('main')?.dataset.axisProgramLoading === 'false');
|
|
for (const id of ['ac', 'bc']) {
|
|
await control('machine-glb-settings').click(); await control(`machine-glb-${id}`).click();
|
|
await page.waitForFunction(() => document.querySelector('main')?.dataset.axisProgramLoading === 'false'); await page.keyboard.press('Escape');
|
|
await control('toolbar-power').click(); await control('manual-home-all').click();
|
|
await page.waitForFunction(() => !document.querySelector('[data-axis-control="switchkins-1"]').disabled);
|
|
const axis = id[0];
|
|
await seed(`G21 G90 G0 X0 Y0 Z30 ${axis.toUpperCase()}49 C20`, 49);
|
|
await hold(axis, 1, async () => {
|
|
await page.waitForFunction(() => Math.abs(JSON.parse(document.querySelector('.xyzac-machine-view').dataset.xyzacJoints)[3] - 50) < 1e-7);
|
|
await queueEmpty();
|
|
});
|
|
const atLimit = await sample(); assert.ok(atLimit.joints[3] <= 50 + 1e-8);
|
|
await hold(axis, -1, () => page.waitForFunction(() => JSON.parse(document.querySelector('.xyzac-machine-view').dataset.xyzacJoints)[3] < 49));
|
|
const retreat = await sample(); assert.ok(retreat.joints[3] < atLimit.joints[3]);
|
|
await seed(`G90 G0 X190 Y0 Z30 ${axis.toUpperCase()}0 C0`, 0);
|
|
await control('switchkins-1').click();
|
|
const count = await page.evaluate(() => window.jogErrors.length);
|
|
await hold('c', 1, () => page.waitForFunction((count) => window.jogErrors.length > count, count));
|
|
const fault = await page.evaluate((count) => window.jogErrors[count], count);
|
|
assert.equal(fault.code, 'motion_limit'); assert.equal(fault.reason, 'motion_joint_limit');
|
|
await queueEmpty(); const stopped = await sample();
|
|
assert.ok(stopped.joints[1] <= 100 + 1e-7);
|
|
await page.waitForTimeout(150); assert.deepEqual((await sample()).joints, stopped.joints);
|
|
await hold('c', -1, () => page.waitForFunction((c) => JSON.parse(document.querySelector('.xyzac-machine-view').dataset.xyzacJoints)[4] < c - 1, stopped.joints[4]));
|
|
const resumed = await sample(); assert.ok(resumed.joints[4] < stopped.joints[4]);
|
|
for (let i = 0; i < 4; i++) {
|
|
const before = await sample(), dir = i % 2 ? -1 : 1;
|
|
await hold('c', dir, () => page.waitForFunction(({ c, dir }) => (JSON.parse(document.querySelector('.xyzac-machine-view').dataset.xyzacJoints)[4] - c) * dir > .2, { c: before.joints[4], dir }));
|
|
}
|
|
assert.doesNotMatch(await page.locator('.axis-window-header').innerText(), /ERROR/);
|
|
const pixels = await page.locator('.xyzac-machine-view canvas').evaluate((canvas) => {
|
|
const gl = canvas.getContext('webgl2'), bytes = new Uint8Array(canvas.width * canvas.height * 4), colors = new Set();
|
|
gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, bytes);
|
|
for (let i = 0; i < bytes.length; i += 32) colors.add(`${bytes[i]},${bytes[i + 1]},${bytes[i + 2]}`);
|
|
return colors.size;
|
|
});
|
|
assert.ok(pixels > 100); assert.notDeepEqual(resumed.tip, stopped.tip);
|
|
await page.screenshot({ path: new URL(`${id}-recovered.png`, output).pathname });
|
|
await control('toolbar-power').click();
|
|
await page.waitForFunction(() => !document.querySelector('[data-axis-control="machine-glb-import"]').disabled);
|
|
report.machines.push({ id, atLimit, retreat, stopped, resumed, fault: { code: fault.code, reason: fault.reason }, pixels });
|
|
}
|
|
const faults = await page.evaluate(() => window.jogErrors.map((e) => ({ code: e.code, reason: e.reason })));
|
|
assert.ok(faults.every((e) => e.code === 'motion_limit'), JSON.stringify(faults));
|
|
assert.deepEqual(errors, []); report.status = 'verified';
|
|
} catch (e) { report.status = 'failed'; report.error = String(e.stack ?? e); await page.screenshot({ path: new URL('failure.png', output).pathname }); throw e; }
|
|
finally { report.page_errors = errors; await writeFile(new URL('browser.report.json', output), JSON.stringify(report, null, 2) + '\n'); await browser.close(); }
|
|
console.log(JSON.stringify({ report_id: report.report_id, status: report.status, machines: report.machines.map((m) => m.id) }));
|