Add AC/BC RTCP programs and 3D machine manager
This commit is contained in:
89
scripts/test_jog_limit_browser.mjs
Normal file
89
scripts/test_jog_limit_browser.mjs
Normal file
@@ -0,0 +1,89 @@
|
||||
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) }));
|
||||
Reference in New Issue
Block a user