diff options
| author | tv <tv@krebsco.de> | 2026-10-31 23:11:16 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-31 23:11:16 +0100 |
| commit | 6fc27cae27814467d6c2b82499a6e5e18caf434d (patch) | |
| tree | 73137d86361509f15ce6682b20497e50e3969887 /src/api | |
| parent | 1ee865d3f53f7097656e0c5b571ce7f53fe9fa12 (diff) | |
dashboard: message dot size by bytes
Diffstat (limited to 'src/api')
| -rw-r--r-- | src/api/dashboard/dashboard.html | 53 |
1 files changed, 41 insertions, 12 deletions
diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html index 6effc59..bb49dd8 100644 --- a/src/api/dashboard/dashboard.html +++ b/src/api/dashboard/dashboard.html @@ -1480,10 +1480,28 @@ flowsToggle.addEventListener("click", () => { let lastTraffic = null; const flows = []; let flowFrame = null; -// Messages by kind, of a peer's `traffic.sent` or `.received`. +// Messages and bytes by kind, of a peer's `traffic.sent` or `.received`. function messages(byKind) { return Object.fromEntries(Object.entries(byKind || {}) - .map(([kind, counter]) => [kind, counter.messages])); + .map(([kind, counter]) => [kind, { messages: counter.messages, bytes: counter.bytes }])); +} +// A dot's radius for a message of `bytes`: grows with the size's logarithm, from 1.5 +// (a ping) to 8. +const dotRadius = bytes => Math.min(8, Math.max(1.5, 1.5 + 1.1 * Math.log10(1 + bytes / 32))); +// The dots for messages that grew by `kinds` (kind: messages, bytes): `FLOWS_AT_MOST` +// at most, shared out by kind (one each at least, the busiest first), each as large as +// its kind's messages on average; interleaved. +function dotsFor(kinds, total) { + const byKind = Object.entries(kinds).sort(([, a], [, b]) => b.messages - a.messages) + .map(([kind, grown]) => ({ kind, bytes: grown.bytes / grown.messages, + count: Math.min(grown.messages, Math.max(1, Math.round(FLOWS_AT_MOST * grown.messages / total))) })); + const dots = []; + for (let round = 0; dots.length < FLOWS_AT_MOST && byKind.some(k => k.count > round); round++) { + for (const kind of byKind) { + if (kind.count > round && dots.length < FLOWS_AT_MOST) dots.push(kind); + } + } + return dots; } // The messages exchanged with each peer since the last update: on the map (`flows`, // if shown) and in the console (`logTraffic`). @@ -1507,16 +1525,21 @@ function trafficArrived() { const kinds = {}; let total = 0; for (const [kind, count] of Object.entries(counts[way])) { - const grown = Math.max(0, count - (before[way][kind] || 0)); - if (grown > 0) { kinds[kind] = grown; total += grown; } + const was = before[way][kind] || { messages: 0, bytes: 0 }; + const grown = Math.max(0, count.messages - was.messages); + if (grown > 0) { + kinds[kind] = { messages: grown, bytes: Math.max(0, count.bytes - was.bytes) }; + total += grown; + } } if (total === 0) continue; logTraffic(way, id, total, kinds); if (!dots) continue; - const count = Math.min(FLOWS_AT_MOST, total); - for (let i = 0; i < count; i++) { - flows.push({ from, to, start: now + i * FLOW_SPREAD_MS / count }); - } + const shown = dotsFor(kinds, total); + shown.forEach((dot, i) => { + flows.push({ from, to, start: now + i * FLOW_SPREAD_MS / shown.length, + kind: dot.kind, r: dotRadius(dot.bytes) }); + }); } } } @@ -1528,15 +1551,20 @@ function trafficArrived() { // every update (the same counts as the map's messages), the latest at the bottom, // `CONSOLE_LINES` at most. Scrolled along while at the bottom. --- const CONSOLE_LINES = 200; +const size = bytes => bytes < 1024 ? bytes + " B" : bytes < 1048576 + ? (bytes / 1024).toFixed(1) + " KiB" : (bytes / 1048576).toFixed(1) + " MiB"; // The lines, kept while the box is collapsed and drawn when it's opened (`showConsole`). const consoleLines = []; function consoleLine([when, text]) { return el("div", {}, el("span", { class: "when" }, when + " "), text); } function logTraffic(way, peer, total, kinds) { - const parts = Object.entries(kinds).map(([kind, count]) => kind + " " + count).join(", "); + const parts = Object.entries(kinds).map(([kind, grown]) => kind + " " + grown.messages) + .join(", "); + const bytes = Object.values(kinds).reduce((sum, grown) => sum + grown.bytes, 0); const line = [new Date().toLocaleTimeString(), - (way === "sent" ? "→ " : "← ") + name(peer) + ": " + total + " (" + parts + ")"]; + (way === "sent" ? "→ " : "← ") + name(peer) + ": " + total + ", " + size(bytes) + + " (" + parts + ")"]; consoleLines.push(line); if (consoleLines.length > CONSOLE_LINES) consoleLines.shift(); if (hidden(document.getElementById("console-box"))) return; @@ -1564,7 +1592,8 @@ function drawFlows() { const p = positions.get(flow.from), q = positions.get(flow.to); if (now < flow.start || !p || !q) continue; const t = (now - flow.start) / FLOW_MS; - dots.push({ x: p.x + (q.x - p.x) * t, y: p.y + (q.y - p.y) * t, opacity: Math.sin(Math.PI * t) }); + dots.push({ x: p.x + (q.x - p.x) * t, y: p.y + (q.y - p.y) * t, r: flow.r, + opacity: Math.sin(Math.PI * t) }); } while (layer.children.length < dots.length) layer.append(svg("circle")); while (layer.children.length > dots.length) layer.lastChild.remove(); @@ -1572,7 +1601,7 @@ function drawFlows() { const circle = layer.children[i]; circle.setAttribute("cx", dot.x); circle.setAttribute("cy", dot.y); - circle.setAttribute("r", 3); + circle.setAttribute("r", dot.r.toFixed(1)); circle.setAttribute("fill", "var(--self)"); circle.setAttribute("opacity", dot.opacity.toFixed(2)); }); |
