aprsd 4.0.2__py3-none-any.whl → 4.1.0__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/drivers/aprsis.py +87 -35
- aprsd/client/drivers/kiss.py +28 -5
- aprsd/client/kiss.py +1 -0
- aprsd/cmds/listen.py +84 -91
- aprsd/cmds/server.py +1 -1
- aprsd/conf/common.py +100 -101
- aprsd/conf/log.py +27 -22
- aprsd/log/log.py +24 -14
- aprsd/packets/__init__.py +6 -0
- aprsd/packets/core.py +5 -2
- aprsd/packets/filter.py +58 -0
- aprsd/packets/filters/__init__.py +0 -0
- aprsd/packets/filters/dupe_filter.py +68 -0
- aprsd/packets/filters/packet_type.py +53 -0
- aprsd/packets/packet_list.py +33 -27
- aprsd/threads/__init__.py +1 -2
- aprsd/threads/rx.py +83 -75
- aprsd/threads/stats.py +4 -9
- aprsd/utils/objectstore.py +12 -13
- {aprsd-4.0.2.dist-info → aprsd-4.1.0.dist-info}/METADATA +1 -1
- {aprsd-4.0.2.dist-info → aprsd-4.1.0.dist-info}/RECORD +26 -22
- {aprsd-4.0.2.dist-info → aprsd-4.1.0.dist-info}/AUTHORS +0 -0
- {aprsd-4.0.2.dist-info → aprsd-4.1.0.dist-info}/LICENSE +0 -0
- {aprsd-4.0.2.dist-info → aprsd-4.1.0.dist-info}/WHEEL +0 -0
- {aprsd-4.0.2.dist-info → aprsd-4.1.0.dist-info}/entry_points.txt +0 -0
- {aprsd-4.0.2.dist-info → aprsd-4.1.0.dist-info}/top_level.txt +0 -0
aprsd/client/drivers/aprsis.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import datetime
|
2
2
|
import logging
|
3
3
|
import select
|
4
|
+
import socket
|
4
5
|
import threading
|
5
6
|
|
6
7
|
import aprslib
|
@@ -18,7 +19,7 @@ from aprslib.exceptions import (
|
|
18
19
|
import aprsd
|
19
20
|
from aprsd.packets import core
|
20
21
|
|
21
|
-
LOG = logging.getLogger(
|
22
|
+
LOG = logging.getLogger('APRSD')
|
22
23
|
|
23
24
|
|
24
25
|
class Aprsdis(aprslib.IS):
|
@@ -31,7 +32,7 @@ class Aprsdis(aprslib.IS):
|
|
31
32
|
aprsd_keepalive = datetime.datetime.now()
|
32
33
|
|
33
34
|
# Which server we are connected to?
|
34
|
-
server_string =
|
35
|
+
server_string = 'None'
|
35
36
|
|
36
37
|
# timeout in seconds
|
37
38
|
select_timeout = 1
|
@@ -39,10 +40,10 @@ class Aprsdis(aprslib.IS):
|
|
39
40
|
|
40
41
|
def stop(self):
|
41
42
|
self.thread_stop = True
|
42
|
-
LOG.warning(
|
43
|
+
LOG.warning('Shutdown Aprsdis client.')
|
43
44
|
|
44
45
|
def close(self):
|
45
|
-
LOG.warning(
|
46
|
+
LOG.warning('Closing Aprsdis client.')
|
46
47
|
super().close()
|
47
48
|
|
48
49
|
@wrapt.synchronized(lock)
|
@@ -54,6 +55,57 @@ class Aprsdis(aprslib.IS):
|
|
54
55
|
"""If the connection is alive or not."""
|
55
56
|
return self._connected
|
56
57
|
|
58
|
+
def _connect(self):
|
59
|
+
"""
|
60
|
+
Attemps connection to the server
|
61
|
+
"""
|
62
|
+
|
63
|
+
self.logger.info(
|
64
|
+
'Attempting connection to %s:%s', self.server[0], self.server[1]
|
65
|
+
)
|
66
|
+
|
67
|
+
try:
|
68
|
+
self._open_socket()
|
69
|
+
|
70
|
+
peer = self.sock.getpeername()
|
71
|
+
|
72
|
+
self.logger.info('Connected to %s', str(peer))
|
73
|
+
|
74
|
+
# 5 second timeout to receive server banner
|
75
|
+
self.sock.setblocking(1)
|
76
|
+
self.sock.settimeout(5)
|
77
|
+
|
78
|
+
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
79
|
+
# MACOS doesn't have TCP_KEEPIDLE
|
80
|
+
if hasattr(socket, 'TCP_KEEPIDLE'):
|
81
|
+
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1)
|
82
|
+
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3)
|
83
|
+
self.sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5)
|
84
|
+
|
85
|
+
banner = self.sock.recv(512)
|
86
|
+
if is_py3:
|
87
|
+
banner = banner.decode('latin-1')
|
88
|
+
|
89
|
+
if banner[0] == '#':
|
90
|
+
self.logger.debug('Banner: %s', banner.rstrip())
|
91
|
+
else:
|
92
|
+
raise ConnectionError('invalid banner from server')
|
93
|
+
|
94
|
+
except ConnectionError as e:
|
95
|
+
self.logger.error(str(e))
|
96
|
+
self.close()
|
97
|
+
raise
|
98
|
+
except (socket.error, socket.timeout) as e:
|
99
|
+
self.close()
|
100
|
+
|
101
|
+
self.logger.error('Socket error: %s' % str(e))
|
102
|
+
if str(e) == 'timed out':
|
103
|
+
raise ConnectionError('no banner from server') from e
|
104
|
+
else:
|
105
|
+
raise ConnectionError(e) from e
|
106
|
+
|
107
|
+
self._connected = True
|
108
|
+
|
57
109
|
def _socket_readlines(self, blocking=False):
|
58
110
|
"""
|
59
111
|
Generator for complete lines, received from the server
|
@@ -61,12 +113,12 @@ class Aprsdis(aprslib.IS):
|
|
61
113
|
try:
|
62
114
|
self.sock.setblocking(0)
|
63
115
|
except OSError as e:
|
64
|
-
self.logger.error(f
|
65
|
-
raise aprslib.ConnectionDrop(
|
116
|
+
self.logger.error(f'socket error when setblocking(0): {str(e)}')
|
117
|
+
raise aprslib.ConnectionDrop('connection dropped') from e
|
66
118
|
|
67
119
|
while not self.thread_stop:
|
68
|
-
short_buf = b
|
69
|
-
newline = b
|
120
|
+
short_buf = b''
|
121
|
+
newline = b'\r\n'
|
70
122
|
|
71
123
|
# set a select timeout, so we get a chance to exit
|
72
124
|
# when user hits CTRL-C
|
@@ -91,11 +143,11 @@ class Aprsdis(aprslib.IS):
|
|
91
143
|
# We could just not be blocking, so empty is expected
|
92
144
|
continue
|
93
145
|
else:
|
94
|
-
self.logger.error(
|
95
|
-
raise aprslib.ConnectionDrop(
|
146
|
+
self.logger.error('socket.recv(): returned empty')
|
147
|
+
raise aprslib.ConnectionDrop('connection dropped')
|
96
148
|
except OSError as e:
|
97
149
|
# self.logger.error("socket error on recv(): %s" % str(e))
|
98
|
-
if
|
150
|
+
if 'Resource temporarily unavailable' in str(e):
|
99
151
|
if not blocking:
|
100
152
|
if len(self.buf) == 0:
|
101
153
|
break
|
@@ -111,22 +163,22 @@ class Aprsdis(aprslib.IS):
|
|
111
163
|
"""
|
112
164
|
Sends login string to server
|
113
165
|
"""
|
114
|
-
login_str =
|
166
|
+
login_str = 'user {0} pass {1} vers Python-APRSD {3}{2}\r\n'
|
115
167
|
login_str = login_str.format(
|
116
168
|
self.callsign,
|
117
169
|
self.passwd,
|
118
|
-
(
|
170
|
+
(' filter ' + self.filter) if self.filter != '' else '',
|
119
171
|
aprsd.__version__,
|
120
172
|
)
|
121
173
|
|
122
|
-
self.logger.debug(
|
174
|
+
self.logger.debug('Sending login information')
|
123
175
|
|
124
176
|
try:
|
125
177
|
self._sendall(login_str)
|
126
178
|
self.sock.settimeout(5)
|
127
179
|
test = self.sock.recv(len(login_str) + 100)
|
128
180
|
if is_py3:
|
129
|
-
test = test.decode(
|
181
|
+
test = test.decode('latin-1')
|
130
182
|
test = test.rstrip()
|
131
183
|
|
132
184
|
self.logger.debug("Server: '%s'", test)
|
@@ -134,26 +186,26 @@ class Aprsdis(aprslib.IS):
|
|
134
186
|
if not test:
|
135
187
|
raise LoginError(f"Server Response Empty: '{test}'")
|
136
188
|
|
137
|
-
_, _, callsign, status, e = test.split(
|
138
|
-
s = e.split(
|
189
|
+
_, _, callsign, status, e = test.split(' ', 4)
|
190
|
+
s = e.split(',')
|
139
191
|
if len(s):
|
140
|
-
server_string = s[0].replace(
|
192
|
+
server_string = s[0].replace('server ', '')
|
141
193
|
else:
|
142
|
-
server_string = e.replace(
|
194
|
+
server_string = e.replace('server ', '')
|
143
195
|
|
144
|
-
if callsign ==
|
145
|
-
raise LoginError(
|
196
|
+
if callsign == '':
|
197
|
+
raise LoginError('Server responded with empty callsign???')
|
146
198
|
if callsign != self.callsign:
|
147
|
-
raise LoginError(f
|
148
|
-
if status !=
|
149
|
-
raise LoginError(
|
199
|
+
raise LoginError(f'Server: {test}')
|
200
|
+
if status != 'verified,' and self.passwd != '-1':
|
201
|
+
raise LoginError('Password is incorrect')
|
150
202
|
|
151
|
-
if self.passwd ==
|
152
|
-
self.logger.info(
|
203
|
+
if self.passwd == '-1':
|
204
|
+
self.logger.info('Login successful (receive only)')
|
153
205
|
else:
|
154
|
-
self.logger.info(
|
206
|
+
self.logger.info('Login successful')
|
155
207
|
|
156
|
-
self.logger.info(f
|
208
|
+
self.logger.info(f'Connected to {server_string}')
|
157
209
|
self.server_string = server_string
|
158
210
|
|
159
211
|
except LoginError as e:
|
@@ -164,7 +216,7 @@ class Aprsdis(aprslib.IS):
|
|
164
216
|
self.close()
|
165
217
|
self.logger.error(f"Failed to login '{e}'")
|
166
218
|
self.logger.exception(e)
|
167
|
-
raise LoginError(
|
219
|
+
raise LoginError('Failed to login') from e
|
168
220
|
|
169
221
|
def consumer(self, callback, blocking=True, immortal=False, raw=False):
|
170
222
|
"""
|
@@ -180,21 +232,21 @@ class Aprsdis(aprslib.IS):
|
|
180
232
|
"""
|
181
233
|
|
182
234
|
if not self._connected:
|
183
|
-
raise ConnectionError(
|
235
|
+
raise ConnectionError('not connected to a server')
|
184
236
|
|
185
|
-
line = b
|
237
|
+
line = b''
|
186
238
|
|
187
239
|
while True and not self.thread_stop:
|
188
240
|
try:
|
189
241
|
for line in self._socket_readlines(blocking):
|
190
|
-
if line[0:1] != b
|
242
|
+
if line[0:1] != b'#':
|
191
243
|
self.aprsd_keepalive = datetime.datetime.now()
|
192
244
|
if raw:
|
193
245
|
callback(line)
|
194
246
|
else:
|
195
247
|
callback(self._parse(line))
|
196
248
|
else:
|
197
|
-
self.logger.debug(
|
249
|
+
self.logger.debug('Server: %s', line.decode('utf8'))
|
198
250
|
self.aprsd_keepalive = datetime.datetime.now()
|
199
251
|
except ParseError as exp:
|
200
252
|
self.logger.log(
|
@@ -211,7 +263,7 @@ class Aprsdis(aprslib.IS):
|
|
211
263
|
exp.packet,
|
212
264
|
)
|
213
265
|
except LoginError as exp:
|
214
|
-
self.logger.error(
|
266
|
+
self.logger.error('%s: %s', exp.__class__.__name__, exp)
|
215
267
|
except (KeyboardInterrupt, SystemExit):
|
216
268
|
raise
|
217
269
|
except (ConnectionDrop, ConnectionError):
|
@@ -227,7 +279,7 @@ class Aprsdis(aprslib.IS):
|
|
227
279
|
except StopIteration:
|
228
280
|
break
|
229
281
|
except Exception:
|
230
|
-
self.logger.error(
|
282
|
+
self.logger.error('APRS Packet: %s', line)
|
231
283
|
raise
|
232
284
|
|
233
285
|
if not blocking:
|
aprsd/client/drivers/kiss.py
CHANGED
@@ -18,12 +18,13 @@ class KISS3Client:
|
|
18
18
|
|
19
19
|
# date for last time we heard from the server
|
20
20
|
aprsd_keepalive = datetime.datetime.now()
|
21
|
+
_connected = False
|
21
22
|
|
22
23
|
def __init__(self):
|
23
24
|
self.setup()
|
24
25
|
|
25
26
|
def is_alive(self):
|
26
|
-
return
|
27
|
+
return self._connected
|
27
28
|
|
28
29
|
def setup(self):
|
29
30
|
# we can be TCP kiss or Serial kiss
|
@@ -56,17 +57,33 @@ class KISS3Client:
|
|
56
57
|
self.path = CONF.kiss_tcp.path
|
57
58
|
|
58
59
|
LOG.debug('Starting KISS interface connection')
|
59
|
-
|
60
|
+
try:
|
61
|
+
self.kiss.start()
|
62
|
+
if self.kiss.protocol.transport.is_closing():
|
63
|
+
LOG.warning('KISS transport is closing, not setting consumer callback')
|
64
|
+
self._connected = False
|
65
|
+
else:
|
66
|
+
self._connected = True
|
67
|
+
except Exception:
|
68
|
+
LOG.error('Failed to start KISS interface.')
|
69
|
+
self._connected = False
|
60
70
|
|
61
71
|
@trace.trace
|
62
72
|
def stop(self):
|
73
|
+
if not self._connected:
|
74
|
+
# do nothing since we aren't connected
|
75
|
+
return
|
76
|
+
|
63
77
|
try:
|
64
78
|
self.kiss.stop()
|
65
79
|
self.kiss.loop.call_soon_threadsafe(
|
66
80
|
self.kiss.protocol.transport.close,
|
67
81
|
)
|
68
|
-
except Exception
|
69
|
-
LOG.
|
82
|
+
except Exception:
|
83
|
+
LOG.error('Failed to stop KISS interface.')
|
84
|
+
|
85
|
+
def close(self):
|
86
|
+
self.stop()
|
70
87
|
|
71
88
|
def set_filter(self, filter):
|
72
89
|
# This does nothing right now.
|
@@ -86,8 +103,14 @@ class KISS3Client:
|
|
86
103
|
LOG.exception(ex)
|
87
104
|
|
88
105
|
def consumer(self, callback):
|
106
|
+
if not self._connected:
|
107
|
+
raise Exception('KISS transport is not connected')
|
108
|
+
|
89
109
|
self._parse_callback = callback
|
90
|
-
self.kiss.
|
110
|
+
if not self.kiss.protocol.transport.is_closing():
|
111
|
+
self.kiss.read(callback=self.parse_frame, min_frames=1)
|
112
|
+
else:
|
113
|
+
self._connected = False
|
91
114
|
|
92
115
|
def send(self, packet):
|
93
116
|
"""Send an APRS Message object."""
|
aprsd/client/kiss.py
CHANGED
aprsd/cmds/listen.py
CHANGED
@@ -20,8 +20,10 @@ from aprsd import cli_helper, packets, plugin, threads, utils
|
|
20
20
|
from aprsd.client import client_factory
|
21
21
|
from aprsd.main import cli
|
22
22
|
from aprsd.packets import collector as packet_collector
|
23
|
+
from aprsd.packets import core, seen_list
|
23
24
|
from aprsd.packets import log as packet_log
|
24
|
-
from aprsd.packets import
|
25
|
+
from aprsd.packets.filter import PacketFilter
|
26
|
+
from aprsd.packets.filters import dupe_filter, packet_type
|
25
27
|
from aprsd.stats import collector
|
26
28
|
from aprsd.threads import keepalive, rx
|
27
29
|
from aprsd.threads import stats as stats_thread
|
@@ -29,7 +31,7 @@ from aprsd.threads.aprsd import APRSDThread
|
|
29
31
|
|
30
32
|
# setup the global logger
|
31
33
|
# log.basicConfig(level=log.DEBUG) # level=10
|
32
|
-
LOG = logging.getLogger(
|
34
|
+
LOG = logging.getLogger('APRSD')
|
33
35
|
CONF = cfg.CONF
|
34
36
|
LOGU = logger
|
35
37
|
console = Console()
|
@@ -37,9 +39,9 @@ console = Console()
|
|
37
39
|
|
38
40
|
def signal_handler(sig, frame):
|
39
41
|
threads.APRSDThreadList().stop_all()
|
40
|
-
if
|
42
|
+
if 'subprocess' not in str(frame):
|
41
43
|
LOG.info(
|
42
|
-
|
44
|
+
'Ctrl+C, Sending all threads exit! Can take up to 10 seconds {}'.format(
|
43
45
|
datetime.datetime.now(),
|
44
46
|
),
|
45
47
|
)
|
@@ -48,90 +50,66 @@ def signal_handler(sig, frame):
|
|
48
50
|
collector.Collector().collect()
|
49
51
|
|
50
52
|
|
51
|
-
class
|
53
|
+
class APRSDListenProcessThread(rx.APRSDFilterThread):
|
52
54
|
def __init__(
|
53
55
|
self,
|
54
56
|
packet_queue,
|
55
57
|
packet_filter=None,
|
56
58
|
plugin_manager=None,
|
57
|
-
enabled_plugins=
|
59
|
+
enabled_plugins=None,
|
58
60
|
log_packets=False,
|
59
61
|
):
|
60
|
-
super().__init__(packet_queue)
|
62
|
+
super().__init__('ListenProcThread', packet_queue)
|
61
63
|
self.packet_filter = packet_filter
|
62
64
|
self.plugin_manager = plugin_manager
|
63
65
|
if self.plugin_manager:
|
64
|
-
LOG.info(f
|
66
|
+
LOG.info(f'Plugins {self.plugin_manager.get_message_plugins()}')
|
65
67
|
self.log_packets = log_packets
|
66
68
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
packets.ObjectPacket.__name__: packets.ObjectPacket,
|
77
|
-
packets.StatusPacket.__name__: packets.StatusPacket,
|
78
|
-
packets.ThirdPartyPacket.__name__: packets.ThirdPartyPacket,
|
79
|
-
packets.WeatherPacket.__name__: packets.WeatherPacket,
|
80
|
-
packets.UnknownPacket.__name__: packets.UnknownPacket,
|
81
|
-
}
|
82
|
-
|
83
|
-
if self.packet_filter:
|
84
|
-
filter_class = filters[self.packet_filter]
|
85
|
-
if isinstance(packet, filter_class):
|
86
|
-
if self.log_packets:
|
87
|
-
packet_log.log(packet)
|
88
|
-
if self.plugin_manager:
|
89
|
-
# Don't do anything with the reply
|
90
|
-
# This is the listen only command.
|
91
|
-
self.plugin_manager.run(packet)
|
92
|
-
else:
|
93
|
-
if self.log_packets:
|
94
|
-
packet_log.log(packet)
|
95
|
-
if self.plugin_manager:
|
96
|
-
# Don't do anything with the reply.
|
97
|
-
# This is the listen only command.
|
98
|
-
self.plugin_manager.run(packet)
|
99
|
-
|
100
|
-
packet_collector.PacketCollector().rx(packet)
|
69
|
+
def print_packet(self, packet):
|
70
|
+
if self.log_packets:
|
71
|
+
packet_log.log(packet)
|
72
|
+
|
73
|
+
def process_packet(self, packet: type[core.Packet]):
|
74
|
+
if self.plugin_manager:
|
75
|
+
# Don't do anything with the reply.
|
76
|
+
# This is the listen only command.
|
77
|
+
self.plugin_manager.run(packet)
|
101
78
|
|
102
79
|
|
103
80
|
class ListenStatsThread(APRSDThread):
|
104
81
|
"""Log the stats from the PacketList."""
|
105
82
|
|
106
83
|
def __init__(self):
|
107
|
-
super().__init__(
|
84
|
+
super().__init__('PacketStatsLog')
|
108
85
|
self._last_total_rx = 0
|
86
|
+
self.period = 31
|
109
87
|
|
110
88
|
def loop(self):
|
111
|
-
if self.loop_count %
|
89
|
+
if self.loop_count % self.period == 0:
|
112
90
|
# log the stats every 10 seconds
|
113
91
|
stats_json = collector.Collector().collect()
|
114
|
-
stats = stats_json[
|
115
|
-
total_rx = stats[
|
116
|
-
packet_count = len(stats[
|
92
|
+
stats = stats_json['PacketList']
|
93
|
+
total_rx = stats['rx']
|
94
|
+
packet_count = len(stats['packets'])
|
117
95
|
rx_delta = total_rx - self._last_total_rx
|
118
|
-
rate = rx_delta /
|
96
|
+
rate = rx_delta / self.period
|
119
97
|
|
120
98
|
# Log summary stats
|
121
99
|
LOGU.opt(colors=True).info(
|
122
|
-
f
|
123
|
-
f
|
124
|
-
f
|
125
|
-
f
|
100
|
+
f'<green>RX Rate: {rate:.2f} pps</green> '
|
101
|
+
f'<yellow>Total RX: {total_rx}</yellow> '
|
102
|
+
f'<red>RX Last {self.period} secs: {rx_delta}</red> '
|
103
|
+
f'<white>Packets in PacketListStats: {packet_count}</white>',
|
126
104
|
)
|
127
105
|
self._last_total_rx = total_rx
|
128
106
|
|
129
107
|
# Log individual type stats
|
130
|
-
for k, v in stats[
|
131
|
-
thread_hex = f
|
108
|
+
for k, v in stats['types'].items():
|
109
|
+
thread_hex = f'fg {utils.hex_from_name(k)}'
|
132
110
|
LOGU.opt(colors=True).info(
|
133
|
-
f
|
134
|
-
f
|
111
|
+
f'<{thread_hex}>{k:<15}</{thread_hex}> '
|
112
|
+
f'<blue>RX: {v["rx"]}</blue> <red>TX: {v["tx"]}</red>',
|
135
113
|
)
|
136
114
|
|
137
115
|
time.sleep(1)
|
@@ -141,19 +119,19 @@ class ListenStatsThread(APRSDThread):
|
|
141
119
|
@cli.command()
|
142
120
|
@cli_helper.add_options(cli_helper.common_options)
|
143
121
|
@click.option(
|
144
|
-
|
145
|
-
envvar=
|
122
|
+
'--aprs-login',
|
123
|
+
envvar='APRS_LOGIN',
|
146
124
|
show_envvar=True,
|
147
|
-
help=
|
125
|
+
help='What callsign to send the message from.',
|
148
126
|
)
|
149
127
|
@click.option(
|
150
|
-
|
151
|
-
envvar=
|
128
|
+
'--aprs-password',
|
129
|
+
envvar='APRS_PASSWORD',
|
152
130
|
show_envvar=True,
|
153
|
-
help=
|
131
|
+
help='the APRS-IS password for APRS_LOGIN',
|
154
132
|
)
|
155
133
|
@click.option(
|
156
|
-
|
134
|
+
'--packet-filter',
|
157
135
|
type=click.Choice(
|
158
136
|
[
|
159
137
|
packets.AckPacket.__name__,
|
@@ -170,35 +148,37 @@ class ListenStatsThread(APRSDThread):
|
|
170
148
|
],
|
171
149
|
case_sensitive=False,
|
172
150
|
),
|
173
|
-
|
151
|
+
multiple=True,
|
152
|
+
default=[],
|
153
|
+
help='Filter by packet type',
|
174
154
|
)
|
175
155
|
@click.option(
|
176
|
-
|
156
|
+
'--enable-plugin',
|
177
157
|
multiple=True,
|
178
|
-
help=
|
158
|
+
help='Enable a plugin. This is the name of the file in the plugins directory.',
|
179
159
|
)
|
180
160
|
@click.option(
|
181
|
-
|
161
|
+
'--load-plugins',
|
182
162
|
default=False,
|
183
163
|
is_flag=True,
|
184
|
-
help=
|
164
|
+
help='Load plugins as enabled in aprsd.conf ?',
|
185
165
|
)
|
186
166
|
@click.argument(
|
187
|
-
|
167
|
+
'filter',
|
188
168
|
nargs=-1,
|
189
169
|
required=True,
|
190
170
|
)
|
191
171
|
@click.option(
|
192
|
-
|
172
|
+
'--log-packets',
|
193
173
|
default=False,
|
194
174
|
is_flag=True,
|
195
|
-
help=
|
175
|
+
help='Log incoming packets.',
|
196
176
|
)
|
197
177
|
@click.option(
|
198
|
-
|
178
|
+
'--enable-packet-stats',
|
199
179
|
default=False,
|
200
180
|
is_flag=True,
|
201
|
-
help=
|
181
|
+
help='Enable packet stats periodic logging.',
|
202
182
|
)
|
203
183
|
@click.pass_context
|
204
184
|
@cli_helper.process_standard_options
|
@@ -228,46 +208,46 @@ def listen(
|
|
228
208
|
|
229
209
|
if not aprs_login:
|
230
210
|
click.echo(ctx.get_help())
|
231
|
-
click.echo(
|
232
|
-
ctx.fail(
|
211
|
+
click.echo('')
|
212
|
+
ctx.fail('Must set --aprs-login or APRS_LOGIN')
|
233
213
|
ctx.exit()
|
234
214
|
|
235
215
|
if not aprs_password:
|
236
216
|
click.echo(ctx.get_help())
|
237
|
-
click.echo(
|
238
|
-
ctx.fail(
|
217
|
+
click.echo('')
|
218
|
+
ctx.fail('Must set --aprs-password or APRS_PASSWORD')
|
239
219
|
ctx.exit()
|
240
220
|
|
241
221
|
# CONF.aprs_network.login = aprs_login
|
242
222
|
# config["aprs"]["password"] = aprs_password
|
243
223
|
|
244
|
-
LOG.info(f
|
224
|
+
LOG.info(f'APRSD Listen Started version: {aprsd.__version__}')
|
245
225
|
|
246
226
|
CONF.log_opt_values(LOG, logging.DEBUG)
|
247
227
|
collector.Collector()
|
248
228
|
|
249
229
|
# Try and load saved MsgTrack list
|
250
|
-
LOG.debug(
|
230
|
+
LOG.debug('Loading saved MsgTrack object.')
|
251
231
|
|
252
232
|
# Initialize the client factory and create
|
253
233
|
# The correct client object ready for use
|
254
234
|
# Make sure we have 1 client transport enabled
|
255
235
|
if not client_factory.is_client_enabled():
|
256
|
-
LOG.error(
|
236
|
+
LOG.error('No Clients are enabled in config.')
|
257
237
|
sys.exit(-1)
|
258
238
|
|
259
239
|
# Creates the client object
|
260
|
-
LOG.info(
|
240
|
+
LOG.info('Creating client connection')
|
261
241
|
aprs_client = client_factory.create()
|
262
242
|
LOG.info(aprs_client)
|
263
243
|
if not aprs_client.login_success:
|
264
244
|
# We failed to login, will just quit!
|
265
|
-
msg = f
|
245
|
+
msg = f'Login Failure: {aprs_client.login_failure}'
|
266
246
|
LOG.error(msg)
|
267
247
|
print(msg)
|
268
248
|
sys.exit(-1)
|
269
249
|
|
270
|
-
LOG.debug(f"Filter by '{filter}'")
|
250
|
+
LOG.debug(f"Filter messages on aprsis server by '{filter}'")
|
271
251
|
aprs_client.set_filter(filter)
|
272
252
|
|
273
253
|
keepalive_thread = keepalive.KeepAliveThread()
|
@@ -276,10 +256,19 @@ def listen(
|
|
276
256
|
# just deregister the class from the packet collector
|
277
257
|
packet_collector.PacketCollector().unregister(seen_list.SeenList)
|
278
258
|
|
259
|
+
# we don't want the dupe filter to run here.
|
260
|
+
PacketFilter().unregister(dupe_filter.DupePacketFilter)
|
261
|
+
if packet_filter:
|
262
|
+
LOG.info('Enabling packet filtering for {packet_filter}')
|
263
|
+
packet_type.PacketTypeFilter().set_allow_list(packet_filter)
|
264
|
+
PacketFilter().register(packet_type.PacketTypeFilter)
|
265
|
+
else:
|
266
|
+
LOG.info('No packet filtering enabled.')
|
267
|
+
|
279
268
|
pm = None
|
280
269
|
if load_plugins:
|
281
270
|
pm = plugin.PluginManager()
|
282
|
-
LOG.info(
|
271
|
+
LOG.info('Loading plugins')
|
283
272
|
pm.setup_plugins(load_help_plugin=False)
|
284
273
|
elif enable_plugin:
|
285
274
|
pm = plugin.PluginManager()
|
@@ -290,33 +279,37 @@ def listen(
|
|
290
279
|
else:
|
291
280
|
LOG.warning(
|
292
281
|
"Not Loading any plugins use --load-plugins to load what's "
|
293
|
-
|
282
|
+
'defined in the config file.',
|
294
283
|
)
|
295
284
|
|
296
285
|
if pm:
|
297
286
|
for p in pm.get_plugins():
|
298
|
-
LOG.info(
|
287
|
+
LOG.info('Loaded plugin %s', p.__class__.__name__)
|
299
288
|
|
300
289
|
stats = stats_thread.APRSDStatsStoreThread()
|
301
290
|
stats.start()
|
302
291
|
|
303
|
-
LOG.debug(
|
304
|
-
|
292
|
+
LOG.debug('Start APRSDRxThread')
|
293
|
+
rx_thread = rx.APRSDRXThread(packet_queue=threads.packet_queue)
|
294
|
+
rx_thread.start()
|
295
|
+
|
296
|
+
LOG.debug('Create APRSDListenProcessThread')
|
297
|
+
listen_thread = APRSDListenProcessThread(
|
305
298
|
packet_queue=threads.packet_queue,
|
306
299
|
packet_filter=packet_filter,
|
307
300
|
plugin_manager=pm,
|
308
301
|
enabled_plugins=enable_plugin,
|
309
302
|
log_packets=log_packets,
|
310
303
|
)
|
311
|
-
LOG.debug(
|
304
|
+
LOG.debug('Start APRSDListenProcessThread')
|
312
305
|
listen_thread.start()
|
313
306
|
if enable_packet_stats:
|
314
307
|
listen_stats = ListenStatsThread()
|
315
308
|
listen_stats.start()
|
316
309
|
|
317
310
|
keepalive_thread.start()
|
318
|
-
LOG.debug(
|
311
|
+
LOG.debug('keepalive Join')
|
319
312
|
keepalive_thread.join()
|
320
|
-
|
313
|
+
rx_thread.join()
|
321
314
|
listen_thread.join()
|
322
315
|
stats.join()
|
aprsd/cmds/server.py
CHANGED
@@ -147,7 +147,7 @@ def server(ctx, flush):
|
|
147
147
|
server_threads.register(keepalive.KeepAliveThread())
|
148
148
|
server_threads.register(stats_thread.APRSDStatsStoreThread())
|
149
149
|
server_threads.register(
|
150
|
-
rx.
|
150
|
+
rx.APRSDRXThread(
|
151
151
|
packet_queue=threads.packet_queue,
|
152
152
|
),
|
153
153
|
)
|