aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-31 23:11:16 +0100
committertv <tv@krebsco.de>2026-10-31 23:11:16 +0100
commit69a10ae3598eceb61cf5865c42d4de25d9175552 (patch)
tree66151b0d75f3757dab8307d8cefa4302e2696d4b /src
parentafa42718cbf82b8b56f5962c7bd1318282acbea6 (diff)
dashboard: message dot size by bytes
Diffstat (limited to 'src')
-rw-r--r--src/api/dashboard/dashboard.html53
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));
});