aprsd 4.1.2__py3-none-any.whl → 4.2.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/__init__.py +5 -13
- aprsd/client/client.py +141 -0
- aprsd/client/drivers/__init__.py +10 -0
- aprsd/client/drivers/aprsis.py +174 -255
- aprsd/client/drivers/fake.py +59 -11
- aprsd/client/drivers/lib/__init__.py +0 -0
- aprsd/client/drivers/lib/aprslib.py +296 -0
- aprsd/client/drivers/registry.py +86 -0
- aprsd/client/drivers/tcpkiss.py +408 -0
- aprsd/client/stats.py +2 -2
- aprsd/cmds/dev.py +0 -3
- aprsd/cmds/listen.py +3 -3
- aprsd/cmds/send_message.py +4 -4
- aprsd/cmds/server.py +4 -10
- aprsd/log/log.py +1 -1
- aprsd/main.py +0 -7
- aprsd/packets/core.py +168 -169
- aprsd/packets/log.py +69 -59
- aprsd/plugin.py +3 -2
- aprsd/plugin_utils.py +2 -2
- aprsd/plugins/weather.py +2 -2
- aprsd/stats/collector.py +5 -4
- aprsd/threads/rx.py +12 -10
- aprsd/threads/tx.py +32 -31
- aprsd/utils/keepalive_collector.py +7 -5
- {aprsd-4.1.2.dist-info → aprsd-4.2.0.dist-info}/METADATA +48 -48
- {aprsd-4.1.2.dist-info → aprsd-4.2.0.dist-info}/RECORD +32 -33
- {aprsd-4.1.2.dist-info → aprsd-4.2.0.dist-info}/WHEEL +1 -1
- aprsd/client/aprsis.py +0 -183
- aprsd/client/base.py +0 -156
- aprsd/client/drivers/kiss.py +0 -144
- aprsd/client/factory.py +0 -91
- aprsd/client/fake.py +0 -49
- aprsd/client/kiss.py +0 -143
- {aprsd-4.1.2.dist-info → aprsd-4.2.0.dist-info}/entry_points.txt +0 -0
- {aprsd-4.1.2.dist-info → aprsd-4.2.0.dist-info/licenses}/AUTHORS +0 -0
- {aprsd-4.1.2.dist-info → aprsd-4.2.0.dist-info/licenses}/LICENSE +0 -0
- {aprsd-4.1.2.dist-info → aprsd-4.2.0.dist-info}/top_level.txt +0 -0
aprsd/client/__init__.py
CHANGED
@@ -1,13 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
TRANSPORT_SERIALKISS = "serialkiss"
|
7
|
-
TRANSPORT_FAKE = "fake"
|
8
|
-
|
9
|
-
|
10
|
-
client_factory = factory.ClientFactory()
|
11
|
-
client_factory.register(aprsis.APRSISClient)
|
12
|
-
client_factory.register(kiss.KISSClient)
|
13
|
-
client_factory.register(fake.APRSDFakeClient)
|
1
|
+
# define the client transports here
|
2
|
+
TRANSPORT_APRSIS = 'aprsis'
|
3
|
+
TRANSPORT_TCPKISS = 'tcpkiss'
|
4
|
+
TRANSPORT_SERIALKISS = 'serialkiss'
|
5
|
+
TRANSPORT_FAKE = 'fake'
|
aprsd/client/client.py
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
import logging
|
2
|
+
import threading
|
3
|
+
from typing import Callable
|
4
|
+
|
5
|
+
import timeago
|
6
|
+
import wrapt
|
7
|
+
from loguru import logger
|
8
|
+
from oslo_config import cfg
|
9
|
+
|
10
|
+
from aprsd.client import drivers # noqa - ensure drivers are registered
|
11
|
+
from aprsd.client.drivers.registry import DriverRegistry
|
12
|
+
from aprsd.packets import core
|
13
|
+
from aprsd.utils import keepalive_collector
|
14
|
+
|
15
|
+
CONF = cfg.CONF
|
16
|
+
LOG = logging.getLogger('APRSD')
|
17
|
+
LOGU = logger
|
18
|
+
|
19
|
+
|
20
|
+
class APRSDClient:
|
21
|
+
"""APRSD client class.
|
22
|
+
|
23
|
+
This is a singleton class that provides a single instance of the APRSD client.
|
24
|
+
It is responsible for connecting to the appropriate APRSD client driver based on
|
25
|
+
the configuration.
|
26
|
+
|
27
|
+
"""
|
28
|
+
|
29
|
+
_instance = None
|
30
|
+
driver = None
|
31
|
+
lock = threading.Lock()
|
32
|
+
filter = None
|
33
|
+
|
34
|
+
def __new__(cls, *args, **kwargs):
|
35
|
+
"""This magic turns this into a singleton."""
|
36
|
+
if cls._instance is None:
|
37
|
+
cls._instance = super().__new__(cls)
|
38
|
+
keepalive_collector.KeepAliveCollector().register(cls)
|
39
|
+
return cls._instance
|
40
|
+
|
41
|
+
def __init__(self):
|
42
|
+
self.connected = False
|
43
|
+
self.login_status = {
|
44
|
+
'success': False,
|
45
|
+
'message': None,
|
46
|
+
}
|
47
|
+
if not self.driver:
|
48
|
+
self.driver = DriverRegistry().get_driver()
|
49
|
+
self.driver.setup_connection()
|
50
|
+
|
51
|
+
def stats(self, serializable=False) -> dict:
|
52
|
+
stats = {}
|
53
|
+
if self.driver:
|
54
|
+
stats = self.driver.stats(serializable=serializable)
|
55
|
+
return stats
|
56
|
+
|
57
|
+
@property
|
58
|
+
def is_enabled(self):
|
59
|
+
if not self.driver:
|
60
|
+
return False
|
61
|
+
return self.driver.is_enabled()
|
62
|
+
|
63
|
+
@property
|
64
|
+
def is_configured(self):
|
65
|
+
if not self.driver:
|
66
|
+
return False
|
67
|
+
return self.driver.is_configured()
|
68
|
+
|
69
|
+
# @property
|
70
|
+
# def is_connected(self):
|
71
|
+
# if not self.driver:
|
72
|
+
# return False
|
73
|
+
# return self.driver.is_connected()
|
74
|
+
|
75
|
+
@property
|
76
|
+
def login_success(self):
|
77
|
+
if not self.driver:
|
78
|
+
return False
|
79
|
+
return self.driver.login_success
|
80
|
+
|
81
|
+
@property
|
82
|
+
def login_failure(self):
|
83
|
+
if not self.driver:
|
84
|
+
return None
|
85
|
+
return self.driver.login_failure
|
86
|
+
|
87
|
+
def set_filter(self, filter):
|
88
|
+
self.filter = filter
|
89
|
+
if not self.driver:
|
90
|
+
return
|
91
|
+
self.driver.set_filter(filter)
|
92
|
+
|
93
|
+
def get_filter(self):
|
94
|
+
if not self.driver:
|
95
|
+
return None
|
96
|
+
return self.driver.filter
|
97
|
+
|
98
|
+
def is_alive(self):
|
99
|
+
return self.driver.is_alive()
|
100
|
+
|
101
|
+
def close(self):
|
102
|
+
if not self.driver:
|
103
|
+
return
|
104
|
+
self.driver.close()
|
105
|
+
|
106
|
+
@wrapt.synchronized(lock)
|
107
|
+
def reset(self):
|
108
|
+
"""Call this to force a rebuild/reconnect."""
|
109
|
+
LOG.info('Resetting client connection.')
|
110
|
+
if self.driver:
|
111
|
+
self.driver.close()
|
112
|
+
self.driver.setup_connection()
|
113
|
+
if self.filter:
|
114
|
+
self.driver.set_filter(self.filter)
|
115
|
+
else:
|
116
|
+
LOG.warning('Client not initialized, nothing to reset.')
|
117
|
+
|
118
|
+
def send(self, packet: core.Packet) -> bool:
|
119
|
+
return self.driver.send(packet)
|
120
|
+
|
121
|
+
# For the keepalive collector
|
122
|
+
def keepalive_check(self):
|
123
|
+
# Don't check the first time through.
|
124
|
+
if not self.driver.is_alive and self._checks:
|
125
|
+
LOG.warning("Resetting client. It's not alive.")
|
126
|
+
self.reset()
|
127
|
+
self._checks = True
|
128
|
+
|
129
|
+
# For the keepalive collector
|
130
|
+
def keepalive_log(self):
|
131
|
+
if ka := self.driver.keepalive:
|
132
|
+
keepalive = timeago.format(ka)
|
133
|
+
else:
|
134
|
+
keepalive = 'N/A'
|
135
|
+
LOGU.opt(colors=True).info(f'<green>Client keepalive {keepalive}</green>')
|
136
|
+
|
137
|
+
def consumer(self, callback: Callable, raw: bool = False):
|
138
|
+
return self.driver.consumer(callback=callback, raw=raw)
|
139
|
+
|
140
|
+
def decode_packet(self, *args, **kwargs) -> core.Packet:
|
141
|
+
return self.driver.decode_packet(*args, **kwargs)
|
aprsd/client/drivers/__init__.py
CHANGED
@@ -0,0 +1,10 @@
|
|
1
|
+
# All client drivers must be registered here
|
2
|
+
from aprsd.client.drivers.aprsis import APRSISDriver
|
3
|
+
from aprsd.client.drivers.fake import APRSDFakeDriver
|
4
|
+
from aprsd.client.drivers.registry import DriverRegistry
|
5
|
+
from aprsd.client.drivers.tcpkiss import TCPKISSDriver
|
6
|
+
|
7
|
+
driver_registry = DriverRegistry()
|
8
|
+
driver_registry.register(APRSDFakeDriver)
|
9
|
+
driver_registry.register(APRSISDriver)
|
10
|
+
driver_registry.register(TCPKISSDriver)
|
aprsd/client/drivers/aprsis.py
CHANGED
@@ -1,286 +1,205 @@
|
|
1
1
|
import datetime
|
2
2
|
import logging
|
3
|
-
import
|
4
|
-
import
|
5
|
-
import threading
|
3
|
+
import time
|
4
|
+
from typing import Callable
|
6
5
|
|
7
|
-
import
|
8
|
-
import
|
9
|
-
from
|
10
|
-
from aprslib.exceptions import (
|
11
|
-
ConnectionDrop,
|
12
|
-
ConnectionError,
|
13
|
-
GenericError,
|
14
|
-
LoginError,
|
15
|
-
ParseError,
|
16
|
-
UnknownFormat,
|
17
|
-
)
|
6
|
+
from aprslib.exceptions import LoginError
|
7
|
+
from loguru import logger
|
8
|
+
from oslo_config import cfg
|
18
9
|
|
19
|
-
import
|
10
|
+
from aprsd import client, exception
|
11
|
+
from aprsd.client.drivers.lib.aprslib import APRSLibClient
|
20
12
|
from aprsd.packets import core
|
21
13
|
|
14
|
+
CONF = cfg.CONF
|
22
15
|
LOG = logging.getLogger('APRSD')
|
16
|
+
LOGU = logger
|
23
17
|
|
24
18
|
|
25
|
-
class
|
26
|
-
|
19
|
+
# class APRSISDriver(metaclass=trace.TraceWrapperMetaclass):
|
20
|
+
class APRSISDriver:
|
21
|
+
"""This is the APRS-IS driver for the APRSD client.
|
27
22
|
|
28
|
-
|
29
|
-
thread_stop = False
|
23
|
+
This driver uses our modified aprslib.IS class to connect to the APRS-IS server.
|
30
24
|
|
31
|
-
|
32
|
-
aprsd_keepalive = datetime.datetime.now()
|
25
|
+
"""
|
33
26
|
|
34
|
-
|
35
|
-
|
27
|
+
_client = None
|
28
|
+
_checks = False
|
36
29
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
def close(self):
|
46
|
-
LOG.warning('Closing Aprsdis client.')
|
47
|
-
super().close()
|
48
|
-
|
49
|
-
@wrapt.synchronized(lock)
|
50
|
-
def send(self, packet: core.Packet):
|
51
|
-
"""Send an APRS Message object."""
|
52
|
-
self.sendall(packet.raw)
|
53
|
-
|
54
|
-
def is_alive(self):
|
55
|
-
"""If the connection is alive or not."""
|
56
|
-
return self._connected
|
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
|
-
)
|
30
|
+
def __init__(self):
|
31
|
+
max_timeout = {'hours': 0.0, 'minutes': 2, 'seconds': 0}
|
32
|
+
self.max_delta = datetime.timedelta(**max_timeout)
|
33
|
+
self.login_status = {
|
34
|
+
'success': False,
|
35
|
+
'message': None,
|
36
|
+
}
|
66
37
|
|
38
|
+
@staticmethod
|
39
|
+
def is_enabled():
|
40
|
+
# Defaults to True if the enabled flag is non existent
|
67
41
|
try:
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
if
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
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
|
-
|
109
|
-
def _socket_readlines(self, blocking=False):
|
110
|
-
"""
|
111
|
-
Generator for complete lines, received from the server
|
112
|
-
"""
|
113
|
-
try:
|
114
|
-
self.sock.setblocking(0)
|
115
|
-
except OSError as e:
|
116
|
-
self.logger.error(f'socket error when setblocking(0): {str(e)}')
|
117
|
-
raise aprslib.ConnectionDrop('connection dropped') from e
|
42
|
+
return CONF.aprs_network.enabled
|
43
|
+
except KeyError:
|
44
|
+
return False
|
45
|
+
|
46
|
+
@staticmethod
|
47
|
+
def is_configured():
|
48
|
+
if APRSISDriver.is_enabled():
|
49
|
+
# Ensure that the config vars are correctly set
|
50
|
+
if not CONF.aprs_network.login:
|
51
|
+
LOG.error('Config aprs_network.login not set.')
|
52
|
+
raise exception.MissingConfigOptionException(
|
53
|
+
'aprs_network.login is not set.',
|
54
|
+
)
|
55
|
+
if not CONF.aprs_network.password:
|
56
|
+
LOG.error('Config aprs_network.password not set.')
|
57
|
+
raise exception.MissingConfigOptionException(
|
58
|
+
'aprs_network.password is not set.',
|
59
|
+
)
|
60
|
+
if not CONF.aprs_network.host:
|
61
|
+
LOG.error('Config aprs_network.host not set.')
|
62
|
+
raise exception.MissingConfigOptionException(
|
63
|
+
'aprs_network.host is not set.',
|
64
|
+
)
|
118
65
|
|
119
|
-
|
120
|
-
|
121
|
-
newline = b'\r\n'
|
66
|
+
return True
|
67
|
+
return True
|
122
68
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
self.select_timeout,
|
130
|
-
)
|
131
|
-
if not readable:
|
132
|
-
if not blocking:
|
133
|
-
break
|
134
|
-
else:
|
135
|
-
continue
|
69
|
+
@property
|
70
|
+
def is_alive(self):
|
71
|
+
if not self._client:
|
72
|
+
LOG.warning(f'APRS_CLIENT {self._client} alive? NO!!!')
|
73
|
+
return False
|
74
|
+
return self._client.is_alive() and not self._is_stale_connection()
|
136
75
|
|
76
|
+
def close(self):
|
77
|
+
if self._client:
|
78
|
+
self._client.stop()
|
79
|
+
self._client.close()
|
80
|
+
|
81
|
+
def send(self, packet: core.Packet) -> bool:
|
82
|
+
return self._client.send(packet)
|
83
|
+
|
84
|
+
def setup_connection(self):
|
85
|
+
user = CONF.aprs_network.login
|
86
|
+
password = CONF.aprs_network.password
|
87
|
+
host = CONF.aprs_network.host
|
88
|
+
port = CONF.aprs_network.port
|
89
|
+
self.connected = False
|
90
|
+
backoff = 1
|
91
|
+
retries = 3
|
92
|
+
retry_count = 0
|
93
|
+
while not self.connected:
|
94
|
+
retry_count += 1
|
95
|
+
if retry_count >= retries:
|
96
|
+
break
|
137
97
|
try:
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
self.passwd,
|
170
|
-
(' filter ' + self.filter) if self.filter != '' else '',
|
171
|
-
aprsd.__version__,
|
172
|
-
)
|
173
|
-
|
174
|
-
self.logger.debug('Sending login information')
|
175
|
-
|
176
|
-
try:
|
177
|
-
self._sendall(login_str)
|
178
|
-
self.sock.settimeout(5)
|
179
|
-
test = self.sock.recv(len(login_str) + 100)
|
180
|
-
if is_py3:
|
181
|
-
test = test.decode('latin-1')
|
182
|
-
test = test.rstrip()
|
183
|
-
|
184
|
-
self.logger.debug("Server: '%s'", test)
|
185
|
-
|
186
|
-
if not test:
|
187
|
-
raise LoginError(f"Server Response Empty: '{test}'")
|
188
|
-
|
189
|
-
_, _, callsign, status, e = test.split(' ', 4)
|
190
|
-
s = e.split(',')
|
191
|
-
if len(s):
|
192
|
-
server_string = s[0].replace('server ', '')
|
193
|
-
else:
|
194
|
-
server_string = e.replace('server ', '')
|
195
|
-
|
196
|
-
if callsign == '':
|
197
|
-
raise LoginError('Server responded with empty callsign???')
|
198
|
-
if callsign != self.callsign:
|
199
|
-
raise LoginError(f'Server: {test}')
|
200
|
-
if status != 'verified,' and self.passwd != '-1':
|
201
|
-
raise LoginError('Password is incorrect')
|
98
|
+
LOG.info(
|
99
|
+
f'Creating aprslib client({host}:{port}) and logging in {user}.'
|
100
|
+
)
|
101
|
+
self._client = APRSLibClient(
|
102
|
+
user, passwd=password, host=host, port=port
|
103
|
+
)
|
104
|
+
# Force the log to be the same
|
105
|
+
self._client.logger = LOG
|
106
|
+
self._client.connect()
|
107
|
+
self.connected = self.login_status['success'] = True
|
108
|
+
self.login_status['message'] = self._client.server_string
|
109
|
+
backoff = 1
|
110
|
+
except LoginError as e:
|
111
|
+
LOG.error(f"Failed to login to APRS-IS Server '{e}'")
|
112
|
+
self.connected = self.login_status['success'] = False
|
113
|
+
self.login_status['message'] = (
|
114
|
+
e.message if hasattr(e, 'message') else str(e)
|
115
|
+
)
|
116
|
+
LOG.error(self.login_status['message'])
|
117
|
+
time.sleep(backoff)
|
118
|
+
except Exception as e:
|
119
|
+
LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
|
120
|
+
self.connected = self.login_status['success'] = False
|
121
|
+
self.login_status['message'] = getattr(e, 'message', str(e))
|
122
|
+
time.sleep(backoff)
|
123
|
+
# Don't allow the backoff to go to inifinity.
|
124
|
+
if backoff > 5:
|
125
|
+
backoff = 5
|
126
|
+
else:
|
127
|
+
backoff += 1
|
128
|
+
continue
|
202
129
|
|
203
|
-
|
204
|
-
|
205
|
-
else:
|
206
|
-
self.logger.info('Login successful')
|
130
|
+
def set_filter(self, filter):
|
131
|
+
self._client.set_filter(filter)
|
207
132
|
|
208
|
-
|
209
|
-
|
133
|
+
def login_success(self) -> bool:
|
134
|
+
return self.login_status.get('success', False)
|
210
135
|
|
211
|
-
|
212
|
-
|
213
|
-
self.close()
|
214
|
-
raise
|
215
|
-
except Exception as e:
|
216
|
-
self.close()
|
217
|
-
self.logger.error(f"Failed to login '{e}'")
|
218
|
-
self.logger.exception(e)
|
219
|
-
raise LoginError('Failed to login') from e
|
136
|
+
def login_failure(self) -> str:
|
137
|
+
return self.login_status.get('message', None)
|
220
138
|
|
221
|
-
|
222
|
-
|
223
|
-
|
139
|
+
@property
|
140
|
+
def filter(self):
|
141
|
+
return self._client.filter
|
224
142
|
|
225
|
-
|
226
|
-
|
143
|
+
@property
|
144
|
+
def server_string(self):
|
145
|
+
return self._client.server_string
|
227
146
|
|
228
|
-
|
229
|
-
|
147
|
+
@property
|
148
|
+
def keepalive(self):
|
149
|
+
return self._client.aprsd_keepalive
|
230
150
|
|
231
|
-
|
232
|
-
|
151
|
+
def _is_stale_connection(self):
|
152
|
+
delta = datetime.datetime.now() - self._client.aprsd_keepalive
|
153
|
+
if delta > self.max_delta:
|
154
|
+
LOG.error(f'Connection is stale, last heard {delta} ago.')
|
155
|
+
return True
|
156
|
+
return False
|
233
157
|
|
234
|
-
|
235
|
-
|
158
|
+
@staticmethod
|
159
|
+
def transport():
|
160
|
+
return client.TRANSPORT_APRSIS
|
236
161
|
|
237
|
-
|
162
|
+
def decode_packet(self, *args, **kwargs):
|
163
|
+
"""APRS lib already decodes this."""
|
164
|
+
return core.factory(args[0])
|
238
165
|
|
239
|
-
|
166
|
+
def consumer(self, callback: Callable, raw: bool = False):
|
167
|
+
if self._client:
|
240
168
|
try:
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
else:
|
247
|
-
callback(self._parse(line))
|
248
|
-
else:
|
249
|
-
self.logger.debug('Server: %s', line.decode('utf8'))
|
250
|
-
self.aprsd_keepalive = datetime.datetime.now()
|
251
|
-
except ParseError as exp:
|
252
|
-
self.logger.log(
|
253
|
-
11,
|
254
|
-
"%s Packet: '%s'",
|
255
|
-
exp,
|
256
|
-
exp.packet,
|
257
|
-
)
|
258
|
-
except UnknownFormat as exp:
|
259
|
-
self.logger.log(
|
260
|
-
9,
|
261
|
-
"%s Packet: '%s'",
|
262
|
-
exp,
|
263
|
-
exp.packet,
|
169
|
+
self._client.consumer(
|
170
|
+
callback,
|
171
|
+
blocking=False,
|
172
|
+
immortal=False,
|
173
|
+
raw=raw,
|
264
174
|
)
|
265
|
-
except
|
266
|
-
|
267
|
-
|
268
|
-
raise
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
175
|
+
except Exception as e:
|
176
|
+
LOG.error(e)
|
177
|
+
LOG.info(e.__cause__)
|
178
|
+
raise e
|
179
|
+
else:
|
180
|
+
LOG.warning('client is None, might be resetting.')
|
181
|
+
self.connected = False
|
182
|
+
|
183
|
+
def stats(self, serializable=False) -> dict:
|
184
|
+
stats = {}
|
185
|
+
if self.is_configured():
|
186
|
+
if self._client:
|
187
|
+
keepalive = self._client.aprsd_keepalive
|
188
|
+
server_string = self._client.server_string
|
189
|
+
if serializable:
|
190
|
+
keepalive = keepalive.isoformat()
|
191
|
+
filter = self.filter
|
192
|
+
else:
|
193
|
+
keepalive = 'None'
|
194
|
+
server_string = 'None'
|
195
|
+
filter = 'None'
|
196
|
+
stats = {
|
197
|
+
'connected': self.is_alive,
|
198
|
+
'filter': filter,
|
199
|
+
'login_status': self.login_status,
|
200
|
+
'connection_keepalive': keepalive,
|
201
|
+
'server_string': server_string,
|
202
|
+
'transport': self.transport(),
|
203
|
+
}
|
204
|
+
|
205
|
+
return stats
|