// Runs one configuration headlessly and prints the status every half second. // node --experimental-strip-types scripts/probe.ts "duration=1&E=1.6e11&..." [--seed S --index I (parameters of a dataset clip, plus overrides after &)] [--wav out.wav] import { chromium } from "playwright"; import { createServer } from "node:http"; import { readFileSync } from "node:fs"; import { join, extname, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { writeFileSync } from "node:fs"; import { drawSample, toQuery, wavBytes } from "./generate.ts"; const root = join(dirname(fileURLToPath(import.meta.url)), ".."); const argv = process.argv.slice(2); const arg = (n: string, d: string) => { const k = argv.indexOf(`--${n}`); return k >= 0 ? argv[k + 1] : d; }; let query = argv.find((a) => !a.startsWith("--") && !/^\d+$/.test(a)) ?? ""; if (argv.includes("--index")) { const s = drawSample(parseInt(arg("seed", "1")), parseInt(arg("index", "0"))); query = toQuery(s.params) + (query ? "&" + query : ""); } else query = "autostart=1&" + query; const MIME: Record = { ".html": "text/html", ".js": "text/javascript", ".css": "text/css" }; const server = createServer((req, res) => { const path = decodeURIComponent((req.url ?? "/").split("?")[0]); try { const f = join(root, "dist", path === "/" ? "index.html" : path); res.writeHead(200, { "content-type": MIME[extname(f)] ?? "application/octet-stream" }); res.end(readFileSync(f)); } catch { res.writeHead(404); res.end(); } }); await new Promise((r) => server.listen(0, "127.0.0.1", () => r())); const port = (server.address() as { port: number }).port; const browser = await chromium.launch({ headless: true, args: ["--enable-unsafe-webgpu", "--enable-features=Vulkan", "--use-angle=vulkan", "--ignore-gpu-blocklist", "--no-sandbox"] }); const page = await browser.newPage(); page.on("pageerror", (e) => console.log("pageerror", e.message)); page.on("console", (m) => { if (m.type() === "error") console.log("console.error", m.text()); }); console.log(query); await page.goto(`http://127.0.0.1:${port}/?${query}`); const t0 = Date.now(); for (;;) { await page.waitForTimeout(500); const st = await page.evaluate | null>("window.steelsim ? window.steelsim.status() : null"); const noGpu = await page.evaluate("window.steelsim ? window.steelsim.noGpu() : null"); if (noGpu) { console.log("no gpu:", noGpu); break; } if (!st) continue; const stick = st.stick as { x: number; v: number; force: number; maxForce: number } | null; console.log(`${((Date.now() - t0) / 1000).toFixed(1)}s phase ${st.phase} t ${(st.t as number).toFixed(3)} ke ${(st.ke as number).toExponential(2)} wmax ${((st.wmax as number) * 1e3).toFixed(2)} mm peak ${(st.peakPressure as number).toFixed(3)} Pa ${(st.stepsPerSec as number / 1e3).toFixed(0)} k/s ${st.nx}x${st.ny}` + (stick ? ` stick x ${(stick.x * 1e3).toFixed(1)} mm v ${stick.v.toFixed(2)} F ${stick.force.toFixed(1)} Fmax ${stick.maxForce.toFixed(1)}` : "")); if (st.phase === "done") break; } if (argv.includes("--wav")) { const audio = new Float32Array(await page.evaluate("window.steelsim.audio()")); let peak = 0; for (const v of audio) peak = Math.max(peak, Math.abs(v)); writeFileSync(arg("wav", "probe.wav"), wavBytes(audio, 48000, peak > 0 ? 0.99 / peak : 0)); console.log(`wrote ${arg("wav", "probe.wav")}, peak ${peak.toFixed(3)} Pa`); } await browser.close(); server.close();