1
0
mirror of https://github.com/craigerl/aprsd.git synced 2025-12-20 12:44:42 -05:00
Hemna ff392395ed Decouple admin web interface from server command
This patch introduces rpyc based RPC client/server for
the flask web interface to call into the running aprsd server
command to fetch stats, logs, etc to send to the browser.

This allows running the web interface via gunicorn command
gunicorn -k gevent --reload --threads 10 -w 1 aprsd.flask:app --log-level DEBUG
2022-12-28 15:55:09 -05:00

191 lines
6.6 KiB
JavaScript

// watchlist is a dict of ham callsign => symbol, packets
var watchlist = {};
var our_callsign = "";
function aprs_img(item, x_offset, y_offset) {
var x = x_offset * -16;
if (y_offset > 5) {
y_offset = 5;
}
var y = y_offset * -16;
var loc = x + 'px '+ y + 'px'
item.css('background-position', loc);
}
function show_aprs_icon(item, symbol) {
var offset = ord(symbol) - 33;
var col = Math.floor(offset / 16);
var row = offset % 16;
//console.log("'" + symbol+"' off: "+offset+" row: "+ row + " col: " + col)
aprs_img(item, row, col);
}
function ord(str){return str.charCodeAt(0);}
function update_watchlist( data ) {
// Update the watch list
var watchdiv = $("#watchDiv");
var html_str = '<table class="ui celled striped table"><thead><tr><th>HAM Callsign</th><th>Age since last seen by APRSD</th></tr></thead><tbody>'
watchdiv.html('')
jQuery.each(data["stats"]["aprsd"]["watch_list"], function(i, val) {
html_str += '<tr><td class="collapsing"><img id="callsign_'+i+'" class="aprsd_1"></img>' + i + '</td><td>' + val["last"] + '</td></tr>'
});
html_str += "</tbody></table>";
watchdiv.append(html_str);
jQuery.each(watchlist, function(i, val) {
//update the symbol
var call_img = $('#callsign_'+i);
show_aprs_icon(call_img, val['symbol'])
});
}
function update_watchlist_from_packet(callsign, val) {
if (!watchlist.hasOwnProperty(callsign)) {
watchlist[callsign] = {
"symbol": '[',
"packets": {},
}
} else {
if (val.hasOwnProperty('symbol')) {
//console.log("Updating symbol for "+callsign + " to "+val["symbol"])
watchlist[callsign]["symbol"] = val["symbol"]
}
}
if (watchlist[callsign]["packets"].hasOwnProperty(val['ts']) == false) {
watchlist[callsign]["packets"][val['ts']]= val;
}
//console.log(watchlist)
}
function update_seenlist( data ) {
var seendiv = $("#seenDiv");
var html_str = '<table class="ui celled striped table">'
html_str += '<thead><tr><th>HAM Callsign</th><th>Age since last seen by APRSD</th>'
html_str += '<th>Number of packets RX</th></tr></thead><tbody>'
seendiv.html('')
var seen_list = data["stats"]["aprsd"]["seen_list"]
var len = Object.keys(seen_list).length
$('#seen_count').html(len)
jQuery.each(seen_list, function(i, val) {
html_str += '<tr><td class="collapsing">'
html_str += '<img id="callsign_'+i+'" class="aprsd_1"></img>' + i + '</td>'
html_str += '<td>' + val["last"] + '</td>'
html_str += '<td>' + val["count"] + '</td></tr>'
});
html_str += "</tbody></table>";
seendiv.append(html_str);
}
function update_plugins( data ) {
var plugindiv = $("#pluginDiv");
var html_str = '<table class="ui celled striped table"><thead><tr>'
html_str += '<th>Plugin Name</th><th>Plugin Enabled?</th>'
html_str += '<th>Processed Packets</th><th>Sent Packets</th>'
html_str += '<th>Version</th>'
html_str += '</tr></thead><tbody>'
plugindiv.html('')
var plugins = data["stats"]["plugins"];
var keys = Object.keys(plugins);
keys.sort();
for (var i=0; i<keys.length; i++) { // now lets iterate in sort order
var key = keys[i];
var val = plugins[key];
html_str += '<tr><td class="collapsing">' + key + '</td>';
html_str += '<td>' + val["enabled"] + '</td><td>' + val["rx"] + '</td>';
html_str += '<td>' + val["tx"] + '</td><td>' + val["version"] +'</td></tr>';
}
html_str += "</tbody></table>";
plugindiv.append(html_str);
}
function update_packets( data ) {
var packetsdiv = $("#packetsDiv");
//nuke the contents first, then add to it.
if (size_dict(packet_list) == 0 && size_dict(data) > 0) {
packetsdiv.html('')
}
jQuery.each(data, function(i, val) {
pkt = JSON.parse(val);
update_watchlist_from_packet(pkt['from_call'], pkt);
if ( packet_list.hasOwnProperty(pkt.timestamp) == false ) {
// Store the packet
packet_list[pkt.timestamp] = pkt;
//ts_str = val["timestamp"].toString();
//ts = ts_str.split(".")[0]*1000;
ts = pkt.timestamp
var d = new Date(ts).toLocaleDateString("en-US");
var t = new Date(ts).toLocaleTimeString("en-US");
var from_call = pkt.from_call;
if (from_call == our_callsign) {
title_id = 'title_tx';
} else {
title_id = 'title_rx';
}
var from_to = d + " " + t + "&nbsp;&nbsp;&nbsp;&nbsp;" + from_call + " > "
if (val.hasOwnProperty('addresse')) {
from_to = from_to + pkt['addresse']
} else if (pkt.hasOwnProperty('to_call')) {
from_to = from_to + pkt['to_call']
} else if (pkt.hasOwnProperty('format') && pkt['format'] == 'mic-e') {
from_to = from_to + "Mic-E"
}
from_to = from_to + "&nbsp;&nbsp;-&nbsp;&nbsp;" + pkt['raw']
json_pretty = Prism.highlight(JSON.stringify(pkt, null, '\t'), Prism.languages.json, 'json');
pkt_html = '<div class="title" id="' + title_id + '"><i class="dropdown icon"></i>' + from_to + '</div><div class="content"><p class="transition hidden"><pre class="language-json">' + json_pretty + '</p></p></div>'
packetsdiv.prepend(pkt_html);
}
});
$('.ui.accordion').accordion('refresh');
// Update the count of messages shown
cnt = size_dict(packet_list);
//console.log("packets list " + cnt)
$('#packets_count').html(cnt);
const html_pretty = Prism.highlight(JSON.stringify(data, null, '\t'), Prism.languages.json, 'json');
$("#packetsjson").html(html_pretty);
}
function start_update() {
(function statsworker() {
$.ajax({
url: "/stats",
type: 'GET',
dataType: 'json',
success: function(data) {
update_stats(data);
update_watchlist(data);
update_seenlist(data);
update_plugins(data);
},
complete: function() {
setTimeout(statsworker, 10000);
}
});
})();
(function packetsworker() {
$.ajax({
url: "/packets",
type: 'GET',
dataType: 'json',
success: function(data) {
update_packets(data);
},
complete: function() {
setTimeout(packetsworker, 10000);
}
});
})();
}