Files

97 lines
5.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 浏览器实测(第二轮):切链路后**实际节拍**是否真的跟着变。
* 前一轮只验了输入框/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);