mirror of
https://github.com/pablorevilla-meshtastic/meshview.git
synced 2026-06-10 17:24:56 +02:00
added hop count the the reliablility report.
This commit is contained in:
@@ -348,12 +348,13 @@
|
||||
<tr>
|
||||
<th>Gateway</th>
|
||||
<th>Heard</th>
|
||||
<th>Avg Hops</th>
|
||||
<th>Reliability</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="reach-packet-body-left">
|
||||
<tr>
|
||||
<td colspan="3" class="reach-muted">Loading packets...</td>
|
||||
<td colspan="4" class="reach-muted">Loading packets...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -364,12 +365,13 @@
|
||||
<tr>
|
||||
<th>Gateway</th>
|
||||
<th>Heard</th>
|
||||
<th>Avg Hops</th>
|
||||
<th>Reliability</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="reach-packet-body-right">
|
||||
<tr>
|
||||
<td colspan="3" class="reach-muted">Loading packets...</td>
|
||||
<td colspan="4" class="reach-muted">Loading packets...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -391,12 +393,13 @@
|
||||
<th>Packet</th>
|
||||
<th>Time</th>
|
||||
<th>Type</th>
|
||||
<th>Hops</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="gateway-packet-body">
|
||||
<tr>
|
||||
<td colspan="4" class="reach-muted">No packet selected.</td>
|
||||
<td colspan="5" class="reach-muted">No packet selected.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -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(`
|
||||
<b>${nodeLink(row.nodeId, nodesById)}</b><br>
|
||||
Reliability: ${row.percent.toFixed(1)}%<br>
|
||||
Reliability: ${row.percent.toFixed(1)}% · Avg. hops ${formatAverageHopCount(row.avgHopCount)}<br>
|
||||
Heard:
|
||||
<button class="reach-heard-link" type="button" onclick="openGatewayPacketModal(${row.nodeId})">
|
||||
${row.heardCount}
|
||||
@@ -587,6 +603,7 @@ function openGatewayPacketModal(nodeId) {
|
||||
<td><a href="/packet/${packet.id}">${packet.id}</a></td>
|
||||
<td>${escapeHtml(formatPacketTime(packet.import_time_us))}</td>
|
||||
<td>${packetTypeTag(packet.portnum)}</td>
|
||||
<td>${packet.hopCount ?? "—"}</td>
|
||||
<td class="${statusClass}">${statusText}</td>
|
||||
</tr>
|
||||
`;
|
||||
@@ -658,7 +675,7 @@ async function loadReachReport(nodeId = reachNodeId) {
|
||||
reliabilityGatewayDetails = new Map();
|
||||
nodeLinkEl.href = `/node/${encodeURIComponent(reachNodeId)}`;
|
||||
bucketBodyEl.innerHTML = '<tr><td colspan="2" class="reach-muted">Loading statistics...</td></tr>';
|
||||
setGatewayTables('<tr><td colspan="3" class="reach-muted">Loading packets...</td></tr>');
|
||||
setGatewayTables('<tr><td colspan="4" class="reach-muted">Loading packets...</td></tr>');
|
||||
resetReliabilityMap();
|
||||
|
||||
try {
|
||||
@@ -679,7 +696,7 @@ async function loadReachReport(nodeId = reachNodeId) {
|
||||
if (packets.length === 0) {
|
||||
gatewayCountEl.textContent = "0";
|
||||
bucketBodyEl.innerHTML = '<tr><td colspan="2" class="reach-muted">No packets found.</td></tr>';
|
||||
setGatewayTables('<tr><td colspan="3" class="reach-muted">No packets found.</td></tr>');
|
||||
setGatewayTables('<tr><td colspan="4" class="reach-muted">No packets found.</td></tr>');
|
||||
resetReliabilityMap();
|
||||
return;
|
||||
}
|
||||
@@ -698,19 +715,27 @@ async function loadReachReport(nodeId = reachNodeId) {
|
||||
const gatewayStats = new Map();
|
||||
const reportResults = seenResults.filter(result => !isSkippedReliabilityPacket(result.packet));
|
||||
reportResults.forEach(result => {
|
||||
const gatewaysForPacket = new Set(result.seen.map(row => Number(row.node_id)));
|
||||
gatewaysForPacket.delete(reachNodeId);
|
||||
gatewaysForPacket.forEach(nodeId => {
|
||||
gatewayStats.set(nodeId, (gatewayStats.get(nodeId) || 0) + 1);
|
||||
const seenByGateway = new Map();
|
||||
result.seen.forEach(row => {
|
||||
const nodeId = Number(row.node_id);
|
||||
if (nodeId === reachNodeId || seenByGateway.has(nodeId)) return;
|
||||
seenByGateway.set(nodeId, row);
|
||||
});
|
||||
seenByGateway.forEach((seenRow, nodeId) => {
|
||||
const stats = gatewayStats.get(nodeId) || { heardCount: 0, hopTotal: 0 };
|
||||
stats.heardCount += 1;
|
||||
stats.hopTotal += packetSeenHopCount(seenRow);
|
||||
gatewayStats.set(nodeId, stats);
|
||||
});
|
||||
});
|
||||
|
||||
const gatewayRows = [...gatewayStats.entries()]
|
||||
.map(([nodeId, heardCount]) => ({
|
||||
.map(([nodeId, stats]) => ({
|
||||
nodeId,
|
||||
heardCount,
|
||||
heardCount: stats.heardCount,
|
||||
avgHopCount: stats.heardCount ? stats.hopTotal / stats.heardCount : null,
|
||||
total: reportResults.length,
|
||||
percent: reportResults.length ? (heardCount / reportResults.length) * 100 : 0,
|
||||
percent: reportResults.length ? (stats.heardCount / reportResults.length) * 100 : 0,
|
||||
}))
|
||||
.sort((a, b) => b.percent - a.percent || b.heardCount - a.heardCount || a.nodeId - b.nodeId);
|
||||
|
||||
@@ -746,37 +771,42 @@ async function loadReachReport(nodeId = reachNodeId) {
|
||||
</button>
|
||||
<span class="text-secondary">(${reportResults.length})</span>
|
||||
</td>
|
||||
<td>${formatAverageHopCount(row.avgHopCount)}</td>
|
||||
<td>${row.percent.toFixed(1)}%</td>
|
||||
</tr>
|
||||
`).join("")
|
||||
: '<tr><td colspan="3" class="reach-muted">No gateways heard these packets.</td></tr>';
|
||||
: '<tr><td colspan="4" class="reach-muted">No gateways heard these packets.</td></tr>';
|
||||
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))
|
||||
: '<tr><td colspan="3" class="reach-muted">No additional gateways.</td></tr>';
|
||||
: '<tr><td colspan="4" class="reach-muted">No additional gateways.</td></tr>';
|
||||
renderReliabilityMap(gatewayRows, nodesById);
|
||||
} catch (err) {
|
||||
console.error("Failed to load reach report:", err);
|
||||
packetCountEl.textContent = "!";
|
||||
gatewayCountEl.textContent = "!";
|
||||
bucketBodyEl.innerHTML = '<tr><td colspan="2" class="text-danger">Failed to load statistics.</td></tr>';
|
||||
setGatewayTables('<tr><td colspan="3" class="text-danger">Failed to load report.</td></tr>');
|
||||
setGatewayTables('<tr><td colspan="4" class="text-danger">Failed to load report.</td></tr>');
|
||||
resetReliabilityMap();
|
||||
}
|
||||
}
|
||||
@@ -794,9 +824,9 @@ document.addEventListener("DOMContentLoaded", async () => {
|
||||
document.getElementById("reach-bucket-body").innerHTML =
|
||||
'<tr><td colspan="2" class="text-warning">Enter a valid node ID or select a node.</td></tr>';
|
||||
document.getElementById("reach-packet-body-left").innerHTML =
|
||||
'<tr><td colspan="3" class="text-warning">Enter a valid node ID or select a node.</td></tr>';
|
||||
'<tr><td colspan="4" class="text-warning">Enter a valid node ID or select a node.</td></tr>';
|
||||
document.getElementById("reach-packet-body-right").innerHTML =
|
||||
'<tr><td colspan="3" class="text-warning">Enter a valid node ID or select a node.</td></tr>';
|
||||
'<tr><td colspan="4" class="text-warning">Enter a valid node ID or select a node.</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user