mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-08-16 23:50:05 +00:00
7ea34a2672e358b2d9d63a037814ec0dfefeb190
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
196f42789c |
refactor(web-viewer): fill card height, trim hop axis, drop live feed
Three layout and cost changes. Hops away and Roles now grow into their cards instead of leaving dead space under a fixed-height canvas. Chart.js needs a positioned parent with a real height, so the body becomes a flex column and the chart takes the slack via flex-basis 0 — height: 100% would resolve against an auto-height parent and collapse. The hop axis now ends at the last hop that carries an observation rather than at the last non-empty bucket of the padded union. On a quiet window that collapses 64 buckets to 13; on a full one it changes nothing, because the flood series really does have packets at every hop out to 63 — 14 of them at hop 63, and 14.1% of all flood traffic beyond hop 20. That tail is real data, so it is drawn rather than truncated. Dropped the Live Activity card. It opened three SocketIO subscriptions and re-rendered on every packet to duplicate /realtime, which is a page that already does it better. The dashboard now costs one snapshot read per poll and holds no streaming subscriptions. A test asserts it stays that way rather than merely that the markup is gone. |
||
|
|
f68da5287c |
fix(web-viewer): derive neighbours and hop distance from path evidence
307 direct neighbours was not plausible, and it was not real. complete_contact_tracking.hop_count claims 800 zero-hop contacts. Only 68 of them have any one-hop path in observed_paths to corroborate that. Their stored SNR piles up in a 1.5 dB band — 655 of 800 between 11.25 and 12.75 dB — and their RSSI clusters at -39..-48 dBm. Hundreds of radios at different distances and terrain cannot land in a 10 dB window. That is the signature of one strong local link being recorded against every node whose traffic happened to arrive through it. Their return paths agree: these "direct" contacts have out_path_len of 3 to 11. The writer's intent is sound — repeater_manager only stores RSSI/SNR when signal_info reports hops == 0 — so the field being fed to it does not mean what the surrounding code assumes. Left as is; this change stops the dashboard depending on it. Neighbour membership now comes from path evidence: an advert whose path_length equals its bytes_per_hop travelled exactly one hop. That yields 38 nodes in 24h and 124 in 7d, with a plausible spread. Signal is shown only where the path evidence and the stored hop count agree, which is 5 and 12 nodes respectively; the rest read "no signal reading" rather than borrowing a measurement taken on somebody else's link. A 24h/7d selector bounds the window, capped well under observed_paths' 90-day retention because a month-old link says nothing about today. Separately, this fixes a bug I introduced. path_length is a BYTE count, and with 2- or 3-byte hop encoding a 3-hop path is 6 or 9 bytes long. The path-length histogram plotted that raw value on an axis readers would take as hops, overstating distance two- to threefold on a mesh that is ~95% multibyte. It is replaced by a single hops-away chart computed as path_length / bytes_per_hop, which also retires the histogram built on the untrustworthy stored hop count. The result is unimodal, peaking at 3 hops and decaying — the shape a mesh should have, and not the bimodal one the old chart drew. |
||
|
|
c94a3f861e |
refactor(web-viewer): rework dashboard signal, role and contact tiles
Three adjustments from review. Role and device type are the same field twice. Measured on the live database they disagree on 16 of 11,028 contacts (ten roomservers and a handful of bots and gateways reporting device Companion); every other row is repeater/Repeater, companion/Companion, type11/Type11 and so on. Charting both filled half a card with a copy of the other half. Keep the role mix, which also carries the type0..type15 bucketing, and move it into the routing row where the old signal card was. Drop the tracked-contacts tile. is_currently_tracked does not describe anything a reader can act on, and node activity is already covered by nodes-heard and gone-quiet. The known-contacts total moves onto the coverage tile, which leaves five tiles splitting the row evenly. Rebuild the signal panel around zero-hop neighbours. Percentiles over every received message answered no question anyone has: SNR on a relayed packet measures the last hop into this radio, not the link to the node that sent it, so averaging across hop counts describes nothing in particular. The panel now shows the nodes heard with no repeater in between — how many, their SNR distribution, and the weakest links named, worst first, on a fixed -12..+14 dB scale so bars mean the same thing between refreshes. On the live database that is 307 neighbours, median 12.0 dB, with eight marginal links surfaced from -9.0 dB down. This also corrects the source. The plan rejected complete_contact_tracking.snr as a badly biased 7% sample; in fact it is populated on exactly the 800 hop_count=0 contacts and NULL on all 10,228 others. It is not a sample of the network, it is a complete census of the neighbours — which is precisely the population the metric applies to. message_stats.hops=0 covers only 34 senders by comparison. |
||
|
|
7e3ab04b53 |
feat(web-viewer): rebuild dashboard on a background snapshot
The landing page re-ran ~50 aggregate queries five times per load, then repeated the whole sequence every 30 seconds forever — including in backgrounded tabs. Against the live 1.44 GB database that was roughly 20 seconds of SQLite work per page load. Move the work off the request path. A refresher thread in the viewer process (which already runs migrations, so it works for a split-DB install) writes two tables: daily_rollup, one row per local date, and dashboard_snapshot, a single JSON row. A page load now reads one row. Measured on the live database: first paint 6 requests -> 2, /api/dashboard/summary p50 1.1 ms (304 in 0.8 ms), /api/stats 130 ms, and a 0.32 s refresh once a minute in the background. Make the numbers mean what they say: - Window selectors are built from each source's retention. The page offered "30d" and "All" against tables pruned at 7 days, so three of four choices returned the same figure under a label that denied it. - The incoming-packet chart reports its measured window instead of claiming 7 days for a table pruned at 3 — it sat beside a genuine 7-day contacts chart inviting an invalid comparison. - Days with no source data store NULL and render as gaps. Writing 0 would put a fake cliff at every retention boundary. - Signal metrics are stored as sums and counts, never means, so any window re-aggregates correctly. - Delta chips compare the last two complete calendar days and say so; the headline above them is a rolling 24 hours. - Unmapped role ordinals (type0..type15) bucket into "Unknown". - SNR comes from message_stats, where it is populated on every row, not from complete_contact_tracking, where it is populated on 7%. Kill the json_extract scans: packet_stream gains denormalized route_type_name, payload_type_name, path_len and bytes_per_hop, written at capture time. Aggregating those from JSON cost 3-6 s per query. Existing rows convert a bounded batch per tick rather than in one migration that would rewrite ~180 MB into the WAL and stall bot startup. A partial index serves as the backfill worklist — without it the "any rows left?" probe is a full scan costing 4.6 s per tick, and it costs that after the backfill finishes, because finding nothing still means reading everything. Also: replace the per-contact hop-prefix scan with the existing bucketed matcher and memoize the 7-day chunk set (264 ms -> 35 ms on a synthetic 100k-row database, regression-locked by a test); move the dashboard's JS and CSS to static files, which removes the CSP nonce requirement for the bulk of the page; and give cleanup_old_stats a future-timestamp guard, without which rows dated 2103 are never older than the cutoff and so live forever. Deletes the orphaned /stats page, unreachable from the nav and rendering stub charts that never populated. /api/stats stays as a shim with every key name intact plus Deprecation and Sunset headers. All schema changes are additive, so a downgraded codebase can read the data; it would however need the new schema_version rows removed, since MigrationRunner rejects versions it does not know. |