Compare commits

...

6 Commits

Author SHA1 Message Date
Hemna 026dc6e376 syncronize the add for StatsStore 2024-04-11 22:55:01 -04:00
Hemna f59b65d13c Lock on stats for PacketList 2024-04-11 22:24:02 -04:00
Hemna 5ff62c9bdf Fixed PacketList maxlen
This patch removes the MutableMapping from PacketList
and fixes the code that keeps the max packets in the internal
dict.
2024-04-11 21:40:43 -04:00
Hemna 5fa4eaf909 Fixed a problem with the webchat tab notification
Somehow the hidden div for the webchat interface's
tab notification was removed.  this patch adds it back in
so the user knows that they have message(s) for a tab that
isn't selected
2024-04-11 18:11:05 -04:00
Hemna f34120c2df Another fix for ACK packets 2024-04-11 17:28:47 -04:00
Hemna 3bef1314f8 Fix issue not tracking RX Ack packets for stats
This patch updates the RX tracking for packets.  Every
packet we get into the rx thread, we now will track
every packet we RX so the stats are acurate.
2024-04-11 16:54:46 -04:00
5 changed files with 28 additions and 29 deletions

View File

@ -1,5 +1,4 @@
from collections import OrderedDict from collections import OrderedDict
from collections.abc import MutableMapping
import logging import logging
import threading import threading
@ -14,7 +13,7 @@ CONF = cfg.CONF
LOG = logging.getLogger("APRSD") LOG = logging.getLogger("APRSD")
class PacketList(MutableMapping, objectstore.ObjectStoreMixin): class PacketList(objectstore.ObjectStoreMixin):
_instance = None _instance = None
lock = threading.Lock() lock = threading.Lock()
_total_rx: int = 0 _total_rx: int = 0
@ -57,10 +56,15 @@ class PacketList(MutableMapping, objectstore.ObjectStoreMixin):
self._add(packet) self._add(packet)
def _add(self, packet): def _add(self, packet):
if packet.key in self.data["packets"]:
self.data["packets"].move_to_end(packet.key)
elif len(self.data["packets"]) == self.maxlen:
self.data["packets"].popitem(last=False)
self.data["packets"][packet.key] = packet self.data["packets"][packet.key] = packet
@wrapt.synchronized(lock)
def copy(self): def copy(self):
return self.d.copy() return self.data.copy()
@property @property
def maxlen(self): def maxlen(self):
@ -68,25 +72,9 @@ class PacketList(MutableMapping, objectstore.ObjectStoreMixin):
@wrapt.synchronized(lock) @wrapt.synchronized(lock)
def find(self, packet): def find(self, packet):
return self.get(packet.key) return self.data["packets"][packet.key]
def __getitem__(self, key):
# self.d.move_to_end(key)
return self.data["packets"][key]
def __setitem__(self, key, value):
if key in self.data["packets"]:
self.data["packets"].move_to_end(key)
elif len(self.data["packets"]) == self.maxlen:
self.data["packets"].popitem(last=False)
self.data["packets"][key] = value
def __delitem__(self, key):
del self.data["packets"][key]
def __iter__(self):
return self.data["packets"].__iter__()
@wrapt.synchronized(lock)
def __len__(self): def __len__(self):
return len(self.data["packets"]) return len(self.data["packets"])
@ -98,13 +86,13 @@ class PacketList(MutableMapping, objectstore.ObjectStoreMixin):
def total_tx(self): def total_tx(self):
return self._total_tx return self._total_tx
@wrapt.synchronized(lock)
def stats(self, serializable=False) -> dict: def stats(self, serializable=False) -> dict:
stats = { stats = {
"total_tracked": self.total_tx() + self.total_rx(), "total_tracked": self._total_rx + self._total_rx,
"rx": self.total_rx(), "rx": self._total_rx,
"tx": self.total_tx(), "tx": self._total_tx,
"types": self.data["types"], "types": self.data["types"],
"packets": self.data["packets"], "packets": self.data["packets"],
} }
return stats return stats

View File

@ -96,6 +96,7 @@ class APRSDDupeRXThread(APRSDRXThread):
packet = self._client.decode_packet(*args, **kwargs) packet = self._client.decode_packet(*args, **kwargs)
# LOG.debug(raw) # LOG.debug(raw)
packet_log.log(packet) packet_log.log(packet)
pkt_list = packets.PacketList()
if isinstance(packet, packets.AckPacket): if isinstance(packet, packets.AckPacket):
# We don't need to drop AckPackets, those should be # We don't need to drop AckPackets, those should be
@ -106,7 +107,6 @@ class APRSDDupeRXThread(APRSDRXThread):
# For RF based APRS Clients we can get duplicate packets # For RF based APRS Clients we can get duplicate packets
# So we need to track them and not process the dupes. # So we need to track them and not process the dupes.
found = False found = False
pkt_list = packets.PacketList()
try: try:
# Find the packet in the list of already seen packets # Find the packet in the list of already seen packets
# Based on the packet.key # Based on the packet.key
@ -157,6 +157,7 @@ class APRSDProcessPacketThread(APRSDThread):
"""We got an ack for a message, no need to resend it.""" """We got an ack for a message, no need to resend it."""
ack_num = packet.msgNo ack_num = packet.msgNo
LOG.debug(f"Got ack for message {ack_num}") LOG.debug(f"Got ack for message {ack_num}")
packets.PacketList().rx(packet)
pkt_tracker = packets.PacketTrack() pkt_tracker = packets.PacketTrack()
pkt_tracker.remove(ack_num) pkt_tracker.remove(ack_num)
@ -164,6 +165,7 @@ class APRSDProcessPacketThread(APRSDThread):
"""We got a reject message for a packet. Stop sending the message.""" """We got a reject message for a packet. Stop sending the message."""
ack_num = packet.msgNo ack_num = packet.msgNo
LOG.debug(f"Got REJECT for message {ack_num}") LOG.debug(f"Got REJECT for message {ack_num}")
packets.PacketList().rx(packet)
pkt_tracker = packets.PacketTrack() pkt_tracker = packets.PacketTrack()
pkt_tracker.remove(ack_num) pkt_tracker.remove(ack_num)

