aprsd 3.3.4__py2.py3-none-any.whl → 3.4.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aprsd/client.py +133 -20
- aprsd/clients/aprsis.py +6 -3
- aprsd/clients/fake.py +1 -1
- aprsd/clients/kiss.py +1 -1
- aprsd/cmds/completion.py +13 -27
- aprsd/cmds/fetch_stats.py +53 -57
- aprsd/cmds/healthcheck.py +32 -30
- aprsd/cmds/list_plugins.py +2 -2
- aprsd/cmds/listen.py +33 -17
- aprsd/cmds/send_message.py +2 -2
- aprsd/cmds/server.py +26 -9
- aprsd/cmds/webchat.py +34 -29
- aprsd/conf/common.py +46 -31
- aprsd/log/log.py +28 -6
- aprsd/main.py +4 -17
- aprsd/packets/__init__.py +3 -2
- aprsd/packets/collector.py +56 -0
- aprsd/packets/core.py +456 -321
- aprsd/packets/log.py +143 -0
- aprsd/packets/packet_list.py +83 -66
- aprsd/packets/seen_list.py +30 -19
- aprsd/packets/tracker.py +60 -62
- aprsd/packets/watch_list.py +64 -38
- aprsd/plugin.py +41 -16
- aprsd/plugins/email.py +35 -7
- aprsd/plugins/time.py +3 -2
- aprsd/plugins/version.py +4 -5
- aprsd/plugins/weather.py +0 -1
- aprsd/stats/__init__.py +20 -0
- aprsd/stats/app.py +46 -0
- aprsd/stats/collector.py +38 -0
- aprsd/threads/__init__.py +3 -2
- aprsd/threads/aprsd.py +67 -36
- aprsd/threads/keep_alive.py +55 -49
- aprsd/threads/log_monitor.py +46 -0
- aprsd/threads/rx.py +43 -24
- aprsd/threads/stats.py +44 -0
- aprsd/threads/tx.py +36 -17
- aprsd/utils/__init__.py +12 -0
- aprsd/utils/counter.py +6 -3
- aprsd/utils/json.py +20 -0
- aprsd/utils/objectstore.py +22 -17
- aprsd/web/admin/static/css/prism.css +4 -189
- aprsd/web/admin/static/js/charts.js +9 -7
- aprsd/web/admin/static/js/echarts.js +71 -9
- aprsd/web/admin/static/js/main.js +47 -6
- aprsd/web/admin/static/js/prism.js +11 -2246
- aprsd/web/admin/templates/index.html +18 -7
- aprsd/web/chat/static/js/gps.js +3 -1
- aprsd/web/chat/static/js/main.js +4 -3
- aprsd/web/chat/static/js/send-message.js +5 -2
- aprsd/web/chat/templates/index.html +1 -0
- aprsd/wsgi.py +62 -127
- {aprsd-3.3.4.dist-info → aprsd-3.4.0.dist-info}/METADATA +14 -16
- {aprsd-3.3.4.dist-info → aprsd-3.4.0.dist-info}/RECORD +60 -63
- {aprsd-3.3.4.dist-info → aprsd-3.4.0.dist-info}/WHEEL +1 -1
- aprsd-3.4.0.dist-info/pbr.json +1 -0
- aprsd/plugins/query.py +0 -81
- aprsd/rpc/__init__.py +0 -14
- aprsd/rpc/client.py +0 -165
- aprsd/rpc/server.py +0 -99
- aprsd/stats.py +0 -266
- aprsd/web/admin/static/json-viewer/jquery.json-viewer.css +0 -57
- aprsd/web/admin/static/json-viewer/jquery.json-viewer.js +0 -158
- aprsd/web/chat/static/json-viewer/jquery.json-viewer.css +0 -57
- aprsd/web/chat/static/json-viewer/jquery.json-viewer.js +0 -158
- aprsd-3.3.4.dist-info/pbr.json +0 -1
- {aprsd-3.3.4.dist-info → aprsd-3.4.0.dist-info}/LICENSE +0 -0
- {aprsd-3.3.4.dist-info → aprsd-3.4.0.dist-info}/entry_points.txt +0 -0
- {aprsd-3.3.4.dist-info → aprsd-3.4.0.dist-info}/top_level.txt +0 -0
aprsd/packets/log.py
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from loguru import logger
|
5
|
+
from oslo_config import cfg
|
6
|
+
|
7
|
+
from aprsd.packets.core import AckPacket, RejectPacket
|
8
|
+
|
9
|
+
|
10
|
+
LOG = logging.getLogger()
|
11
|
+
LOGU = logger
|
12
|
+
CONF = cfg.CONF
|
13
|
+
|
14
|
+
FROM_COLOR = "fg #C70039"
|
15
|
+
TO_COLOR = "fg #D033FF"
|
16
|
+
TX_COLOR = "red"
|
17
|
+
RX_COLOR = "green"
|
18
|
+
PACKET_COLOR = "cyan"
|
19
|
+
|
20
|
+
|
21
|
+
def log_multiline(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> None:
|
22
|
+
"""LOG a packet to the logfile."""
|
23
|
+
if not CONF.enable_packet_logging:
|
24
|
+
return
|
25
|
+
if CONF.log_packet_format == "compact":
|
26
|
+
return
|
27
|
+
|
28
|
+
# asdict(packet)
|
29
|
+
logit = ["\n"]
|
30
|
+
name = packet.__class__.__name__
|
31
|
+
|
32
|
+
if isinstance(packet, AckPacket):
|
33
|
+
pkt_max_send_count = CONF.default_ack_send_count
|
34
|
+
else:
|
35
|
+
pkt_max_send_count = CONF.default_packet_send_count
|
36
|
+
|
37
|
+
if header:
|
38
|
+
if tx:
|
39
|
+
header_str = f"<{TX_COLOR}>TX</{TX_COLOR}>"
|
40
|
+
logit.append(
|
41
|
+
f"{header_str}________(<{PACKET_COLOR}>{name}</{PACKET_COLOR}> "
|
42
|
+
f"TX:{packet.send_count + 1} of {pkt_max_send_count}",
|
43
|
+
)
|
44
|
+
else:
|
45
|
+
header_str = f"<{RX_COLOR}>RX</{RX_COLOR}>"
|
46
|
+
logit.append(
|
47
|
+
f"{header_str}________(<{PACKET_COLOR}>{name}</{PACKET_COLOR}>)",
|
48
|
+
)
|
49
|
+
|
50
|
+
else:
|
51
|
+
header_str = ""
|
52
|
+
logit.append(f"__________(<{PACKET_COLOR}>{name}</{PACKET_COLOR}>)")
|
53
|
+
# log_list.append(f" Packet : {packet.__class__.__name__}")
|
54
|
+
if packet.msgNo:
|
55
|
+
logit.append(f" Msg # : {packet.msgNo}")
|
56
|
+
if packet.from_call:
|
57
|
+
logit.append(f" From : <{FROM_COLOR}>{packet.from_call}</{FROM_COLOR}>")
|
58
|
+
if packet.to_call:
|
59
|
+
logit.append(f" To : <{TO_COLOR}>{packet.to_call}</{TO_COLOR}>")
|
60
|
+
if hasattr(packet, "path") and packet.path:
|
61
|
+
logit.append(f" Path : {'=>'.join(packet.path)}")
|
62
|
+
if hasattr(packet, "via") and packet.via:
|
63
|
+
logit.append(f" VIA : {packet.via}")
|
64
|
+
|
65
|
+
if not isinstance(packet, AckPacket) and not isinstance(packet, RejectPacket):
|
66
|
+
msg = packet.human_info
|
67
|
+
|
68
|
+
if msg:
|
69
|
+
msg = msg.replace("<", "\\<")
|
70
|
+
logit.append(f" Info : <light-yellow><b>{msg}</b></light-yellow>")
|
71
|
+
|
72
|
+
if hasattr(packet, "comment") and packet.comment:
|
73
|
+
logit.append(f" Comment : {packet.comment}")
|
74
|
+
|
75
|
+
raw = packet.raw.replace("<", "\\<")
|
76
|
+
logit.append(f" Raw : <fg #828282>{raw}</fg #828282>")
|
77
|
+
logit.append(f"{header_str}________(<{PACKET_COLOR}>{name}</{PACKET_COLOR}>)")
|
78
|
+
|
79
|
+
LOGU.opt(colors=True).info("\n".join(logit))
|
80
|
+
LOG.debug(repr(packet))
|
81
|
+
|
82
|
+
|
83
|
+
def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> None:
|
84
|
+
if not CONF.enable_packet_logging:
|
85
|
+
return
|
86
|
+
if CONF.log_packet_format == "multiline":
|
87
|
+
log_multiline(packet, tx, header)
|
88
|
+
return
|
89
|
+
|
90
|
+
logit = []
|
91
|
+
name = packet.__class__.__name__
|
92
|
+
if isinstance(packet, AckPacket):
|
93
|
+
pkt_max_send_count = CONF.default_ack_send_count
|
94
|
+
else:
|
95
|
+
pkt_max_send_count = CONF.default_packet_send_count
|
96
|
+
|
97
|
+
if header:
|
98
|
+
if tx:
|
99
|
+
via_color = "red"
|
100
|
+
arrow = f"<{via_color}>-></{via_color}>"
|
101
|
+
logit.append(
|
102
|
+
f"<red>TX {arrow}</red> "
|
103
|
+
f"<cyan>{name}</cyan>"
|
104
|
+
f":{packet.msgNo}"
|
105
|
+
f" ({packet.send_count + 1} of {pkt_max_send_count})",
|
106
|
+
)
|
107
|
+
else:
|
108
|
+
via_color = "fg #828282"
|
109
|
+
arrow = f"<{via_color}>-></{via_color}>"
|
110
|
+
left_arrow = f"<{via_color}><-</{via_color}>"
|
111
|
+
logit.append(
|
112
|
+
f"<fg #1AA730>RX</fg #1AA730> {left_arrow} "
|
113
|
+
f"<cyan>{name}</cyan>"
|
114
|
+
f":{packet.msgNo}",
|
115
|
+
)
|
116
|
+
else:
|
117
|
+
via_color = "green"
|
118
|
+
arrow = f"<{via_color}>-></{via_color}>"
|
119
|
+
logit.append(
|
120
|
+
f"<cyan>{name}</cyan>"
|
121
|
+
f":{packet.msgNo}",
|
122
|
+
)
|
123
|
+
|
124
|
+
tmp = None
|
125
|
+
if packet.path:
|
126
|
+
tmp = f"{arrow}".join(packet.path) + f"{arrow} "
|
127
|
+
|
128
|
+
logit.append(
|
129
|
+
f"<{FROM_COLOR}>{packet.from_call}</{FROM_COLOR}> {arrow}"
|
130
|
+
f"{tmp if tmp else ' '}"
|
131
|
+
f"<{TO_COLOR}>{packet.to_call}</{TO_COLOR}>",
|
132
|
+
)
|
133
|
+
|
134
|
+
if not isinstance(packet, AckPacket) and not isinstance(packet, RejectPacket):
|
135
|
+
logit.append(":")
|
136
|
+
msg = packet.human_info
|
137
|
+
|
138
|
+
if msg:
|
139
|
+
msg = msg.replace("<", "\\<")
|
140
|
+
logit.append(f"<light-yellow><b>{msg}</b></light-yellow>")
|
141
|
+
|
142
|
+
LOGU.opt(colors=True).info(" ".join(logit))
|
143
|
+
log_multiline(packet, tx, header)
|
aprsd/packets/packet_list.py
CHANGED
@@ -1,99 +1,116 @@
|
|
1
1
|
from collections import OrderedDict
|
2
|
-
from collections.abc import MutableMapping
|
3
2
|
import logging
|
4
|
-
import threading
|
5
3
|
|
6
4
|
from oslo_config import cfg
|
7
|
-
import wrapt
|
8
5
|
|
9
|
-
from aprsd import
|
10
|
-
from aprsd.
|
6
|
+
from aprsd.packets import collector, core
|
7
|
+
from aprsd.utils import objectstore
|
11
8
|
|
12
9
|
|
13
10
|
CONF = cfg.CONF
|
14
11
|
LOG = logging.getLogger("APRSD")
|
15
12
|
|
16
13
|
|
17
|
-
class PacketList(
|
14
|
+
class PacketList(objectstore.ObjectStoreMixin):
|
15
|
+
"""Class to keep track of the packets we tx/rx."""
|
18
16
|
_instance = None
|
19
|
-
lock = threading.Lock()
|
20
17
|
_total_rx: int = 0
|
21
18
|
_total_tx: int = 0
|
22
|
-
|
19
|
+
maxlen: int = 100
|
23
20
|
|
24
21
|
def __new__(cls, *args, **kwargs):
|
25
22
|
if cls._instance is None:
|
26
23
|
cls._instance = super().__new__(cls)
|
27
|
-
cls.
|
28
|
-
cls.
|
24
|
+
cls._instance.maxlen = CONF.packet_list_maxlen
|
25
|
+
cls._instance._init_data()
|
29
26
|
return cls._instance
|
30
27
|
|
31
|
-
|
32
|
-
|
28
|
+
def _init_data(self):
|
29
|
+
self.data = {
|
30
|
+
"types": {},
|
31
|
+
"packets": OrderedDict(),
|
32
|
+
}
|
33
|
+
|
34
|
+
def rx(self, packet: type[core.Packet]):
|
33
35
|
"""Add a packet that was received."""
|
34
|
-
self.
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
@wrapt.synchronized(lock)
|
44
|
-
def tx(self, packet):
|
36
|
+
with self.lock:
|
37
|
+
self._total_rx += 1
|
38
|
+
self._add(packet)
|
39
|
+
ptype = packet.__class__.__name__
|
40
|
+
if not ptype in self.data["types"]:
|
41
|
+
self.data["types"][ptype] = {"tx": 0, "rx": 0}
|
42
|
+
self.data["types"][ptype]["rx"] += 1
|
43
|
+
|
44
|
+
def tx(self, packet: type[core.Packet]):
|
45
45
|
"""Add a packet that was received."""
|
46
|
-
self.
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
@wrapt.synchronized(lock)
|
46
|
+
with self.lock:
|
47
|
+
self._total_tx += 1
|
48
|
+
self._add(packet)
|
49
|
+
ptype = packet.__class__.__name__
|
50
|
+
if not ptype in self.data["types"]:
|
51
|
+
self.data["types"][ptype] = {"tx": 0, "rx": 0}
|
52
|
+
self.data["types"][ptype]["tx"] += 1
|
53
|
+
|
56
54
|
def add(self, packet):
|
57
|
-
self.
|
55
|
+
with self.lock:
|
56
|
+
self._add(packet)
|
58
57
|
|
59
58
|
def _add(self, packet):
|
60
|
-
self
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
return self._maxlen
|
59
|
+
if not self.data.get("packets"):
|
60
|
+
self._init_data()
|
61
|
+
if packet.key in self.data["packets"]:
|
62
|
+
self.data["packets"].move_to_end(packet.key)
|
63
|
+
elif len(self.data["packets"]) == self.maxlen:
|
64
|
+
self.data["packets"].popitem(last=False)
|
65
|
+
self.data["packets"][packet.key] = packet
|
68
66
|
|
69
|
-
@wrapt.synchronized(lock)
|
70
67
|
def find(self, packet):
|
71
|
-
|
72
|
-
|
73
|
-
def __getitem__(self, key):
|
74
|
-
# self.d.move_to_end(key)
|
75
|
-
return self.d[key]
|
76
|
-
|
77
|
-
def __setitem__(self, key, value):
|
78
|
-
if key in self.d:
|
79
|
-
self.d.move_to_end(key)
|
80
|
-
elif len(self.d) == self.maxlen:
|
81
|
-
self.d.popitem(last=False)
|
82
|
-
self.d[key] = value
|
83
|
-
|
84
|
-
def __delitem__(self, key):
|
85
|
-
del self.d[key]
|
86
|
-
|
87
|
-
def __iter__(self):
|
88
|
-
return self.d.__iter__()
|
68
|
+
with self.lock:
|
69
|
+
return self.data["packets"][packet.key]
|
89
70
|
|
90
71
|
def __len__(self):
|
91
|
-
|
72
|
+
with self.lock:
|
73
|
+
return len(self.data["packets"])
|
92
74
|
|
93
|
-
@wrapt.synchronized(lock)
|
94
75
|
def total_rx(self):
|
95
|
-
|
76
|
+
with self.lock:
|
77
|
+
return self._total_rx
|
96
78
|
|
97
|
-
@wrapt.synchronized(lock)
|
98
79
|
def total_tx(self):
|
99
|
-
|
80
|
+
with self.lock:
|
81
|
+
return self._total_tx
|
82
|
+
|
83
|
+
def stats(self, serializable=False) -> dict:
|
84
|
+
# limit the number of packets to return to 50
|
85
|
+
with self.lock:
|
86
|
+
tmp = OrderedDict(
|
87
|
+
reversed(
|
88
|
+
list(
|
89
|
+
self.data.get("packets", OrderedDict()).items(),
|
90
|
+
),
|
91
|
+
),
|
92
|
+
)
|
93
|
+
pkts = []
|
94
|
+
count = 1
|
95
|
+
for packet in tmp:
|
96
|
+
pkts.append(tmp[packet])
|
97
|
+
count += 1
|
98
|
+
if count > CONF.packet_list_stats_maxlen:
|
99
|
+
break
|
100
|
+
|
101
|
+
stats = {
|
102
|
+
"total_tracked": self._total_rx + self._total_rx,
|
103
|
+
"rx": self._total_rx,
|
104
|
+
"tx": self._total_tx,
|
105
|
+
"types": self.data.get("types", []),
|
106
|
+
"packet_count": len(self.data.get("packets", [])),
|
107
|
+
"maxlen": self.maxlen,
|
108
|
+
"packets": pkts,
|
109
|
+
}
|
110
|
+
return stats
|
111
|
+
|
112
|
+
|
113
|
+
# Now register the PacketList with the collector
|
114
|
+
# every packet we RX and TX goes through the collector
|
115
|
+
# for processing for whatever reason is needed.
|
116
|
+
collector.PacketCollector().register(PacketList)
|
aprsd/packets/seen_list.py
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
import datetime
|
2
2
|
import logging
|
3
|
-
import threading
|
4
3
|
|
5
4
|
from oslo_config import cfg
|
6
|
-
import wrapt
|
7
5
|
|
6
|
+
from aprsd.packets import collector, core
|
8
7
|
from aprsd.utils import objectstore
|
9
8
|
|
10
9
|
|
@@ -16,28 +15,40 @@ class SeenList(objectstore.ObjectStoreMixin):
|
|
16
15
|
"""Global callsign seen list."""
|
17
16
|
|
18
17
|
_instance = None
|
19
|
-
lock = threading.Lock()
|
20
18
|
data: dict = {}
|
21
19
|
|
22
20
|
def __new__(cls, *args, **kwargs):
|
23
21
|
if cls._instance is None:
|
24
22
|
cls._instance = super().__new__(cls)
|
25
|
-
cls._instance._init_store()
|
26
23
|
cls._instance.data = {}
|
27
24
|
return cls._instance
|
28
25
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
26
|
+
def stats(self, serializable=False):
|
27
|
+
"""Return the stats for the PacketTrack class."""
|
28
|
+
with self.lock:
|
29
|
+
return self.data
|
30
|
+
|
31
|
+
def rx(self, packet: type[core.Packet]):
|
32
|
+
"""When we get a packet from the network, update the seen list."""
|
33
|
+
with self.lock:
|
34
|
+
callsign = None
|
35
|
+
if packet.from_call:
|
36
|
+
callsign = packet.from_call
|
37
|
+
else:
|
38
|
+
LOG.warning(f"Can't find FROM in packet {packet}")
|
39
|
+
return
|
40
|
+
if callsign not in self.data:
|
41
|
+
self.data[callsign] = {
|
42
|
+
"last": None,
|
43
|
+
"count": 0,
|
44
|
+
}
|
45
|
+
self.data[callsign]["last"] = datetime.datetime.now()
|
46
|
+
self.data[callsign]["count"] += 1
|
47
|
+
|
48
|
+
def tx(self, packet: type[core.Packet]):
|
49
|
+
"""We don't care about TX packets."""
|
50
|
+
|
51
|
+
|
52
|
+
# Register with the packet collector so we can process the packet
|
53
|
+
# when we get it off the client (network)
|
54
|
+
collector.PacketCollector().register(SeenList)
|
aprsd/packets/tracker.py
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
import datetime
|
2
|
-
import
|
2
|
+
import logging
|
3
3
|
|
4
4
|
from oslo_config import cfg
|
5
|
-
import wrapt
|
6
5
|
|
7
|
-
from aprsd.
|
6
|
+
from aprsd.packets import collector, core
|
8
7
|
from aprsd.utils import objectstore
|
9
8
|
|
10
9
|
|
11
10
|
CONF = cfg.CONF
|
11
|
+
LOG = logging.getLogger("APRSD")
|
12
12
|
|
13
13
|
|
14
14
|
class PacketTrack(objectstore.ObjectStoreMixin):
|
@@ -26,7 +26,6 @@ class PacketTrack(objectstore.ObjectStoreMixin):
|
|
26
26
|
|
27
27
|
_instance = None
|
28
28
|
_start_time = None
|
29
|
-
lock = threading.Lock()
|
30
29
|
|
31
30
|
data: dict = {}
|
32
31
|
total_tracked: int = 0
|
@@ -38,74 +37,73 @@ class PacketTrack(objectstore.ObjectStoreMixin):
|
|
38
37
|
cls._instance._init_store()
|
39
38
|
return cls._instance
|
40
39
|
|
41
|
-
@wrapt.synchronized(lock)
|
42
40
|
def __getitem__(self, name):
|
43
|
-
|
41
|
+
with self.lock:
|
42
|
+
return self.data[name]
|
44
43
|
|
45
|
-
@wrapt.synchronized(lock)
|
46
44
|
def __iter__(self):
|
47
|
-
|
45
|
+
with self.lock:
|
46
|
+
return iter(self.data)
|
48
47
|
|
49
|
-
@wrapt.synchronized(lock)
|
50
48
|
def keys(self):
|
51
|
-
|
49
|
+
with self.lock:
|
50
|
+
return self.data.keys()
|
52
51
|
|
53
|
-
@wrapt.synchronized(lock)
|
54
52
|
def items(self):
|
55
|
-
|
53
|
+
with self.lock:
|
54
|
+
return self.data.items()
|
56
55
|
|
57
|
-
@wrapt.synchronized(lock)
|
58
56
|
def values(self):
|
59
|
-
|
57
|
+
with self.lock:
|
58
|
+
return self.data.values()
|
59
|
+
|
60
|
+
def stats(self, serializable=False):
|
61
|
+
with self.lock:
|
62
|
+
stats = {
|
63
|
+
"total_tracked": self.total_tracked,
|
64
|
+
}
|
65
|
+
pkts = {}
|
66
|
+
for key in self.data:
|
67
|
+
last_send_time = self.data[key].last_send_time
|
68
|
+
pkts[key] = {
|
69
|
+
"last_send_time": last_send_time,
|
70
|
+
"send_count": self.data[key].send_count,
|
71
|
+
"retry_count": self.data[key].retry_count,
|
72
|
+
"message": self.data[key].raw,
|
73
|
+
}
|
74
|
+
stats["packets"] = pkts
|
75
|
+
return stats
|
76
|
+
|
77
|
+
def rx(self, packet: type[core.Packet]) -> None:
|
78
|
+
"""When we get a packet from the network, check if we should remove it."""
|
79
|
+
if isinstance(packet, core.AckPacket):
|
80
|
+
self._remove(packet.msgNo)
|
81
|
+
elif isinstance(packet, core.RejectPacket):
|
82
|
+
self._remove(packet.msgNo)
|
83
|
+
elif hasattr(packet, "ackMsgNo"):
|
84
|
+
# Got a piggyback ack, so remove the original message
|
85
|
+
self._remove(packet.ackMsgNo)
|
86
|
+
|
87
|
+
def tx(self, packet: type[core.Packet]) -> None:
|
88
|
+
"""Add a packet that was sent."""
|
89
|
+
with self.lock:
|
90
|
+
key = packet.msgNo
|
91
|
+
packet.send_count = 0
|
92
|
+
self.data[key] = packet
|
93
|
+
self.total_tracked += 1
|
60
94
|
|
61
|
-
|
62
|
-
|
63
|
-
return len(self.data)
|
95
|
+
def remove(self, key):
|
96
|
+
self._remove(key)
|
64
97
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
98
|
+
def _remove(self, key):
|
99
|
+
with self.lock:
|
100
|
+
try:
|
101
|
+
del self.data[key]
|
102
|
+
except KeyError:
|
103
|
+
pass
|
71
104
|
|
72
|
-
@wrapt.synchronized(lock)
|
73
|
-
def get(self, key):
|
74
|
-
return self.data.get(key, None)
|
75
105
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
except KeyError:
|
81
|
-
pass
|
82
|
-
|
83
|
-
def restart(self):
|
84
|
-
"""Walk the list of messages and restart them if any."""
|
85
|
-
for key in self.data.keys():
|
86
|
-
pkt = self.data[key]
|
87
|
-
if pkt._last_send_attempt < pkt.retry_count:
|
88
|
-
tx.send(pkt)
|
89
|
-
|
90
|
-
def _resend(self, packet):
|
91
|
-
packet._last_send_attempt = 0
|
92
|
-
tx.send(packet)
|
93
|
-
|
94
|
-
def restart_delayed(self, count=None, most_recent=True):
|
95
|
-
"""Walk the list of delayed messages and restart them if any."""
|
96
|
-
if not count:
|
97
|
-
# Send all the delayed messages
|
98
|
-
for key in self.data.keys():
|
99
|
-
pkt = self.data[key]
|
100
|
-
if pkt._last_send_attempt == pkt._retry_count:
|
101
|
-
self._resend(pkt)
|
102
|
-
else:
|
103
|
-
# They want to resend <count> delayed messages
|
104
|
-
tmp = sorted(
|
105
|
-
self.data.items(),
|
106
|
-
reverse=most_recent,
|
107
|
-
key=lambda x: x[1].last_send_time,
|
108
|
-
)
|
109
|
-
pkt_list = tmp[:count]
|
110
|
-
for (_key, pkt) in pkt_list:
|
111
|
-
self._resend(pkt)
|
106
|
+
# Now register the PacketList with the collector
|
107
|
+
# every packet we RX and TX goes through the collector
|
108
|
+
# for processing for whatever reason is needed.
|
109
|
+
collector.PacketCollector().register(PacketTrack)
|