aprsd 4.0.2__py3-none-any.whl → 4.1.1__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/cli_helper.py +36 -35
- aprsd/client/base.py +14 -11
- 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/send_message.py +30 -28
- aprsd/cmds/server.py +29 -64
- aprsd/conf/common.py +100 -101
- aprsd/conf/log.py +32 -22
- aprsd/log/log.py +31 -18
- aprsd/main.py +22 -22
- 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/plugin.py +52 -52
- aprsd/plugin_utils.py +20 -21
- aprsd/plugins/weather.py +110 -109
- aprsd/threads/__init__.py +1 -2
- aprsd/threads/rx.py +83 -75
- aprsd/threads/service.py +42 -0
- aprsd/threads/stats.py +4 -9
- aprsd/utils/objectstore.py +12 -13
- {aprsd-4.0.2.dist-info → aprsd-4.1.1.dist-info}/METADATA +22 -20
- {aprsd-4.0.2.dist-info → aprsd-4.1.1.dist-info}/RECORD +34 -29
- {aprsd-4.0.2.dist-info → aprsd-4.1.1.dist-info}/WHEEL +1 -1
- {aprsd-4.0.2.dist-info → aprsd-4.1.1.dist-info}/AUTHORS +0 -0
- {aprsd-4.0.2.dist-info → aprsd-4.1.1.dist-info}/LICENSE +0 -0
- {aprsd-4.0.2.dist-info → aprsd-4.1.1.dist-info}/entry_points.txt +0 -0
- {aprsd-4.0.2.dist-info → aprsd-4.1.1.dist-info}/top_level.txt +0 -0
aprsd/cli_helper.py
CHANGED
@@ -13,35 +13,35 @@ from aprsd.utils import trace
|
|
13
13
|
|
14
14
|
CONF = cfg.CONF
|
15
15
|
home = str(Path.home())
|
16
|
-
DEFAULT_CONFIG_DIR = f
|
17
|
-
DEFAULT_SAVE_FILE = f
|
18
|
-
DEFAULT_CONFIG_FILE = f
|
16
|
+
DEFAULT_CONFIG_DIR = f'{home}/.config/aprsd/'
|
17
|
+
DEFAULT_SAVE_FILE = f'{home}/.config/aprsd/aprsd.p'
|
18
|
+
DEFAULT_CONFIG_FILE = f'{home}/.config/aprsd/aprsd.conf'
|
19
19
|
|
20
20
|
|
21
|
-
F = t.TypeVar(
|
21
|
+
F = t.TypeVar('F', bound=t.Callable[..., t.Any])
|
22
22
|
|
23
23
|
common_options = [
|
24
24
|
click.option(
|
25
|
-
|
26
|
-
default=
|
25
|
+
'--loglevel',
|
26
|
+
default='INFO',
|
27
27
|
show_default=True,
|
28
28
|
type=click.Choice(
|
29
|
-
[
|
29
|
+
['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'],
|
30
30
|
case_sensitive=False,
|
31
31
|
),
|
32
32
|
show_choices=True,
|
33
|
-
help=
|
33
|
+
help='The log level to use for aprsd.log',
|
34
34
|
),
|
35
35
|
click.option(
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
'-c',
|
37
|
+
'--config',
|
38
|
+
'config_file',
|
39
39
|
show_default=True,
|
40
40
|
default=DEFAULT_CONFIG_FILE,
|
41
|
-
help=
|
41
|
+
help='The aprsd config file to use for options.',
|
42
42
|
),
|
43
43
|
click.option(
|
44
|
-
|
44
|
+
'--quiet',
|
45
45
|
is_flag=True,
|
46
46
|
default=False,
|
47
47
|
help="Don't log to stdout",
|
@@ -59,7 +59,7 @@ class AliasedGroup(click.Group):
|
|
59
59
|
"""
|
60
60
|
|
61
61
|
def decorator(f):
|
62
|
-
aliases = kwargs.pop(
|
62
|
+
aliases = kwargs.pop('aliases', [])
|
63
63
|
cmd = click.decorators.command(*args, **kwargs)(f)
|
64
64
|
self.add_command(cmd)
|
65
65
|
for alias in aliases:
|
@@ -77,7 +77,7 @@ class AliasedGroup(click.Group):
|
|
77
77
|
"""
|
78
78
|
|
79
79
|
def decorator(f):
|
80
|
-
aliases = kwargs.pop(
|
80
|
+
aliases = kwargs.pop('aliases', [])
|
81
81
|
cmd = click.decorators.group(*args, **kwargs)(f)
|
82
82
|
self.add_command(cmd)
|
83
83
|
for alias in aliases:
|
@@ -101,36 +101,37 @@ def process_standard_options(f: F) -> F:
|
|
101
101
|
ctx = args[0]
|
102
102
|
ctx.ensure_object(dict)
|
103
103
|
config_file_found = True
|
104
|
-
if kwargs[
|
105
|
-
default_config_files = [kwargs[
|
104
|
+
if kwargs['config_file']:
|
105
|
+
default_config_files = [kwargs['config_file']]
|
106
106
|
else:
|
107
107
|
default_config_files = None
|
108
|
+
|
108
109
|
try:
|
109
110
|
CONF(
|
110
111
|
[],
|
111
|
-
project=
|
112
|
+
project='aprsd',
|
112
113
|
version=aprsd.__version__,
|
113
114
|
default_config_files=default_config_files,
|
114
115
|
)
|
115
116
|
except cfg.ConfigFilesNotFoundError:
|
116
117
|
config_file_found = False
|
117
|
-
ctx.obj[
|
118
|
+
ctx.obj['loglevel'] = kwargs['loglevel']
|
118
119
|
# ctx.obj["config_file"] = kwargs["config_file"]
|
119
|
-
ctx.obj[
|
120
|
+
ctx.obj['quiet'] = kwargs['quiet']
|
120
121
|
log.setup_logging(
|
121
|
-
ctx.obj[
|
122
|
-
ctx.obj[
|
122
|
+
ctx.obj['loglevel'],
|
123
|
+
ctx.obj['quiet'],
|
123
124
|
)
|
124
125
|
if CONF.trace_enabled:
|
125
|
-
trace.setup_tracing([
|
126
|
+
trace.setup_tracing(['method', 'api'])
|
126
127
|
|
127
128
|
if not config_file_found:
|
128
|
-
LOG = logging.getLogger(
|
129
|
+
LOG = logging.getLogger('APRSD') # noqa: N806
|
129
130
|
LOG.error("No config file found!! run 'aprsd sample-config'")
|
130
131
|
|
131
|
-
del kwargs[
|
132
|
-
del kwargs[
|
133
|
-
del kwargs[
|
132
|
+
del kwargs['loglevel']
|
133
|
+
del kwargs['config_file']
|
134
|
+
del kwargs['quiet']
|
134
135
|
return f(*args, **kwargs)
|
135
136
|
|
136
137
|
return update_wrapper(t.cast(F, new_func), f)
|
@@ -142,17 +143,17 @@ def process_standard_options_no_config(f: F) -> F:
|
|
142
143
|
def new_func(*args, **kwargs):
|
143
144
|
ctx = args[0]
|
144
145
|
ctx.ensure_object(dict)
|
145
|
-
ctx.obj[
|
146
|
-
ctx.obj[
|
147
|
-
ctx.obj[
|
146
|
+
ctx.obj['loglevel'] = kwargs['loglevel']
|
147
|
+
ctx.obj['config_file'] = kwargs['config_file']
|
148
|
+
ctx.obj['quiet'] = kwargs['quiet']
|
148
149
|
log.setup_logging(
|
149
|
-
ctx.obj[
|
150
|
-
ctx.obj[
|
150
|
+
ctx.obj['loglevel'],
|
151
|
+
ctx.obj['quiet'],
|
151
152
|
)
|
152
153
|
|
153
|
-
del kwargs[
|
154
|
-
del kwargs[
|
155
|
-
del kwargs[
|
154
|
+
del kwargs['loglevel']
|
155
|
+
del kwargs['config_file']
|
156
|
+
del kwargs['quiet']
|
156
157
|
return f(*args, **kwargs)
|
157
158
|
|
158
159
|
return update_wrapper(t.cast(F, new_func), f)
|
aprsd/client/base.py
CHANGED
@@ -9,7 +9,7 @@ from aprsd.packets import core
|
|
9
9
|
from aprsd.utils import keepalive_collector
|
10
10
|
|
11
11
|
CONF = cfg.CONF
|
12
|
-
LOG = logging.getLogger(
|
12
|
+
LOG = logging.getLogger('APRSD')
|
13
13
|
|
14
14
|
|
15
15
|
class APRSClient:
|
@@ -20,8 +20,8 @@ class APRSClient:
|
|
20
20
|
|
21
21
|
connected = False
|
22
22
|
login_status = {
|
23
|
-
|
24
|
-
|
23
|
+
'success': False,
|
24
|
+
'message': None,
|
25
25
|
}
|
26
26
|
filter = None
|
27
27
|
lock = threading.Lock()
|
@@ -59,17 +59,20 @@ class APRSClient:
|
|
59
59
|
|
60
60
|
@property
|
61
61
|
def login_success(self):
|
62
|
-
return self.login_status.get(
|
62
|
+
return self.login_status.get('success', False)
|
63
63
|
|
64
64
|
@property
|
65
65
|
def login_failure(self):
|
66
|
-
return self.login_status[
|
66
|
+
return self.login_status['message']
|
67
67
|
|
68
68
|
def set_filter(self, filter):
|
69
69
|
self.filter = filter
|
70
70
|
if self._client:
|
71
71
|
self._client.set_filter(filter)
|
72
72
|
|
73
|
+
def get_filter(self):
|
74
|
+
return self.filter
|
75
|
+
|
73
76
|
@property
|
74
77
|
def client(self):
|
75
78
|
if not self._client:
|
@@ -80,16 +83,16 @@ class APRSClient:
|
|
80
83
|
try:
|
81
84
|
self._client = self.setup_connection()
|
82
85
|
if self.filter:
|
83
|
-
LOG.info(
|
86
|
+
LOG.info('Creating APRS client filter')
|
84
87
|
self._client.set_filter(self.filter)
|
85
88
|
except Exception as e:
|
86
|
-
LOG.error(f
|
89
|
+
LOG.error(f'Failed to create APRS client: {e}')
|
87
90
|
self._client = None
|
88
91
|
raise
|
89
92
|
|
90
93
|
def stop(self):
|
91
94
|
if self._client:
|
92
|
-
LOG.info(
|
95
|
+
LOG.info('Stopping client connection.')
|
93
96
|
self._client.stop()
|
94
97
|
|
95
98
|
def send(self, packet: core.Packet) -> None:
|
@@ -103,16 +106,16 @@ class APRSClient:
|
|
103
106
|
@wrapt.synchronized(lock)
|
104
107
|
def reset(self) -> None:
|
105
108
|
"""Call this to force a rebuild/reconnect."""
|
106
|
-
LOG.info(
|
109
|
+
LOG.info('Resetting client connection.')
|
107
110
|
if self._client:
|
108
111
|
self._client.close()
|
109
112
|
del self._client
|
110
113
|
self._create_client()
|
111
114
|
else:
|
112
|
-
LOG.warning(
|
115
|
+
LOG.warning('Client not initialized, nothing to reset.')
|
113
116
|
|
114
117
|
# Recreate the client
|
115
|
-
LOG.info(f
|
118
|
+
LOG.info(f'Creating new client {self.client}')
|
116
119
|
|
117
120
|
@abc.abstractmethod
|
118
121
|
def setup_connection(self):
|
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