Collect route back. and show initiator and target.
This commit is contained in:
parent
dff6ed035a
commit
f1103748e6
2 changed files with 65 additions and 37 deletions
86
src/index.js
86
src/index.js
|
|
@ -616,54 +616,68 @@ app.get('/api/v1/traceroutes', async (req, res) => {
|
||||||
return trace;
|
return trace;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Build edges from the forward (towards) path using snr_towards
|
// Build edges from both forward (towards) and reverse (back) paths.
|
||||||
// The forward path is: to (initiator) → route[0] → route[1] → ... → from (responder?)
|
// Forward path: to → route[] → from, using snr_towards
|
||||||
// snr_towards holds SNR per hop along that path.
|
// Reverse path: from → route_back[] → to, using snr_back
|
||||||
// We only care about neighbor-like edges between consecutive hops with their SNR and updated_at.
|
const edgeKey = (a, b) => `${String(a)}->${String(b)}`;
|
||||||
const edgeKey = (a, b) => `${String(a)}->${String(b)}`; // directional; map layer can choose how to render
|
|
||||||
const edges = new Map();
|
const edges = new Map();
|
||||||
|
|
||||||
for (const tr of normalized) {
|
function upsertEdgesFromPath(trace, pathNodes, pathSnrs) {
|
||||||
const path = [];
|
for (let i = 0; i < pathNodes.length - 1; i++) {
|
||||||
if (tr.to != null) path.push(Number(tr.to));
|
const hopFrom = pathNodes[i];
|
||||||
if (Array.isArray(tr.route)) {
|
const hopTo = pathNodes[i + 1];
|
||||||
for (const hop of tr.route) {
|
const snr = typeof (pathSnrs && pathSnrs[i]) === 'number' ? pathSnrs[i] : null;
|
||||||
if (hop != null) path.push(Number(hop));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tr.from != null) path.push(Number(tr.from));
|
|
||||||
|
|
||||||
const snrs = Array.isArray(tr.snr_towards) ? tr.snr_towards : [];
|
const key = edgeKey(hopFrom, hopTo);
|
||||||
|
|
||||||
for (let i = 0; i < path.length - 1; i++) {
|
|
||||||
const fromNode = path[i];
|
|
||||||
const toNode = path[i + 1];
|
|
||||||
// snr_towards aligns to hops; guard if missing
|
|
||||||
const snr = typeof snrs[i] === 'number' ? snrs[i] : null;
|
|
||||||
|
|
||||||
const key = edgeKey(fromNode, toNode);
|
|
||||||
const existing = edges.get(key);
|
const existing = edges.get(key);
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
edges.set(key, {
|
edges.set(key, {
|
||||||
from: fromNode,
|
from: hopFrom,
|
||||||
to: toNode,
|
to: hopTo,
|
||||||
snr: snr,
|
snr: snr,
|
||||||
updated_at: tr.updated_at,
|
updated_at: trace.updated_at,
|
||||||
channel_id: tr.channel_id ?? null,
|
channel_id: trace.channel_id ?? null,
|
||||||
gateway_id: tr.gateway_id ?? null,
|
gateway_id: trace.gateway_id ?? null,
|
||||||
|
traceroute_from: trace.from, // original initiator
|
||||||
|
traceroute_to: trace.to, // original target
|
||||||
});
|
});
|
||||||
} else {
|
} else if (new Date(trace.updated_at) > new Date(existing.updated_at)) {
|
||||||
// Deduplicate by keeping the most recent updated_at
|
existing.snr = snr;
|
||||||
if (new Date(tr.updated_at) > new Date(existing.updated_at)) {
|
existing.updated_at = trace.updated_at;
|
||||||
existing.snr = snr;
|
existing.channel_id = trace.channel_id ?? existing.channel_id;
|
||||||
existing.updated_at = tr.updated_at;
|
existing.gateway_id = trace.gateway_id ?? existing.gateway_id;
|
||||||
existing.channel_id = tr.channel_id ?? existing.channel_id;
|
existing.traceroute_from = trace.from;
|
||||||
existing.gateway_id = tr.gateway_id ?? existing.gateway_id;
|
existing.traceroute_to = trace.to;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const tr of normalized) {
|
||||||
|
// Forward path
|
||||||
|
const forwardPath = [];
|
||||||
|
if (tr.to != null) forwardPath.push(Number(tr.to));
|
||||||
|
if (Array.isArray(tr.route)) {
|
||||||
|
for (const hop of tr.route) {
|
||||||
|
if (hop != null) forwardPath.push(Number(hop));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tr.from != null) forwardPath.push(Number(tr.from));
|
||||||
|
const forwardSnrs = Array.isArray(tr.snr_towards) ? tr.snr_towards : [];
|
||||||
|
upsertEdgesFromPath(tr, forwardPath, forwardSnrs);
|
||||||
|
|
||||||
|
// Reverse path
|
||||||
|
const reversePath = [];
|
||||||
|
if (tr.from != null) reversePath.push(Number(tr.from));
|
||||||
|
if (Array.isArray(tr.route_back)) {
|
||||||
|
for (const hop of tr.route_back) {
|
||||||
|
if (hop != null) reversePath.push(Number(hop));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tr.to != null) reversePath.push(Number(tr.to));
|
||||||
|
const reverseSnrs = Array.isArray(tr.snr_back) ? tr.snr_back : [];
|
||||||
|
upsertEdgesFromPath(tr, reversePath, reverseSnrs);
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
traceroute_edges: Array.from(edges.values()),
|
traceroute_edges: Array.from(edges.values()),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3119,11 +3119,15 @@
|
||||||
distance = `${km} kilometers`;
|
distance = `${km} kilometers`;
|
||||||
}
|
}
|
||||||
const terrainImageUrl = getTerrainProfileImage(fromNode, toNode);
|
const terrainImageUrl = getTerrainProfileImage(fromNode, toNode);
|
||||||
|
const targetNode = edge.traceroute_from ? findNodeById(edge.traceroute_from) : null;
|
||||||
|
const initiatorNode = edge.traceroute_to ? findNodeById(edge.traceroute_to) : null;
|
||||||
return `<b>Traceroute hop</b>`
|
return `<b>Traceroute hop</b>`
|
||||||
+ `<br/>from <b>[${escapeString(fromNode.short_name)}] ${escapeString(fromNode.long_name)}</b>`
|
+ `<br/>from <b>[${escapeString(fromNode.short_name)}] ${escapeString(fromNode.long_name)}</b>`
|
||||||
+ ` to <b>[${escapeString(toNode.short_name)}] ${escapeString(toNode.long_name)}</b>`
|
+ ` to <b>[${escapeString(toNode.short_name)}] ${escapeString(toNode.long_name)}</b>`
|
||||||
+ `<br/>SNR: ${snrDb != null ? snrDb + 'dB' : '?'}`
|
+ `<br/>SNR: ${snrDb != null ? snrDb + 'dB' : '?'}`
|
||||||
+ `<br/>Distance: ${distance}`
|
+ `<br/>Distance: ${distance}`
|
||||||
|
+ (initiatorNode ? `<br/>Traceroute from: <b>[${escapeString(initiatorNode.short_name)}] ${escapeString(initiatorNode.long_name)}</b>` : '')
|
||||||
|
+ (targetNode ? `<br/>Traceroute to: <b>[${escapeString(targetNode.short_name)}] ${escapeString(targetNode.long_name)}</b>` : '')
|
||||||
+ `<br/><br/>Terrain images from <a href=\"http://www.heywhatsthat.com\" target=\"_blank\">HeyWhatsThat.com</a>`
|
+ `<br/><br/>Terrain images from <a href=\"http://www.heywhatsthat.com\" target=\"_blank\">HeyWhatsThat.com</a>`
|
||||||
+ `<br/><a href=\"${terrainImageUrl}\" target=\"_blank\"><img src=\"${terrainImageUrl}\" width=\"100%\"></a>`;
|
+ `<br/><a href=\"${terrainImageUrl}\" target=\"_blank\"><img src=\"${terrainImageUrl}\" width=\"100%\"></a>`;
|
||||||
})();
|
})();
|
||||||
|
|
@ -3283,11 +3287,15 @@
|
||||||
distance = `${km} kilometers`;
|
distance = `${km} kilometers`;
|
||||||
}
|
}
|
||||||
const terrainImageUrl = getTerrainProfileImage(fromNode, toNode);
|
const terrainImageUrl = getTerrainProfileImage(fromNode, toNode);
|
||||||
|
const targetNode = edge.traceroute_from ? findNodeById(edge.traceroute_from) : null;
|
||||||
|
const initiatorNode = edge.traceroute_to ? findNodeById(edge.traceroute_to) : null;
|
||||||
return `<b>Traceroute hop</b>`
|
return `<b>Traceroute hop</b>`
|
||||||
+ `<br/>from <b>[${escapeString(fromNode.short_name)}] ${escapeString(fromNode.long_name)}</b>`
|
+ `<br/>from <b>[${escapeString(fromNode.short_name)}] ${escapeString(fromNode.long_name)}</b>`
|
||||||
+ ` to <b>[${escapeString(toNode.short_name)}] ${escapeString(toNode.long_name)}</b>`
|
+ ` to <b>[${escapeString(toNode.short_name)}] ${escapeString(toNode.long_name)}</b>`
|
||||||
+ `<br/>SNR: ${snrDb != null ? snrDb + 'dB' : '?'}`
|
+ `<br/>SNR: ${snrDb != null ? snrDb + 'dB' : '?'}`
|
||||||
+ `<br/>Distance: ${distance}`
|
+ `<br/>Distance: ${distance}`
|
||||||
|
+ (initiatorNode ? `<br/>Traceroute from: <b>[${escapeString(initiatorNode.short_name)}] ${escapeString(initiatorNode.long_name)}</b>` : '')
|
||||||
|
+ (targetNode ? `<br/>Traceroute to: <b>[${escapeString(targetNode.short_name)}] ${escapeString(targetNode.long_name)}</b>` : '')
|
||||||
+ `<br/><br/>Terrain images from <a href=\"http://www.heywhatsthat.com\" target=\"_blank\">HeyWhatsThat.com</a>`
|
+ `<br/><br/>Terrain images from <a href=\"http://www.heywhatsthat.com\" target=\"_blank\">HeyWhatsThat.com</a>`
|
||||||
+ `<br/><a href=\"${terrainImageUrl}\" target=\"_blank\"><img src=\"${terrainImageUrl}\" width=\"100%\"></a>`;
|
+ `<br/><a href=\"${terrainImageUrl}\" target=\"_blank\"><img src=\"${terrainImageUrl}\" width=\"100%\"></a>`;
|
||||||
})();
|
})();
|
||||||
|
|
@ -3789,11 +3797,17 @@
|
||||||
|
|
||||||
const terrainImageUrl = getTerrainProfileImage(fromNode, toNode);
|
const terrainImageUrl = getTerrainProfileImage(fromNode, toNode);
|
||||||
|
|
||||||
const tooltip = `Traceroute hop`
|
// This is backwards. It's because the traceroute packet is sent from the target node.
|
||||||
|
const targetNode = edge.traceroute_from ? findNodeById(edge.traceroute_from) : null;
|
||||||
|
const initiatorNode = edge.traceroute_to ? findNodeById(edge.traceroute_to) : null;
|
||||||
|
|
||||||
|
const tooltip = `<b>Traceroute hop</b>`
|
||||||
+ `<br/>from <b>[${escapeString(fromNode.short_name)}] ${escapeString(fromNode.long_name)}</b>`
|
+ `<br/>from <b>[${escapeString(fromNode.short_name)}] ${escapeString(fromNode.long_name)}</b>`
|
||||||
+ ` to <b>[${escapeString(toNode.short_name)}] ${escapeString(toNode.long_name)}</b>`
|
+ ` to <b>[${escapeString(toNode.short_name)}] ${escapeString(toNode.long_name)}</b>`
|
||||||
+ `<br/>SNR: ${snrDb != null ? snrDb + 'dB' : '?'}`
|
+ `<br/>SNR: ${snrDb != null ? snrDb + 'dB' : '?'}`
|
||||||
+ `<br/>Distance: ${distance}`
|
+ `<br/>Distance: ${distance}`
|
||||||
|
+ (initiatorNode ? `<br/>Traceroute from: <b>[${escapeString(initiatorNode.short_name)}] ${escapeString(initiatorNode.long_name)}</b>` : '')
|
||||||
|
+ (targetNode ? `<br/>Traceroute to: <b>[${escapeString(targetNode.short_name)}] ${escapeString(targetNode.long_name)}</b>` : '')
|
||||||
+ (edge.updated_at ? `<br/>Updated: ${moment(new Date(edge.updated_at)).fromNow()}` : '')
|
+ (edge.updated_at ? `<br/>Updated: ${moment(new Date(edge.updated_at)).fromNow()}` : '')
|
||||||
+ (edge.channel_id ? `<br/>Channel: ${edge.channel_id}` : '')
|
+ (edge.channel_id ? `<br/>Channel: ${edge.channel_id}` : '')
|
||||||
+ `<br/><br/>Terrain images from <a href="http://www.heywhatsthat.com" target="_blank">HeyWhatsThat.com</a>`
|
+ `<br/><br/>Terrain images from <a href="http://www.heywhatsthat.com" target="_blank">HeyWhatsThat.com</a>`
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue