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;
}