diff --git a/aprsd/flask.py b/aprsd/flask.py
index bf1d9bc..7caf5f8 100644
--- a/aprsd/flask.py
+++ b/aprsd/flask.py
@@ -302,6 +302,9 @@ class APRSDFlask(flask_classful.FlaskView):
watch_count = 0
watch_age = 0
+ sl = packets.SeenList()
+ seen_count = len(sl.callsigns)
+
pm = plugin.PluginManager()
plugins = pm.get_plugins()
plugin_count = len(plugins)
@@ -343,6 +346,7 @@ class APRSDFlask(flask_classful.FlaskView):
config_json=json.dumps(self.config.data),
watch_count=watch_count,
watch_age=watch_age,
+ seen_count=seen_count,
plugin_count=plugin_count,
)
diff --git a/aprsd/packets.py b/aprsd/packets.py
index 8e80432..63b6b6c 100644
--- a/aprsd/packets.py
+++ b/aprsd/packets.py
@@ -50,6 +50,7 @@ class PacketList:
else:
self.total_recv += 1
self.packet_list.append(packet)
+ SeenList().update_seen(packet)
def get(self):
with self.lock:
@@ -149,6 +150,35 @@ class WatchList:
return False
+class SeenList:
+ """Global callsign seen list."""
+
+ _instance = None
+ callsigns = {}
+ config = None
+
+ def __new__(cls, *args, **kwargs):
+ if cls._instance is None:
+ cls._instance = super().__new__(cls)
+ cls._instance.lock = threading.Lock()
+ cls.callsigns = {}
+ return cls._instance
+
+ def update_seen(self, packet):
+ callsign = None
+ if "fromcall" in packet:
+ callsign = packet["fromcall"]
+ elif "from" in packet:
+ callsign = packet["from"]
+ if callsign not in self.callsigns:
+ self.callsigns[callsign] = {
+ "last": None,
+ "count": 0,
+ }
+ self.callsigns[callsign]["last"] = str(datetime.datetime.now())
+ self.callsigns[callsign]["count"] += 1
+
+
def get_packet_type(packet):
"""Decode the packet type from the packet."""
diff --git a/aprsd/stats.py b/aprsd/stats.py
index d4678f3..0590ff7 100644
--- a/aprsd/stats.py
+++ b/aprsd/stats.py
@@ -201,6 +201,7 @@ class APRSDStats:
}
wl = packets.WatchList()
+ sl = packets.SeenList()
stats = {
"aprsd": {
@@ -211,6 +212,7 @@ class APRSDStats:
"memory_peak": self.memory_peak,
"memory_peak_str": utils.human_size(self.memory_peak),
"watch_list": wl.callsigns,
+ "seen_list": sl.callsigns,
},
"aprs-is": {
"server": self.aprsis_server,
diff --git a/aprsd/web/static/js/main.js b/aprsd/web/static/js/main.js
index fa48fbb..40b3942 100644
--- a/aprsd/web/static/js/main.js
+++ b/aprsd/web/static/js/main.js
@@ -58,11 +58,31 @@ function update_watchlist_from_packet(callsign, val) {
//console.log(watchlist)
}
+function update_seenlist( data ) {
+ var seendiv = $("#seenDiv");
+ var html_str = '
'
+ html_str += 'HAM Callsign | Age since last seen by APRSD | '
+ html_str += 'Number of packets RX |
'
+ 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 += ''
+ html_str += ' ' + i + ' | '
+ html_str += '' + val["last"] + ' | '
+ html_str += '' + val["count"] + ' |
'
+ });
+ html_str += "
";
+ seendiv.append(html_str);
+}
+
function update_plugins( data ) {
var plugindiv = $("#pluginDiv");
var html_str = ''
html_str += 'Plugin Name | Plugin Enabled? | '
html_str += 'Processed Packets | Sent Packets | '
+ html_str += 'Version | '
html_str += '
'
plugindiv.html('')
@@ -72,7 +92,9 @@ function update_plugins( data ) {
for (var i=0; i' + key + ' | ' + val["enabled"] + ' | ' + val["rx"] + ' | ' + val["tx"] + ' | ';
+ html_str += '' + key + ' | ';
+ html_str += '' + val["enabled"] + ' | ' + val["rx"] + ' | ';
+ html_str += '' + val["tx"] + ' | ' + val["version"] +' |
';
}
html_str += "
";
plugindiv.append(html_str);
@@ -140,6 +162,7 @@ function start_update() {
success: function(data) {
update_stats(data);
update_watchlist(data);
+ update_seenlist(data);
update_plugins(data);
},
complete: function() {
diff --git a/aprsd/web/templates/index.html b/aprsd/web/templates/index.html
index 8ccceee..b7e4f21 100644
--- a/aprsd/web/templates/index.html
+++ b/aprsd/web/templates/index.html
@@ -18,7 +18,6 @@
-
@@ -79,6 +78,7 @@
+
+