View File

@ -3,6 +3,7 @@ import threading
import time import time
from oslo_config import cfg from oslo_config import cfg
import wrapt
from aprsd.stats import collector from aprsd.stats import collector
from aprsd.threads import APRSDThread from aprsd.threads import APRSDThread
@ -18,6 +19,10 @@ class StatsStore(objectstore.ObjectStoreMixin):
lock = threading.Lock() lock = threading.Lock()
data = {} data = {}
@wrapt.synchronized(lock)
def add(self, stats: dict):
self.data = stats
class APRSDStatsStoreThread(APRSDThread): class APRSDStatsStoreThread(APRSDThread):
"""Save APRSD Stats to disk periodically.""" """Save APRSD Stats to disk periodically."""
@ -32,7 +37,7 @@ class APRSDStatsStoreThread(APRSDThread):
if self.loop_count % self.save_interval == 0: if self.loop_count % self.save_interval == 0:
stats = collector.Collector().collect() stats = collector.Collector().collect()
ss = StatsStore() ss = StatsStore()
ss.data = stats ss.add(stats)
ss.save() ss.save()
time.sleep(1) time.sleep(1)

View File

@ -70,8 +70,8 @@ class ObjectStoreMixin:
"""Save any queued to disk?""" """Save any queued to disk?"""
if not CONF.enable_save: if not CONF.enable_save:
return return
save_filename = self._save_filename()
if len(self) > 0: if len(self) > 0:
save_filename = self._save_filename()
LOG.info( LOG.info(
f"{self.__class__.__name__}::Saving" f"{self.__class__.__name__}::Saving"
f" {len(self)} entries to disk at " f" {len(self)} entries to disk at "
@ -83,7 +83,7 @@ class ObjectStoreMixin:
LOG.debug( LOG.debug(
"{} Nothing to save, flushing old save file '{}'".format( "{} Nothing to save, flushing old save file '{}'".format(
self.__class__.__name__, self.__class__.__name__,
self._save_filename(), save_filename,
), ),
) )
self.flush() self.flush()

View File

@ -313,6 +313,7 @@ function create_callsign_tab(callsign, active=false) {
//item_html += '<button onClick="callsign_select(\''+callsign+'\');" callsign="'+callsign+'" class="nav-link '+active_str+'" id="'+tab_id+'" data-bs-toggle="tab" data-bs-target="#'+tab_content+'" type="button" role="tab" aria-controls="'+callsign+'" aria-selected="true">'; //item_html += '<button onClick="callsign_select(\''+callsign+'\');" callsign="'+callsign+'" class="nav-link '+active_str+'" id="'+tab_id+'" data-bs-toggle="tab" data-bs-target="#'+tab_content+'" type="button" role="tab" aria-controls="'+callsign+'" aria-selected="true">';
item_html += '<button onClick="callsign_select(\''+callsign+'\');" callsign="'+callsign+'" class="nav-link position-relative '+active_str+'" id="'+tab_id+'" data-bs-toggle="tab" data-bs-target="#'+tab_content+'" type="button" role="tab" aria-controls="'+callsign+'" aria-selected="true">'; item_html += '<button onClick="callsign_select(\''+callsign+'\');" callsign="'+callsign+'" class="nav-link position-relative '+active_str+'" id="'+tab_id+'" data-bs-toggle="tab" data-bs-target="#'+tab_content+'" type="button" role="tab" aria-controls="'+callsign+'" aria-selected="true">';
item_html += callsign+'&nbsp;&nbsp;'; item_html += callsign+'&nbsp;&nbsp;';
item_html += '<span id="'+tab_notify_id+'" class="position-absolute top-0 start-80 translate-middle badge bg-danger border border-light rounded-pill visually-hidden">0</span>';
item_html += '<span onclick="delete_tab(\''+callsign+'\');">×</span>'; item_html += '<span onclick="delete_tab(\''+callsign+'\');">×</span>';
item_html += '</button></li>' item_html += '</button></li>'
@ -407,6 +408,9 @@ function append_message(callsign, msg, msg_html) {
tab_notify_id = tab_notification_id(callsign, true); tab_notify_id = tab_notification_id(callsign, true);
// get the current count of notifications // get the current count of notifications
count = parseInt($(tab_notify_id).text()); count = parseInt($(tab_notify_id).text());
if (isNaN(count)) {
count = 0;
}
count += 1; count += 1;
$(tab_notify_id).text(count); $(tab_notify_id).text(count);
$(tab_notify_id).removeClass('visually-hidden'); $(tab_notify_id).removeClass('visually-hidden');