diff --git a/meshview/templates/reliability.html b/meshview/templates/reliability.html index 85affae..be947f9 100644 --- a/meshview/templates/reliability.html +++ b/meshview/templates/reliability.html @@ -348,12 +348,13 @@ Gateway Heard + Avg Hops Reliability - Loading packets... + Loading packets... @@ -364,12 +365,13 @@ Gateway Heard + Avg Hops Reliability - Loading packets... + Loading packets... @@ -391,12 +393,13 @@ Packet Time Type + Hops Status - No packet selected. + No packet selected. @@ -466,6 +469,19 @@ function packetTypeTag(portnum) { `; } +function packetSeenHopCount(row) { + const hopStart = Number(row?.hop_start ?? 0); + const hopLimit = Number(row?.hop_limit ?? 0); + return Math.max(0, hopStart - hopLimit); +} + +function formatAverageHopCount(value) { + if (value === null || value === undefined || !Number.isFinite(value)) return "—"; + + const lower = Math.floor(value); + return value - lower === 0.5 ? lower : Math.round(value); +} + function reliabilityBucketFor(row) { return reliabilityBuckets.find(bucket => bucket.matches(row)) || null; } @@ -545,7 +561,7 @@ function renderReliabilityMap(gatewayRows, nodesById) { icon: reachMapIcon(`${Math.round(row.percent)}%`, bucket.color), }).bindPopup(` ${nodeLink(row.nodeId, nodesById)}
- Reliability: ${row.percent.toFixed(1)}%
+ Reliability: ${row.percent.toFixed(1)}% · Avg. hops ${formatAverageHopCount(row.avgHopCount)}
Heard: (${reportResults.length}) + ${formatAverageHopCount(row.avgHopCount)} ${row.percent.toFixed(1)}% `).join("") - : 'No gateways heard these packets.'; + : 'No gateways heard these packets.'; reliabilityGatewayDetails = new Map(gatewayRows.map(row => [ Number(row.nodeId), { label: gatewayLabel(row.nodeId, nodesById), heardCount: row.heardCount, total: reportResults.length, - packets: seenResults.map(result => ({ - id: result.packet.id, - import_time_us: result.packet.import_time_us, - portnum: result.packet.portnum, - skipped: isSkippedReliabilityPacket(result.packet), - heard: new Set(result.seen.map(seen => Number(seen.node_id))).has(Number(row.nodeId)), - })), + packets: seenResults.map(result => { + const seenRow = result.seen.find(seen => Number(seen.node_id) === Number(row.nodeId)); + return { + id: result.packet.id, + import_time_us: result.packet.import_time_us, + portnum: result.packet.portnum, + skipped: isSkippedReliabilityPacket(result.packet), + heard: Boolean(seenRow), + hopCount: seenRow ? packetSeenHopCount(seenRow) : null, + }; + }), } ])); const splitIndex = Math.ceil(gatewayRows.length / 2); bodyEls[0].innerHTML = renderGatewayRows(gatewayRows.slice(0, splitIndex)); bodyEls[1].innerHTML = gatewayRows.length > 1 ? renderGatewayRows(gatewayRows.slice(splitIndex)) - : 'No additional gateways.'; + : 'No additional gateways.'; renderReliabilityMap(gatewayRows, nodesById); } catch (err) { console.error("Failed to load reach report:", err); packetCountEl.textContent = "!"; gatewayCountEl.textContent = "!"; bucketBodyEl.innerHTML = 'Failed to load statistics.'; - setGatewayTables('Failed to load report.'); + setGatewayTables('Failed to load report.'); resetReliabilityMap(); } } @@ -794,9 +824,9 @@ document.addEventListener("DOMContentLoaded", async () => { document.getElementById("reach-bucket-body").innerHTML = 'Enter a valid node ID or select a node.'; document.getElementById("reach-packet-body-left").innerHTML = - 'Enter a valid node ID or select a node.'; + 'Enter a valid node ID or select a node.'; document.getElementById("reach-packet-body-right").innerHTML = - 'Enter a valid node ID or select a node.'; + 'Enter a valid node ID or select a node.'; return; }