97 lines
5.2 KiB
JavaScript
97 lines
5.2 KiB
JavaScript
/**
|
||
* 浏览器实测(第二轮):切链路后**实际节拍**是否真的跟着变。
|
||
* 前一轮只验了输入框/state;这里数真实的 fetchMotorState 调用频率 ——
|
||
* 若 applyTransportRefresh 忘了重排定时器,state 是新值但节拍还是旧的。
|
||
*/
|
||
const CDP_BASE = "http://127.0.0.1:9333";
|
||
const list = await (await fetch(CDP_BASE + "/json/list")).json();
|
||
const page = list.find((t) => t.type === "page");
|
||
const sock = new WebSocket(page.webSocketDebuggerUrl);
|
||
let id = 0; const pending = new Map();
|
||
await new Promise((r, j) => { sock.onopen = r; sock.onerror = j; });
|
||
sock.onmessage = (ev) => { const m = JSON.parse(ev.data); if (m.id && pending.has(m.id)) { pending.get(m.id)(m); pending.delete(m.id); } };
|
||
const send = (method, params = {}) => { const myId = ++id; return new Promise((res) => { pending.set(myId, res); sock.send(JSON.stringify({ id: myId, method, params })); }); };
|
||
async function ev(expr) {
|
||
const r = await send("Runtime.evaluate", { expression: expr, awaitPromise: true, returnByValue: true });
|
||
return r.result && r.result.result && r.result.result.value;
|
||
}
|
||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||
let pass = 0, fail = 0; const out = [];
|
||
const check = (n, c, d) => { if (c) { pass++; out.push(" \u2713 " + n); } else { fail++; out.push(" \u2717 " + n + (d ? " \u2014 " + d : "")); } };
|
||
|
||
await send("Runtime.enable"); await send("Page.enable");
|
||
/* 页面服务端口可传参:node scripts/xxx.mjs [port](默认 2625) */
|
||
const PORT = Number(process.argv[2] || 2625);
|
||
const PAGE = `http://127.0.0.1:${PORT}/pages/motor.html`;
|
||
|
||
/* 干净起点,直接进 ECAT 链路(本机有 ecat 服务,能真连上跑起轮询) */
|
||
await send("Page.navigate", { url: PAGE }); await sleep(2000);
|
||
await ev(`localStorage.setItem("dcu_motor_transport","ecat"); localStorage.removeItem("dcu_motor_ui_v2"); "ok"`);
|
||
await send("Page.navigate", { url: PAGE }); await sleep(3000);
|
||
|
||
/* 装一个计数探针:统计 /ecat/api/motor/state 的实际请求次数 */
|
||
await ev(`(() => {
|
||
window.__cnt = 0;
|
||
if (!window.__origFetch) window.__origFetch = window.fetch;
|
||
window.fetch = function (u, i) {
|
||
if (String(u).includes("motor/state")) window.__cnt++;
|
||
return window.__origFetch.apply(this, arguments);
|
||
};
|
||
return "ok";
|
||
})()`);
|
||
|
||
const state0 = await ev(`JSON.stringify({tp: motorDbg.state.tpId, conn: motorDbg.state.connected, eff: motorDbg.state.refreshIntervalMs})`);
|
||
out.push("\n[前置] 当前链路状态: " + state0);
|
||
const st0 = JSON.parse(state0);
|
||
check("已在 ecat 链路", st0.tp === "ecat", state0);
|
||
check("已连接(否则数不到轮询)", st0.conn === true, state0);
|
||
check("生效间隔 50ms", st0.eff === 50, state0);
|
||
|
||
async function measure(seconds) {
|
||
await ev(`window.__cnt = 0; "ok"`);
|
||
await sleep(seconds * 1000);
|
||
return await ev(`window.__cnt`);
|
||
}
|
||
|
||
out.push("\n[A] ECAT 默认 50ms 的实际节拍");
|
||
let n = await measure(3);
|
||
let hz = n / 3;
|
||
out.push(` 3 秒内 ${n} 次 ≈ ${hz.toFixed(1)} Hz(理论 20 Hz)`);
|
||
check("接近 20Hz(>12Hz,留足浏览器调度余量)", hz > 12, `实测 ${hz.toFixed(1)}Hz`);
|
||
|
||
out.push("\n[B] 改成 500ms → 节拍应明显变慢");
|
||
await ev(`(() => { const i=document.querySelector("#refreshRate"); i.value="500"; i.dispatchEvent(new Event("change")); return "ok"; })()`);
|
||
await sleep(800);
|
||
n = await measure(4);
|
||
hz = n / 4;
|
||
out.push(` 4 秒内 ${n} 次 ≈ ${hz.toFixed(1)} Hz(理论 2 Hz)`);
|
||
check("落到 2Hz 附近(0.8~4Hz)", hz > 0.8 && hz < 4, `实测 ${hz.toFixed(1)}Hz`);
|
||
|
||
out.push("\n[C] 切到 WiFi 再切回 ECAT:节拍应自动回到 ecat 那一份(500ms)");
|
||
await ev(`(() => { const s=document.querySelector("#transportSelect"); s.value="wifi"; s.dispatchEvent(new Event("change")); return "ok"; })()`);
|
||
await sleep(1500);
|
||
const wifiState = await ev(`JSON.stringify({input: document.querySelector("#refreshRate").value, eff: motorDbg.state.refreshIntervalMs})`);
|
||
check("切 WiFi 后生效值变 250(不是带着 ECAT 的 500)", JSON.parse(wifiState).eff === 250, wifiState);
|
||
await ev(`(() => { const s=document.querySelector("#transportSelect"); s.value="ecat"; s.dispatchEvent(new Event("change")); return "ok"; })()`);
|
||
await sleep(2500);
|
||
const backState = await ev(`JSON.stringify({input: document.querySelector("#refreshRate").value, eff: motorDbg.state.refreshIntervalMs, conn: motorDbg.state.connected})`);
|
||
check("切回 ECAT 生效值回到 500(自己那一份)", JSON.parse(backState).eff === 500, backState);
|
||
n = await measure(4);
|
||
hz = n / 4;
|
||
out.push(` 4 秒内 ${n} 次 ≈ ${hz.toFixed(1)} Hz(理论 2 Hz)`);
|
||
check("实际节拍也回到 2Hz 附近(不是 20Hz 的旧节拍)", hz > 0.8 && hz < 4, `实测 ${hz.toFixed(1)}Hz`);
|
||
|
||
out.push("\n[D] 改回 50ms,节拍应立刻提上去(验证重排真的生效)");
|
||
await ev(`(() => { const i=document.querySelector("#refreshRate"); i.value="50"; i.dispatchEvent(new Event("change")); return "ok"; })()`);
|
||
await sleep(600);
|
||
n = await measure(3);
|
||
hz = n / 3;
|
||
out.push(` 3 秒内 ${n} 次 ≈ ${hz.toFixed(1)} Hz(理论 20 Hz)`);
|
||
check("回到 20Hz 附近(>12Hz)", hz > 12, `实测 ${hz.toFixed(1)}Hz`);
|
||
|
||
console.log(out.join("\n"));
|
||
console.log("\n" + "=".repeat(56));
|
||
console.log(`通过 ${pass} 项,失败 ${fail} 项`);
|
||
sock.close();
|
||
process.exit(fail === 0 ? 0 : 1);
|