{% /* * Part of AREDN® -- Used for creating Amateur Radio Emergency Data Networks * Copyright (C) 2024 Tim Wilkinson * See Contributors file for additional contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additional Terms: * * Additional use restrictions exist on the AREDN® trademark and logo. * See AREDNLicense.txt for more info. * * Attributions to the AREDN® Project must be retained in the source code. * If importing this code into a new or existing project attribution * to the AREDN® project must be added to the source code. * * You must not misrepresent the origin of the material contained within. * * Modified versions must be modified to attribute to the original source * and be marked in reasonable ways as differentiate it from the original * version */ %} {% if (request.env.REQUEST_METHOD === "PUT") { configuration.prepareChanges(); const userb = split(uciMesh.get("aredn", "@lqm[0]", "user_blocks"), ","); const blockedmacs = {}; for (let i = 0; i < length(userb); i++) { blockedmacs[uc(userb[i])] = true; } const usera = split(uciMesh.get("aredn", "@lqm[0]", "user_allows"), ","); const allowedmacs = {}; for (let i = 0; i < length(usera); i++) { allowedmacs[uc(usera[i])] = true; } for (let mac in request.args) { if (request.args[mac] === "always") { delete blockedmacs[uc(mac)]; allowedmacs[uc(mac)] = true; } else if (request.args[mac] === "user") { blockedmacs[uc(mac)] = true; delete allowedmacs[uc(mac)]; } else { delete blockedmacs[uc(mac)]; delete allowedmacs[uc(mac)]; } } uciMesh.set("aredn", "@lqm[0]", "user_blocks", join(",", keys(blockedmacs))); uciMesh.set("aredn", "@lqm[0]", "user_allows", join(",", keys(allowedmacs))); uciMesh.commit("aredn"); print(_R("changes")); return; } if (request.env.REQUEST_METHOD === "DELETE") { configuration.revertModalChanges(); print(_R("changes")); return; } const selected = substr(request.env.QUERY_STRING, 2); const blockedmacs = {}; const user = split(uciMesh.get("aredn", "@lqm[0]", "user_blocks"), ","); for (let i = 0; i < length(user); i++) { blockedmacs[uc(user[i])] = true; } const usera = split(uciMesh.get("aredn", "@lqm[0]", "user_allows"), ","); const allowedmacs = {}; for (let i = 0; i < length(usera); i++) { allowedmacs[uc(usera[i])] = true; } const lqmInfo = lqm.get(); const trackers = lqm.getTrackers(); const tracker = trackers[selected]; let neighbor = null; if (tracker) { if (allowedmacs[uc(tracker.mac)]) { tracker.user_allow = true; } else if (blockedmacs[uc(tracker.mac)]) { tracker.blocks.user = true; } neighbor = { name: tracker.hostname || `|${tracker.ip}`, n: tracker, l: null }; const o = olsr.getLinks(); for (let i = 0; i < length(o); i++) { if (o[i].remoteIP === tracker.ip) { neighbor.l = o[i]; break; } } } %}
{% if (tracker && tracker.type === "DtD" && tracker.distance < 100) { %} {{_R("dialog-header", "Local Node")}} {% } else { %} {{_R("dialog-header", "Neighborhood Node")}} {% } %}
{{_H("Provides more detailed information about the state of a link from this node to another. The current blocked state is show in the top/right corner. This can be changed to either always block or never block to override the automatic management of the link's use.")}} {% function state(n) { if (n.lastseen < lqmInfo.now) { return "disconnected"; } if (n.blocks.user) { return "blocked by user"; } if (n.blocked) { if (n.blocks.signal) { return "blocked: low snr"; } if (n.blocks.distance) { return "blocked: too far away"; } if (n.blocks.quality) { if ("tx_quality" in n) { if ("ping_quality" in n && n.ping_quality < n.tx_quality) { return "blocked: poor tx latency"; } else { return "blocked: too many tx error"; } } else { return "blocked: poor tx latency"; } } if (n.blocks.dup || n.blocks.dtd) { return "blocked: duplicate link"; } return "blocked"; } if (n.routable) { if ((n.blocks.signal || n.blocks.distance || n.blocks.quality) && ( (n.leaf === "major" && n.rev_leaf === "minor") || (n.leaf === "minor" && n.rev_leaf === "major"))) { return "routing leaf"; } return "routing"; } return "unused"; } function map(n, v) { const map_url = uci.get("aredn", "@location[0]", "map"); if (n.lat && n.lon && map_url) { return `${v}`; } return v; } if (neighbor) { const n = neighbor.n; const l = neighbor.l; %}
{{n.hostname || n.ip}}
{{n.type}}
type
{{n.mac}}
mac address
{{n.ip}}
ip address
{% if (n.model && n.firmware_version) { %}
{{n.model}}
model
{{n.firmware_version}}
firmware
{% } %}
{{map(n, n.lat) || "-"}}
latitude
{{map(n, n.lon) || "-"}}
longitude
{{"distance" in n ? map(n, sprintf("%.1f %s", units.meters2distance(n.distance), units.distanceUnit())) : "-"}}
distance
{% if (l && l.lossMultiplier) { const lq = min(100, int(100 * l.linkQuality * 65536 / l.lossMultiplier)); const nlq = min(100, int(100 * l.neighborLinkQuality * 65536 / l.lossMultiplier)); const etx = 10000.0 / (lq * nlq); %}
{{lq}}%
lq | rx success
{{nlq}}%
nlq | tx success
{{sprintf("%.1f", etx)}}
etx
{% } %}
{{type(n.ping_success_time) ? sprintf("%.1f ms", n.ping_success_time * 1000) : "-"}}
ping time
{{type(n.ping_quality) ? sprintf("%d%%", n.ping_quality) : "-"}}
ping success
{{type(n.avg_tx) ? sprintf("%.1f pkt/sec", n.avg_tx / 60) : "-"}}
avg tx
{{type(n.rev_ping_success_time) ? sprintf("%.1f ms", n.rev_ping_success_time * 1000) : "-"}}
neighbor ping time
{{type(n.rev_ping_quality) ? sprintf("%d%%", n.rev_ping_quality) : "-"}}
neighbor ping success
{{type(n.rev_quality) ? sprintf("%d%%", 100 - n.rev_quality) : "-"}}
neighbor errors
{% if (n.type == "RF") { %}
{{n.snr}}
local snr
{{n.rev_snr || "-"}}
neighbor snr
{{n.avg_tx_fail ? sprintf("%.1f%%", 100 * n.avg_tx_fail / n.avg_tx) : "-"}}
tx failures
{{n.rx_bitrate ? sprintf("%.1f Mbps", n.rx_bitrate) : "-"}}
physical rx bitrate
{{n.tx_bitrate ? sprintf("%.1f Mbps", n.tx_bitrate) : "-"}}
physical tx bitrate
{{n.avg_tx_retries ? sprintf("%.1f%%", 100 * n.avg_tx_retries / n.avg_tx) : "-"}}
tx retransmissions
{% } else if (type(n.avg_tx_fail)) { %}
{{sprintf("%.1f%%", 100 * n.avg_tx_fail / n.avg_tx)}}
tx failures
{% } %}
{{state(n)}}
state
{{n.node_route_count}}
active routes
{% const snr = `/tmp/snrlog/${uc(selected)}-${lc(n.hostname)}`; if (fs.access(snr)) { let signal = ""; noise += "' />"; %}
dBm 0 -20 -40 -60 -80 -100 -120 {{signal}}{{noise}} {{hints}}
{% } %}
{% } %}
{{_R("dialog-footer")}}