diff --git a/meshview/templates/node.html b/meshview/templates/node.html
index 92c9004..d547a7e 100644
--- a/meshview/templates/node.html
+++ b/meshview/templates/node.html
@@ -608,10 +608,16 @@ function addMarker(id, lat, lon, color = "red", node = null) {
m.bringToFront();
}
-async function drawNeighbors(src, nids){
+async function drawNeighbors(src, nids) {
if (!map) return;
- const srcPos = nodePositions[src];
- if (!srcPos) return;
+
+ // Ensure source node position exists
+ const srcNode = await fetchNodeFromApi(src);
+ if (!srcNode || !srcNode.last_lat || !srcNode.last_long) return;
+
+ const srcLat = srcNode.last_lat / 1e7;
+ const srcLon = srcNode.last_long / 1e7;
+ nodePositions[src] = [srcLat, srcLon];
for (const nid of nids) {
const neighbor = await fetchNodeFromApi(nid);
@@ -620,13 +626,22 @@ async function drawNeighbors(src, nids){
const lat = neighbor.last_lat / 1e7;
const lon = neighbor.last_long / 1e7;
+ nodePositions[nid] = [lat, lon];
+
+ // Marker
addMarker(nid, lat, lon, "blue", neighbor);
- const dstPos = [lat, lon];
- L.polyline([srcPos, dstPos], { color:'gray', weight:1 }).addTo(map);
+ // Link line
+ L.polyline(
+ [[srcLat, srcLon], [lat, lon]],
+ { color: "gray", weight: 1 }
+ ).addTo(map);
}
+
+ ensureMapVisible();
}
+
function ensureMapVisible(){
if (!map) return;
requestAnimationFrame(() => {
@@ -758,8 +773,26 @@ async function loadPackets(filters = {}) {
const packets = data.packets || [];
currentPacketRows = packets;
+ for (const pkt of packets.reverse()) {
- for (const pkt of (data.packets || []).reverse()) {
+ // ================================
+ // 🔵 NEIGHBOR PACKETS → MAP OVERLAY
+ // ================================
+ if (pkt.portnum === 71 && pkt.payload && map) {
+ const nids = [];
+ const re = /neighbors\s*\{\s*node_id:\s*(\d+)/g;
+ let m;
+ while ((m = re.exec(pkt.payload)) !== null) {
+ nids.push(parseInt(m[1], 10));
+ }
+ if (nids.length) {
+ await drawNeighbors(pkt.from_node_id, nids);
+ }
+ }
+
+ // ================================
+ // TABLE ROW
+ // ================================
const safePayload = (pkt.payload || "")
.replace(/[<>]/g, m => (m === "<" ? "<" : ">"));
@@ -807,6 +840,7 @@ async function loadPackets(filters = {}) {
}
+
/* ======================================================
TELEMETRY CHARTS (portnum=67)
====================================================== */
@@ -983,7 +1017,7 @@ async function loadNeighborTimeSeries() {
}
const data = await res.json();
- let packets = data.packets || [];
+ const packets = data.packets || [];
if (!packets.length) {
document.getElementById("neighbor_chart_container").style.display = "none";
@@ -992,7 +1026,6 @@ async function loadNeighborTimeSeries() {
packets.sort((a, b) => (a.import_time_us || 0) - (b.import_time_us || 0));
- // neighborHistory = { node_id: { name, snr:[...], times:[...] } }
const neighborHistory = {};
for (const pkt of packets) {
@@ -1005,9 +1038,9 @@ async function loadNeighborTimeSeries() {
minute: "2-digit"
});
- // Extract neighbor blocks
const blockRe = /neighbors\s*\{([^}]+)\}/g;
let m;
+
while ((m = blockRe.exec(pkt.payload)) !== null) {
const block = m[1];
@@ -1019,9 +1052,12 @@ async function loadNeighborTimeSeries() {
const nid = parseInt(idMatch[1], 10);
const snr = parseFloat(snrMatch[1]);
+ // 🔑 ENSURE NODE DATA EXISTS
+ const neighbor = await fetchNodeFromApi(nid);
+
if (!neighborHistory[nid]) {
neighborHistory[nid] = {
- name: nodeMap[nid] || `Node ${nid}`,
+ name: neighbor?.short_name || neighbor?.long_name || `Node ${nid}`,
times: [],
snr: []
};
@@ -1037,39 +1073,29 @@ async function loadNeighborTimeSeries() {
const legend = [];
const series = [];
- for (const [nid, entry] of Object.entries(neighborHistory)) {
+ for (const entry of Object.values(neighborHistory)) {
legend.push(entry.name);
-
series.push({
name: entry.name,
type: "line",
smooth: true,
- connectNulls: true, // --- FIX #2: connect dots even if missing ---
+ connectNulls: true,
showSymbol: false,
- data: entry.snr,
+ data: entry.snr
});
}
- // Collect all timestamps from all neighbors
- const allTimesSet = new Set();
+ const allTimes = new Set();
+ Object.values(neighborHistory).forEach(e => e.times.forEach(t => allTimes.add(t)));
- for (const entry of Object.values(neighborHistory)) {
- for (const t of entry.times) {
- allTimesSet.add(t);
- }
- }
-
- // Convert to array and sort chronologically
- const sampleTimes = Array.from(allTimesSet).sort((a, b) => {
- return new Date(a) - new Date(b);
- });
+ const xTimes = Array.from(allTimes).sort((a, b) => new Date(a) - new Date(b));
chart.setOption({
tooltip: { trigger: "axis" },
legend: { data: legend, textStyle: { color: "#ccc" } },
xAxis: {
type: "category",
- data: sampleTimes,
+ data: xTimes,
axisLabel: { color: "#ccc" }
},
yAxis: {