116 lines
3.1 KiB
JavaScript
116 lines
3.1 KiB
JavaScript
function qs(name) {
|
|
const url = new URL(window.location.href);
|
|
return url.searchParams.get(name);
|
|
}
|
|
|
|
async function fetchJson(url) {
|
|
const res = await fetch(url, { cache: "no-store" });
|
|
const text = await res.text();
|
|
let data;
|
|
try {
|
|
data = JSON.parse(text);
|
|
} catch {
|
|
throw new Error(`Bad JSON from ${url}: ${text.slice(0, 200)}`);
|
|
}
|
|
if (!res.ok) {
|
|
const msg = data && data.error ? data.error : `HTTP ${res.status}`;
|
|
throw new Error(msg);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
function fmt(n, digits = 1) {
|
|
if (typeof n !== "number" || !isFinite(n)) return "-";
|
|
return n.toFixed(digits);
|
|
}
|
|
|
|
async function updateIndex() {
|
|
const body = document.getElementById("graphs-body");
|
|
if (!body) return;
|
|
|
|
let graphs = [];
|
|
try {
|
|
graphs = await fetchJson("/api/graphs");
|
|
} catch (e) {
|
|
body.innerHTML = `<tr><td colspan="3">${e.message}</td></tr>`;
|
|
return;
|
|
}
|
|
|
|
body.innerHTML = "";
|
|
for (const g of graphs) {
|
|
const tr = document.createElement("tr");
|
|
const statusCls = g.running ? "status-running" : "status-stopped";
|
|
tr.innerHTML = `
|
|
<td><a href="/graph.html?name=${encodeURIComponent(g.name)}">${g.name}</a></td>
|
|
<td class="${statusCls}">${g.running ? "Running" : "Stopped"}</td>
|
|
<td>${fmt(g.total_fps)}</td>
|
|
`;
|
|
body.appendChild(tr);
|
|
}
|
|
}
|
|
|
|
async function updateGraph() {
|
|
const name = qs("name");
|
|
const nodesBody = document.getElementById("nodes-body");
|
|
const edgesBody = document.getElementById("edges-body");
|
|
if (!name || !nodesBody || !edgesBody) return;
|
|
|
|
const title = document.getElementById("graph-title");
|
|
if (title) title.textContent = `Graph: ${name}`;
|
|
|
|
let g;
|
|
try {
|
|
g = await fetchJson(`/api/graphs/${encodeURIComponent(name)}`);
|
|
} catch (e) {
|
|
nodesBody.innerHTML = `<tr><td colspan="9">${e.message}</td></tr>`;
|
|
edgesBody.innerHTML = `<tr><td colspan="7">${e.message}</td></tr>`;
|
|
return;
|
|
}
|
|
|
|
const meta = document.getElementById("graph-meta");
|
|
if (meta) {
|
|
meta.textContent = `running=${g.running} total_fps=${fmt(g.total_fps)} ts_ms=${g.timestamp_ms}`;
|
|
}
|
|
|
|
nodesBody.innerHTML = "";
|
|
for (const n of g.nodes) {
|
|
const tr = document.createElement("tr");
|
|
tr.innerHTML = `
|
|
<td>${n.id}</td>
|
|
<td>${n.type}</td>
|
|
<td>${n.role || ""}</td>
|
|
<td>${fmt(n.input_fps)}</td>
|
|
<td>${fmt(n.output_fps)}</td>
|
|
<td>${n.input_queue ? `${n.input_queue.size}/${n.input_queue.capacity}` : "-"}</td>
|
|
<td>${n.drop_total}</td>
|
|
<td>${n.error_total}</td>
|
|
<td>${fmt(n.avg_process_time_ms, 3)}</td>
|
|
`;
|
|
nodesBody.appendChild(tr);
|
|
}
|
|
|
|
edgesBody.innerHTML = "";
|
|
for (const e of g.edges) {
|
|
const tr = document.createElement("tr");
|
|
tr.innerHTML = `
|
|
<td>${e.from}</td>
|
|
<td>${e.to}</td>
|
|
<td>${e.queue.size}</td>
|
|
<td>${e.queue.capacity}</td>
|
|
<td>${e.queue.dropped_total}</td>
|
|
<td>${fmt(e.queue.pushed_fps)}</td>
|
|
<td>${fmt(e.queue.popped_fps)}</td>
|
|
`;
|
|
edgesBody.appendChild(tr);
|
|
}
|
|
}
|
|
|
|
function boot() {
|
|
updateIndex();
|
|
updateGraph();
|
|
setInterval(updateIndex, 2000);
|
|
setInterval(updateGraph, 1000);
|
|
}
|
|
|
|
window.addEventListener("load", boot);
|