diff options
Diffstat (limited to 'src/api')
| -rw-r--r-- | src/api/dashboard/dashboard.html | 54 |
1 files changed, 53 insertions, 1 deletions
diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html index 4232cbd..0ae99f4 100644 --- a/src/api/dashboard/dashboard.html +++ b/src/api/dashboard/dashboard.html @@ -79,6 +79,10 @@ margin-left: 1em; cursor: pointer; } section h2 label.choice input { vertical-align: -2px; margin: 0 3px 0 0; } section.collapsed h2 label.choice { display: none; } + /* The console's rates, after its filter. */ + section h2 .rates { font-size: 12px; text-transform: none; letter-spacing: 0; + margin-left: 1em; font-variant-numeric: tabular-nums; } + section.collapsed h2 .rates { display: none; } /* The right column dragged off to the side: the map takes the width. */ @media (min-width: 901px) { main.right-hidden > section:not(#map) { display: none; } @@ -258,7 +262,7 @@ <div id="column-hint" class="note">Drag the space left of these boxes to the right to hide the column.</div> <section id="console-box"> - <h2>Console <label class="choice" title="Only the messages with the node or over the link in focus (clicked on the map or in the peers); all while nothing is"><input type="checkbox" id="console-filter">filter selected</label></h2> + <h2>Console <label class="choice" title="Only the messages with the node or over the link in focus (clicked on the map or in the peers); all while nothing is"><input type="checkbox" id="console-filter">filter selected</label><span id="console-rates" class="rates" title="Messages per second sent and received, as load averages are: over the last 1, 5 and 15 minutes (exponentially weighted), since the page was opened; with the filter, those with the node or over the link in focus">msg/s –</span></h2> <div class="scroll" id="console-scroll"><div id="console" class="console"></div> <div id="console-none" class="note" hidden>No information available</div></div> </section> @@ -1800,10 +1804,57 @@ function trafficArrived() { } } } + measureRates(current); lastTraffic = current; if (flows.length > 0 && flowFrame === null) flowFrame = requestAnimationFrame(drawFlows); } +// --- The console's rates: messages per second sent and received, per peer and in all, +// averaged as load averages are, over 1, 5 and 15 minutes (each sample weighs in by +// how long ago it was, `e^(-age/window)`), from the counts each update brings. The +// first sample is taken as it is, so they needn't climb from 0 first. --- +const RATE_WINDOWS_S = [60, 300, 900]; +// By peer, and in all (`null`): the averages; and when the counts were last taken. +const rates = new Map(); +let ratesAt = null; +function measureRates(current) { + const now = performance.now(); + if (lastTraffic !== null && ratesAt !== null && now > ratesAt) { + const secs = (now - ratesAt) / 1000; + const total = (counts, way) => Object.values(counts[way]) + .reduce((sum, count) => sum + count.messages, 0); + let all = 0; + const sample = (key, rate) => { + const known = rates.get(key); + rates.set(key, known ? known.map((average, i) => + rate + (average - rate) * Math.exp(-secs / RATE_WINDOWS_S[i])) : RATE_WINDOWS_S.map(() => rate)); + }; + for (const [id, counts] of current) { + const before = lastTraffic.get(id); + if (!before) continue; + const grown = Math.max(0, total(counts, "sent") - total(before, "sent")) + + Math.max(0, total(counts, "received") - total(before, "received")); + sample(id, grown / secs); + all += grown; + } + sample(null, all / secs); + // Peers gone from the list: their averages too. + for (const key of [...rates.keys()]) if (key !== null && !current.has(key)) rates.delete(key); + } + ratesAt = now; + showRates(); +} +// The averages shown: in all, or with the filter, of the peer in focus; none (–) for a +// link between other nodes, a node that is no peer, or before two updates came. +function showRates() { + const filtering = consoleFiltered && focused !== null; + const peer = filtering ? peerOf(focused) : null; + const shown = filtering && peer === null ? undefined : rates.get(peer); + const format = rate => rate < 100 ? rate.toFixed(2) : String(Math.round(rate)); + text(document.getElementById("console-rates"), + "msg/s " + (shown ? shown.map(format).join(" ") : "–")); +} + // --- Console: the messages this node exchanged with its peers, one by one, as the // node logs them (`messages` events, `traffic::MessageLog`), the latest at the bottom, // `CONSOLE_LINES` at most. Scrolled along while at the bottom. A click on one shows it @@ -1880,6 +1931,7 @@ consoleFilter.addEventListener("change", () => { // follows from there. let consoleView = null; function filterConsole() { + showRates(); const log = document.getElementById("console"); const filtering = consoleFiltered && focused !== null; log.classList.toggle("filtering", filtering); |
