aprsd 3.4.3__py3-none-any.whl → 4.0.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/cli_helper.py +12 -5
- aprsd/client/aprsis.py +68 -17
- aprsd/client/base.py +60 -12
- aprsd/client/drivers/aprsis.py +11 -5
- aprsd/client/drivers/fake.py +15 -20
- aprsd/client/factory.py +6 -3
- aprsd/client/fake.py +5 -4
- aprsd/client/kiss.py +43 -7
- aprsd/client/stats.py +2 -22
- aprsd/cmds/completion.py +7 -4
- aprsd/cmds/dev.py +39 -43
- aprsd/cmds/fetch_stats.py +221 -69
- aprsd/cmds/healthcheck.py +7 -5
- aprsd/cmds/list_plugins.py +140 -134
- aprsd/cmds/listen.py +102 -11
- aprsd/cmds/server.py +71 -37
- aprsd/conf/__init__.py +1 -2
- aprsd/conf/client.py +3 -4
- aprsd/conf/common.py +29 -92
- aprsd/conf/log.py +2 -4
- aprsd/conf/opts.py +5 -4
- aprsd/conf/plugin_common.py +11 -121
- aprsd/exception.py +2 -0
- aprsd/log/log.py +7 -46
- aprsd/main.py +19 -9
- aprsd/packets/__init__.py +14 -4
- aprsd/packets/core.py +82 -91
- aprsd/packets/log.py +8 -8
- aprsd/packets/packet_list.py +18 -25
- aprsd/plugin.py +33 -15
- aprsd/plugins/fortune.py +2 -2
- aprsd/plugins/notify.py +1 -4
- aprsd/plugins/weather.py +10 -8
- aprsd/stats/__init__.py +0 -2
- aprsd/stats/collector.py +11 -3
- aprsd/threads/__init__.py +3 -2
- aprsd/threads/aprsd.py +57 -10
- aprsd/threads/{keep_alive.py → keepalive.py} +14 -37
- aprsd/threads/registry.py +3 -3
- aprsd/threads/rx.py +48 -17
- aprsd/threads/stats.py +2 -2
- aprsd/threads/tx.py +34 -10
- aprsd/utils/__init__.py +62 -15
- aprsd/utils/counter.py +15 -12
- aprsd/utils/json.py +9 -4
- aprsd/utils/keepalive_collector.py +55 -0
- aprsd/utils/trace.py +4 -4
- aprsd-4.0.0.dist-info/AUTHORS +1 -0
- aprsd-4.0.0.dist-info/METADATA +293 -0
- aprsd-4.0.0.dist-info/RECORD +74 -0
- {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/WHEEL +1 -1
- aprsd/cmds/webchat.py +0 -674
- aprsd/conf/plugin_email.py +0 -105
- aprsd/plugins/email.py +0 -709
- aprsd/plugins/location.py +0 -179
- aprsd/threads/log_monitor.py +0 -121
- aprsd/web/__init__.py +0 -0
- aprsd/web/admin/__init__.py +0 -0
- aprsd/web/admin/static/css/index.css +0 -84
- aprsd/web/admin/static/css/prism.css +0 -4
- aprsd/web/admin/static/css/tabs.css +0 -35
- aprsd/web/admin/static/images/Untitled.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-16-0.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-16-1.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-64-0.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-64-1.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-64-2.png +0 -0
- aprsd/web/admin/static/js/charts.js +0 -235
- aprsd/web/admin/static/js/echarts.js +0 -465
- aprsd/web/admin/static/js/logs.js +0 -26
- aprsd/web/admin/static/js/main.js +0 -231
- aprsd/web/admin/static/js/prism.js +0 -12
- aprsd/web/admin/static/js/send-message.js +0 -114
- aprsd/web/admin/static/js/tabs.js +0 -28
- aprsd/web/admin/templates/index.html +0 -196
- aprsd/web/chat/static/css/chat.css +0 -115
- aprsd/web/chat/static/css/index.css +0 -66
- aprsd/web/chat/static/css/style.css.map +0 -1
- aprsd/web/chat/static/css/tabs.css +0 -41
- aprsd/web/chat/static/css/upstream/bootstrap.min.css +0 -6
- aprsd/web/chat/static/css/upstream/font.woff2 +0 -0
- aprsd/web/chat/static/css/upstream/google-fonts.css +0 -23
- aprsd/web/chat/static/css/upstream/jquery-ui.css +0 -1311
- aprsd/web/chat/static/css/upstream/jquery.toast.css +0 -28
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff2 +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff2 +0 -0
- aprsd/web/chat/static/images/Untitled.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-16-0.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-16-1.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-64-0.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-64-1.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-64-2.png +0 -0
- aprsd/web/chat/static/images/globe.svg +0 -3
- aprsd/web/chat/static/js/gps.js +0 -84
- aprsd/web/chat/static/js/main.js +0 -45
- aprsd/web/chat/static/js/send-message.js +0 -585
- aprsd/web/chat/static/js/tabs.js +0 -28
- aprsd/web/chat/static/js/upstream/bootstrap.bundle.min.js +0 -7
- aprsd/web/chat/static/js/upstream/jquery-3.7.1.min.js +0 -2
- aprsd/web/chat/static/js/upstream/jquery-ui.min.js +0 -13
- aprsd/web/chat/static/js/upstream/jquery.toast.js +0 -374
- aprsd/web/chat/static/js/upstream/semantic.min.js +0 -11
- aprsd/web/chat/static/js/upstream/socket.io.min.js +0 -7
- aprsd/web/chat/templates/index.html +0 -139
- aprsd/wsgi.py +0 -315
- aprsd-3.4.3.dist-info/AUTHORS +0 -13
- aprsd-3.4.3.dist-info/METADATA +0 -793
- aprsd-3.4.3.dist-info/RECORD +0 -133
- {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/LICENSE +0 -0
- {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/entry_points.txt +0 -0
- {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/top_level.txt +0 -0
aprsd/log/log.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
import logging
|
2
|
-
from logging.handlers import QueueHandler
|
3
2
|
import queue
|
4
3
|
import sys
|
5
4
|
|
@@ -8,7 +7,6 @@ from oslo_config import cfg
|
|
8
7
|
|
9
8
|
from aprsd.conf import log as conf_log
|
10
9
|
|
11
|
-
|
12
10
|
CONF = cfg.CONF
|
13
11
|
# LOG = logging.getLogger("APRSD")
|
14
12
|
LOG = logger
|
@@ -19,6 +17,7 @@ class QueueLatest(queue.Queue):
|
|
19
17
|
|
20
18
|
This prevents the queue from blowing up in size.
|
21
19
|
"""
|
20
|
+
|
22
21
|
def put(self, *args, **kwargs):
|
23
22
|
try:
|
24
23
|
super().put(*args, **kwargs)
|
@@ -44,7 +43,9 @@ class InterceptHandler(logging.Handler):
|
|
44
43
|
frame = frame.f_back
|
45
44
|
depth += 1
|
46
45
|
|
47
|
-
logger.opt(depth=depth, exception=record.exc_info).log(
|
46
|
+
logger.opt(depth=depth, exception=record.exc_info).log(
|
47
|
+
level, record.getMessage()
|
48
|
+
)
|
48
49
|
|
49
50
|
|
50
51
|
# Setup the log faciility
|
@@ -60,43 +61,18 @@ def setup_logging(loglevel=None, quiet=False):
|
|
60
61
|
logging.root.handlers = [InterceptHandler()]
|
61
62
|
logging.root.setLevel(log_level)
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
"imapclient.util",
|
66
|
-
]
|
67
|
-
aprslib_list = [
|
64
|
+
# We don't really want to see the aprslib parsing debug output.
|
65
|
+
disable_list = [
|
68
66
|
"aprslib",
|
69
67
|
"aprslib.parsing",
|
70
68
|
"aprslib.exceptions",
|
71
69
|
]
|
72
|
-
webserver_list = [
|
73
|
-
"werkzeug",
|
74
|
-
"werkzeug._internal",
|
75
|
-
"socketio",
|
76
|
-
"urllib3.connectionpool",
|
77
|
-
"chardet",
|
78
|
-
"chardet.charsetgroupprober",
|
79
|
-
"chardet.eucjpprober",
|
80
|
-
"chardet.mbcharsetprober",
|
81
|
-
]
|
82
|
-
|
83
|
-
# We don't really want to see the aprslib parsing debug output.
|
84
|
-
disable_list = imap_list + aprslib_list + webserver_list
|
85
70
|
|
86
71
|
# remove every other logger's handlers
|
87
72
|
# and propagate to root logger
|
88
73
|
for name in logging.root.manager.loggerDict.keys():
|
89
74
|
logging.getLogger(name).handlers = []
|
90
|
-
|
91
|
-
logging.getLogger(name).propagate = False
|
92
|
-
else:
|
93
|
-
logging.getLogger(name).propagate = True
|
94
|
-
|
95
|
-
if CONF.webchat.disable_url_request_logging:
|
96
|
-
for name in webserver_list:
|
97
|
-
logging.getLogger(name).handlers = []
|
98
|
-
logging.getLogger(name).propagate = True
|
99
|
-
logging.getLogger(name).setLevel(logging.ERROR)
|
75
|
+
logging.getLogger(name).propagate = name not in disable_list
|
100
76
|
|
101
77
|
handlers = [
|
102
78
|
{
|
@@ -118,21 +94,6 @@ def setup_logging(loglevel=None, quiet=False):
|
|
118
94
|
},
|
119
95
|
)
|
120
96
|
|
121
|
-
if CONF.email_plugin.enabled and CONF.email_plugin.debug:
|
122
|
-
for name in imap_list:
|
123
|
-
logging.getLogger(name).propagate = True
|
124
|
-
|
125
|
-
if CONF.admin.web_enabled:
|
126
|
-
qh = QueueHandler(logging_queue)
|
127
|
-
handlers.append(
|
128
|
-
{
|
129
|
-
"sink": qh, "serialize": False,
|
130
|
-
"format": CONF.logging.logformat,
|
131
|
-
"level": log_level,
|
132
|
-
"colorize": False,
|
133
|
-
},
|
134
|
-
)
|
135
|
-
|
136
97
|
# configure loguru
|
137
98
|
logger.configure(handlers=handlers)
|
138
99
|
logger.level("DEBUG", color="<fg #BABABA>")
|
aprsd/main.py
CHANGED
@@ -22,11 +22,11 @@
|
|
22
22
|
# python included libs
|
23
23
|
import datetime
|
24
24
|
import importlib.metadata as imp
|
25
|
-
from importlib.metadata import version as metadata_version
|
26
25
|
import logging
|
27
26
|
import signal
|
28
27
|
import sys
|
29
28
|
import time
|
29
|
+
from importlib.metadata import version as metadata_version
|
30
30
|
|
31
31
|
import click
|
32
32
|
from oslo_config import cfg, generator
|
@@ -36,7 +36,6 @@ import aprsd
|
|
36
36
|
from aprsd import cli_helper, packets, threads, utils
|
37
37
|
from aprsd.stats import collector
|
38
38
|
|
39
|
-
|
40
39
|
# setup the global logger
|
41
40
|
# log.basicConfig(level=log.DEBUG) # level=10
|
42
41
|
CONF = cfg.CONF
|
@@ -54,8 +53,14 @@ def cli(ctx):
|
|
54
53
|
|
55
54
|
def load_commands():
|
56
55
|
from .cmds import ( # noqa
|
57
|
-
completion,
|
58
|
-
|
56
|
+
completion,
|
57
|
+
dev,
|
58
|
+
fetch_stats,
|
59
|
+
healthcheck,
|
60
|
+
list_plugins,
|
61
|
+
listen,
|
62
|
+
send_message,
|
63
|
+
server,
|
59
64
|
)
|
60
65
|
|
61
66
|
|
@@ -79,11 +84,15 @@ def signal_handler(sig, frame):
|
|
79
84
|
),
|
80
85
|
)
|
81
86
|
time.sleep(1.5)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
+
try:
|
88
|
+
packets.PacketTrack().save()
|
89
|
+
packets.WatchList().save()
|
90
|
+
packets.SeenList().save()
|
91
|
+
packets.PacketList().save()
|
92
|
+
collector.Collector().collect()
|
93
|
+
except Exception as e:
|
94
|
+
LOG.error(f"Failed to save data: {e}")
|
95
|
+
sys.exit(0)
|
87
96
|
# signal.signal(signal.SIGTERM, sys.exit(0))
|
88
97
|
# sys.exit(0)
|
89
98
|
|
@@ -111,6 +120,7 @@ def sample_config(ctx):
|
|
111
120
|
|
112
121
|
def _get_selected_entry_points():
|
113
122
|
import sys
|
123
|
+
|
114
124
|
if sys.version_info < (3, 10):
|
115
125
|
all = imp.entry_points()
|
116
126
|
selected = []
|
aprsd/packets/__init__.py
CHANGED
@@ -1,15 +1,25 @@
|
|
1
1
|
from aprsd.packets import collector
|
2
2
|
from aprsd.packets.core import ( # noqa: F401
|
3
|
-
AckPacket,
|
4
|
-
|
5
|
-
|
3
|
+
AckPacket,
|
4
|
+
BeaconPacket,
|
5
|
+
BulletinPacket,
|
6
|
+
GPSPacket,
|
7
|
+
MessagePacket,
|
8
|
+
MicEPacket,
|
9
|
+
ObjectPacket,
|
10
|
+
Packet,
|
11
|
+
RejectPacket,
|
12
|
+
StatusPacket,
|
13
|
+
ThirdPartyPacket,
|
14
|
+
UnknownPacket,
|
15
|
+
WeatherPacket,
|
16
|
+
factory,
|
6
17
|
)
|
7
18
|
from aprsd.packets.packet_list import PacketList # noqa: F401
|
8
19
|
from aprsd.packets.seen_list import SeenList # noqa: F401
|
9
20
|
from aprsd.packets.tracker import PacketTrack # noqa: F401
|
10
21
|
from aprsd.packets.watch_list import WatchList # noqa: F401
|
11
22
|
|
12
|
-
|
13
23
|
# Register all the packet tracking objects.
|
14
24
|
collector.PacketCollector().register(PacketList)
|
15
25
|
collector.PacketCollector().register(SeenList)
|
aprsd/packets/core.py
CHANGED
@@ -1,20 +1,23 @@
|
|
1
|
-
from dataclasses import dataclass, field
|
2
|
-
from datetime import datetime
|
3
1
|
import logging
|
4
2
|
import re
|
5
3
|
import time
|
4
|
+
from dataclasses import dataclass, field
|
5
|
+
from datetime import datetime
|
6
|
+
|
6
7
|
# Due to a failure in python 3.8
|
7
8
|
from typing import Any, List, Optional, Type, TypeVar, Union
|
8
9
|
|
9
10
|
from aprslib import util as aprslib_util
|
10
11
|
from dataclasses_json import (
|
11
|
-
CatchAll,
|
12
|
+
CatchAll,
|
13
|
+
DataClassJsonMixin,
|
14
|
+
Undefined,
|
15
|
+
dataclass_json,
|
12
16
|
)
|
13
17
|
from loguru import logger
|
14
18
|
|
15
19
|
from aprsd.utils import counter
|
16
20
|
|
17
|
-
|
18
21
|
# For mypy to be happy
|
19
22
|
A = TypeVar("A", bound="DataClassJsonMixin")
|
20
23
|
Json = Union[dict, list, str, int, float, bool, None]
|
@@ -51,7 +54,7 @@ def _init_send_time():
|
|
51
54
|
return NO_DATE
|
52
55
|
|
53
56
|
|
54
|
-
def _init_msgNo():
|
57
|
+
def _init_msgNo(): # noqa: N802
|
55
58
|
"""For some reason __post__init doesn't get called.
|
56
59
|
|
57
60
|
So in order to initialize the msgNo field in the packet
|
@@ -63,15 +66,11 @@ def _init_msgNo(): # noqa: N802
|
|
63
66
|
|
64
67
|
|
65
68
|
def _translate_fields(raw: dict) -> dict:
|
66
|
-
|
67
|
-
|
68
|
-
"
|
69
|
-
|
70
|
-
|
71
|
-
for key in translate_fields:
|
72
|
-
if key in raw:
|
73
|
-
raw[translate_fields[key]] = raw[key]
|
74
|
-
del raw[key]
|
69
|
+
# Direct key checks instead of iteration
|
70
|
+
if "from" in raw:
|
71
|
+
raw["from_call"] = raw.pop("from")
|
72
|
+
if "to" in raw:
|
73
|
+
raw["to_call"] = raw.pop("to")
|
75
74
|
|
76
75
|
# addresse overrides to_call
|
77
76
|
if "addresse" in raw:
|
@@ -88,14 +87,16 @@ class Packet:
|
|
88
87
|
to_call: Optional[str] = field(default=None)
|
89
88
|
addresse: Optional[str] = field(default=None)
|
90
89
|
format: Optional[str] = field(default=None)
|
91
|
-
msgNo: Optional[str] = field(default=None)
|
92
|
-
ackMsgNo: Optional[str] = field(default=None)
|
90
|
+
msgNo: Optional[str] = field(default=None) # noqa: N815
|
91
|
+
ackMsgNo: Optional[str] = field(default=None) # noqa: N815
|
93
92
|
packet_type: Optional[str] = field(default=None)
|
94
93
|
timestamp: float = field(default_factory=_init_timestamp, compare=False, hash=False)
|
95
94
|
# Holds the raw text string to be sent over the wire
|
96
95
|
# or holds the raw string from input packet
|
97
96
|
raw: Optional[str] = field(default=None, compare=False, hash=False)
|
98
|
-
raw_dict: dict = field(
|
97
|
+
raw_dict: dict = field(
|
98
|
+
repr=False, default_factory=lambda: {}, compare=False, hash=False
|
99
|
+
)
|
99
100
|
# Built by calling prepare(). raw needs this built first.
|
100
101
|
payload: Optional[str] = field(default=None)
|
101
102
|
|
@@ -103,6 +104,8 @@ class Packet:
|
|
103
104
|
send_count: int = field(repr=False, default=0, compare=False, hash=False)
|
104
105
|
retry_count: int = field(repr=False, default=3, compare=False, hash=False)
|
105
106
|
last_send_time: float = field(repr=False, default=0, compare=False, hash=False)
|
107
|
+
# Was the packet acked?
|
108
|
+
acked: bool = field(repr=False, default=False, compare=False, hash=False)
|
106
109
|
|
107
110
|
# Do we allow this packet to be saved to send later?
|
108
111
|
allow_delay: bool = field(repr=False, default=True, compare=False, hash=False)
|
@@ -110,11 +113,7 @@ class Packet:
|
|
110
113
|
via: Optional[str] = field(default=None, compare=False, hash=False)
|
111
114
|
|
112
115
|
def get(self, key: str, default: Optional[str] = None):
|
113
|
-
|
114
|
-
if hasattr(self, key):
|
115
|
-
return getattr(self, key)
|
116
|
-
else:
|
117
|
-
return default
|
116
|
+
return getattr(self, key, default)
|
118
117
|
|
119
118
|
@property
|
120
119
|
def key(self) -> str:
|
@@ -135,10 +134,10 @@ class Packet:
|
|
135
134
|
msg = self._filter_for_send(self.raw).rstrip("\n")
|
136
135
|
return msg
|
137
136
|
|
138
|
-
def prepare(self) -> None:
|
137
|
+
def prepare(self, create_msg_number=False) -> None:
|
139
138
|
"""Do stuff here that is needed prior to sending over the air."""
|
140
139
|
# now build the raw message for sending
|
141
|
-
if not self.msgNo:
|
140
|
+
if not self.msgNo and create_msg_number:
|
142
141
|
self.msgNo = _init_msgNo()
|
143
142
|
self._build_payload()
|
144
143
|
self._build_raw()
|
@@ -146,12 +145,12 @@ class Packet:
|
|
146
145
|
def _build_payload(self) -> None:
|
147
146
|
"""The payload is the non headers portion of the packet."""
|
148
147
|
if not self.to_call:
|
149
|
-
raise ValueError(
|
148
|
+
raise ValueError(
|
149
|
+
"to_call isn't set. Must set to_call before calling prepare()"
|
150
|
+
)
|
150
151
|
|
151
152
|
# The base packet class has no real payload
|
152
|
-
self.payload = (
|
153
|
-
f":{self.to_call.ljust(9)}"
|
154
|
-
)
|
153
|
+
self.payload = f":{self.to_call.ljust(9)}"
|
155
154
|
|
156
155
|
def _build_raw(self) -> None:
|
157
156
|
"""Build the self.raw which is what is sent over the air."""
|
@@ -172,8 +171,10 @@ class Packet:
|
|
172
171
|
message = msg[:67]
|
173
172
|
# We all miss George Carlin
|
174
173
|
return re.sub(
|
175
|
-
"fuck|shit|cunt|piss|cock|bitch",
|
176
|
-
|
174
|
+
"fuck|shit|cunt|piss|cock|bitch",
|
175
|
+
"****",
|
176
|
+
message,
|
177
|
+
flags=re.IGNORECASE,
|
177
178
|
)
|
178
179
|
|
179
180
|
def __str__(self) -> str:
|
@@ -220,10 +221,7 @@ class BulletinPacket(Packet):
|
|
220
221
|
return f"BLN{self.bid} {self.message_text}"
|
221
222
|
|
222
223
|
def _build_payload(self) -> None:
|
223
|
-
self.payload =
|
224
|
-
f":BLN{self.bid:<9}"
|
225
|
-
f":{self.message_text}"
|
226
|
-
)
|
224
|
+
self.payload = f":BLN{self.bid:<9}" f":{self.message_text}"
|
227
225
|
|
228
226
|
|
229
227
|
@dataclass_json
|
@@ -252,11 +250,17 @@ class MessagePacket(Packet):
|
|
252
250
|
return self._filter_for_send(self.message_text).rstrip("\n")
|
253
251
|
|
254
252
|
def _build_payload(self):
|
255
|
-
self.
|
256
|
-
self.
|
257
|
-
|
258
|
-
|
259
|
-
|
253
|
+
if self.msgNo:
|
254
|
+
self.payload = ":{}:{}{{{}".format(
|
255
|
+
self.to_call.ljust(9),
|
256
|
+
self._filter_for_send(self.message_text).rstrip("\n"),
|
257
|
+
str(self.msgNo),
|
258
|
+
)
|
259
|
+
else:
|
260
|
+
self.payload = ":{}:{}".format(
|
261
|
+
self.to_call.ljust(9),
|
262
|
+
self._filter_for_send(self.message_text).rstrip("\n"),
|
263
|
+
)
|
260
264
|
|
261
265
|
|
262
266
|
@dataclass_json
|
@@ -335,10 +339,7 @@ class GPSPacket(Packet):
|
|
335
339
|
self.payload = "".join(payload)
|
336
340
|
|
337
341
|
def _build_raw(self):
|
338
|
-
self.raw =
|
339
|
-
f"{self.from_call}>{self.to_call},WIDE2-1:"
|
340
|
-
f"{self.payload}"
|
341
|
-
)
|
342
|
+
self.raw = f"{self.from_call}>{self.to_call},WIDE2-1:" f"{self.payload}"
|
342
343
|
|
343
344
|
@property
|
344
345
|
def human_info(self) -> str:
|
@@ -370,10 +371,7 @@ class BeaconPacket(GPSPacket):
|
|
370
371
|
lat = aprslib_util.latitude_to_ddm(self.latitude)
|
371
372
|
lon = aprslib_util.longitude_to_ddm(self.longitude)
|
372
373
|
|
373
|
-
self.payload =
|
374
|
-
f"@{time_zulu}z{lat}{self.symbol_table}"
|
375
|
-
f"{lon}"
|
376
|
-
)
|
374
|
+
self.payload = f"@{time_zulu}z{lat}{self.symbol_table}" f"{lon}"
|
377
375
|
|
378
376
|
if self.comment:
|
379
377
|
comment = self._filter_for_send(self.comment)
|
@@ -382,10 +380,7 @@ class BeaconPacket(GPSPacket):
|
|
382
380
|
self.payload = f"{self.payload}{self.symbol}APRSD Beacon"
|
383
381
|
|
384
382
|
def _build_raw(self):
|
385
|
-
self.raw =
|
386
|
-
f"{self.from_call}>APZ100:"
|
387
|
-
f"{self.payload}"
|
388
|
-
)
|
383
|
+
self.raw = f"{self.from_call}>APZ100:" f"{self.payload}"
|
389
384
|
|
390
385
|
@property
|
391
386
|
def key(self) -> str:
|
@@ -393,7 +388,7 @@ class BeaconPacket(GPSPacket):
|
|
393
388
|
if self.raw_timestamp:
|
394
389
|
return f"{self.from_call}:{self.raw_timestamp}"
|
395
390
|
else:
|
396
|
-
return f"{self.from_call}:{self.human_info.replace(' ','')}"
|
391
|
+
return f"{self.from_call}:{self.human_info.replace(' ', '')}"
|
397
392
|
|
398
393
|
@property
|
399
394
|
def human_info(self) -> str:
|
@@ -449,7 +444,7 @@ class TelemetryPacket(GPSPacket):
|
|
449
444
|
if self.raw_timestamp:
|
450
445
|
return f"{self.from_call}:{self.raw_timestamp}"
|
451
446
|
else:
|
452
|
-
return f"{self.from_call}:{self.human_info.replace(' ','')}"
|
447
|
+
return f"{self.from_call}:{self.human_info.replace(' ', '')}"
|
453
448
|
|
454
449
|
@property
|
455
450
|
def human_info(self) -> str:
|
@@ -474,10 +469,7 @@ class ObjectPacket(GPSPacket):
|
|
474
469
|
lat = aprslib_util.latitude_to_ddm(self.latitude)
|
475
470
|
long = aprslib_util.longitude_to_ddm(self.longitude)
|
476
471
|
|
477
|
-
self.payload =
|
478
|
-
f"*{time_zulu}z{lat}{self.symbol_table}"
|
479
|
-
f"{long}{self.symbol}"
|
480
|
-
)
|
472
|
+
self.payload = f"*{time_zulu}z{lat}{self.symbol_table}" f"{long}{self.symbol}"
|
481
473
|
|
482
474
|
if self.comment:
|
483
475
|
comment = self._filter_for_send(self.comment)
|
@@ -494,10 +486,7 @@ class ObjectPacket(GPSPacket):
|
|
494
486
|
The frequency, uplink_tone, offset is part of the comment
|
495
487
|
"""
|
496
488
|
|
497
|
-
self.raw =
|
498
|
-
f"{self.from_call}>APZ100:;{self.to_call:9s}"
|
499
|
-
f"{self.payload}"
|
500
|
-
)
|
489
|
+
self.raw = f"{self.from_call}>APZ100:;{self.to_call:9s}" f"{self.payload}"
|
501
490
|
|
502
491
|
@property
|
503
492
|
def human_info(self) -> str:
|
@@ -547,11 +536,13 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
|
|
547
536
|
if "speed" in raw:
|
548
537
|
del raw["speed"]
|
549
538
|
# Let's adjust the rain numbers as well, since it's wrong
|
550
|
-
raw["rain_1h"] = round((raw.get("rain_1h", 0) / .254) * .01, 3)
|
539
|
+
raw["rain_1h"] = round((raw.get("rain_1h", 0) / 0.254) * 0.01, 3)
|
551
540
|
raw["weather"]["rain_1h"] = raw["rain_1h"]
|
552
|
-
raw["rain_24h"] = round((raw.get("rain_24h", 0) / .254) * .01, 3)
|
541
|
+
raw["rain_24h"] = round((raw.get("rain_24h", 0) / 0.254) * 0.01, 3)
|
553
542
|
raw["weather"]["rain_24h"] = raw["rain_24h"]
|
554
|
-
raw["rain_since_midnight"] = round(
|
543
|
+
raw["rain_since_midnight"] = round(
|
544
|
+
(raw.get("rain_since_midnight", 0) / 0.254) * 0.01, 3
|
545
|
+
)
|
555
546
|
raw["weather"]["rain_since_midnight"] = raw["rain_since_midnight"]
|
556
547
|
|
557
548
|
if "wind_direction" not in raw:
|
@@ -593,26 +584,26 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
|
|
593
584
|
def _build_payload(self):
|
594
585
|
"""Build an uncompressed weather packet
|
595
586
|
|
596
|
-
|
587
|
+
Format =
|
597
588
|
|
598
|
-
|
599
|
-
|
589
|
+
_CSE/SPDgXXXtXXXrXXXpXXXPXXXhXXbXXXXX%type NEW FORMAT APRS793 June 97
|
590
|
+
NOT BACKWARD COMPATIBLE
|
600
591
|
|
601
592
|
|
602
|
-
|
603
|
-
|
593
|
+
Where: CSE/SPD is wind direction and sustained 1 minute speed
|
594
|
+
t is in degrees F
|
604
595
|
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
596
|
+
r is Rain per last 60 minutes
|
597
|
+
1.04 inches of rain will show as r104
|
598
|
+
p is precipitation per last 24 hours (sliding 24 hour window)
|
599
|
+
P is precip per last 24 hours since midnight
|
600
|
+
b is Baro in tenths of a mb
|
601
|
+
h is humidity in percent. 00=100
|
602
|
+
g is Gust (peak winds in last 5 minutes)
|
603
|
+
# is the raw rain counter for remote WX stations
|
604
|
+
See notes on remotes below
|
605
|
+
% shows software type d=Dos, m=Mac, w=Win, etc
|
606
|
+
type shows type of WX instrument
|
616
607
|
|
617
608
|
"""
|
618
609
|
time_zulu = self._build_time_zulu()
|
@@ -622,17 +613,18 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
|
|
622
613
|
f"{self.longitude}{self.symbol}",
|
623
614
|
f"{self.wind_direction:03d}",
|
624
615
|
# Speed = sustained 1 minute wind speed in mph
|
625
|
-
f"{self.symbol_table}",
|
616
|
+
f"{self.symbol_table}",
|
617
|
+
f"{self.wind_speed:03.0f}",
|
626
618
|
# wind gust (peak wind speed in mph in the last 5 minutes)
|
627
619
|
f"g{self.wind_gust:03.0f}",
|
628
620
|
# Temperature in degrees F
|
629
621
|
f"t{self.temperature:03.0f}",
|
630
622
|
# Rainfall (in hundredths of an inch) in the last hour
|
631
|
-
f"r{self.rain_1h*100:03.0f}",
|
623
|
+
f"r{self.rain_1h * 100:03.0f}",
|
632
624
|
# Rainfall (in hundredths of an inch) in last 24 hours
|
633
|
-
f"p{self.rain_24h*100:03.0f}",
|
625
|
+
f"p{self.rain_24h * 100:03.0f}",
|
634
626
|
# Rainfall (in hundredths of an inch) since midnigt
|
635
|
-
f"P{self.rain_since_midnight*100:03.0f}",
|
627
|
+
f"P{self.rain_since_midnight * 100:03.0f}",
|
636
628
|
# Humidity
|
637
629
|
f"h{self.humidity:02d}",
|
638
630
|
# Barometric pressure (in tenths of millibars/tenths of hPascal)
|
@@ -644,11 +636,7 @@ class WeatherPacket(GPSPacket, DataClassJsonMixin):
|
|
644
636
|
self.payload = "".join(contents)
|
645
637
|
|
646
638
|
def _build_raw(self):
|
647
|
-
|
648
|
-
self.raw = (
|
649
|
-
f"{self.from_call}>{self.to_call},WIDE1-1,WIDE2-1:"
|
650
|
-
f"{self.payload}"
|
651
|
-
)
|
639
|
+
self.raw = f"{self.from_call}>{self.to_call},WIDE1-1,WIDE2-1:" f"{self.payload}"
|
652
640
|
|
653
641
|
|
654
642
|
@dataclass(unsafe_hash=True)
|
@@ -692,14 +680,17 @@ class UnknownPacket:
|
|
692
680
|
|
693
681
|
All of the unknown attributes are stored in the unknown_fields
|
694
682
|
"""
|
683
|
+
|
695
684
|
unknown_fields: CatchAll
|
696
685
|
_type: str = "UnknownPacket"
|
697
686
|
from_call: Optional[str] = field(default=None)
|
698
687
|
to_call: Optional[str] = field(default=None)
|
699
|
-
msgNo: str = field(default_factory=_init_msgNo)
|
688
|
+
msgNo: str = field(default_factory=_init_msgNo) # noqa: N815
|
700
689
|
format: Optional[str] = field(default=None)
|
701
690
|
raw: Optional[str] = field(default=None)
|
702
|
-
raw_dict: dict = field(
|
691
|
+
raw_dict: dict = field(
|
692
|
+
repr=False, default_factory=lambda: {}, compare=False, hash=False
|
693
|
+
)
|
703
694
|
path: List[str] = field(default_factory=list, compare=False, hash=False)
|
704
695
|
packet_type: Optional[str] = field(default=None)
|
705
696
|
via: Optional[str] = field(default=None, compare=False, hash=False)
|
aprsd/packets/log.py
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
import logging
|
2
2
|
from typing import Optional
|
3
3
|
|
4
|
-
from
|
4
|
+
from haversine import Unit, haversine
|
5
5
|
from loguru import logger
|
6
6
|
from oslo_config import cfg
|
7
7
|
|
8
8
|
from aprsd import utils
|
9
9
|
from aprsd.packets.core import AckPacket, GPSPacket, RejectPacket
|
10
10
|
|
11
|
-
|
12
11
|
LOG = logging.getLogger()
|
13
12
|
LOGU = logger
|
14
13
|
CONF = cfg.CONF
|
@@ -22,7 +21,9 @@ DISTANCE_COLOR = "fg #FF5733"
|
|
22
21
|
DEGREES_COLOR = "fg #FFA900"
|
23
22
|
|
24
23
|
|
25
|
-
def log_multiline(
|
24
|
+
def log_multiline(
|
25
|
+
packet, tx: Optional[bool] = False, header: Optional[bool] = True
|
26
|
+
) -> None:
|
26
27
|
"""LOG a packet to the logfile."""
|
27
28
|
if not CONF.enable_packet_logging:
|
28
29
|
return
|
@@ -121,8 +122,7 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
|
|
121
122
|
via_color = "green"
|
122
123
|
arrow = f"<{via_color}>-></{via_color}>"
|
123
124
|
logit.append(
|
124
|
-
f"<cyan>{name}</cyan>"
|
125
|
-
f":{packet.msgNo}",
|
125
|
+
f"<cyan>{name}</cyan>" f":{packet.msgNo}",
|
126
126
|
)
|
127
127
|
|
128
128
|
tmp = None
|
@@ -145,8 +145,8 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
|
|
145
145
|
|
146
146
|
# is there distance information?
|
147
147
|
if isinstance(packet, GPSPacket) and CONF.latitude and CONF.longitude:
|
148
|
-
my_coords = (CONF.latitude, CONF.longitude)
|
149
|
-
packet_coords = (packet.latitude, packet.longitude)
|
148
|
+
my_coords = (float(CONF.latitude), float(CONF.longitude))
|
149
|
+
packet_coords = (float(packet.latitude), float(packet.longitude))
|
150
150
|
try:
|
151
151
|
bearing = utils.calculate_initial_compass_bearing(my_coords, packet_coords)
|
152
152
|
except Exception as e:
|
@@ -154,7 +154,7 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
|
|
154
154
|
bearing = 0
|
155
155
|
logit.append(
|
156
156
|
f" : <{DEGREES_COLOR}>{utils.degrees_to_cardinal(bearing, full_string=True)}</{DEGREES_COLOR}>"
|
157
|
-
f"<{DISTANCE_COLOR}>@{
|
157
|
+
f"<{DISTANCE_COLOR}>@{haversine(my_coords, packet_coords, unit=Unit.MILES):.2f}miles</{DISTANCE_COLOR}>",
|
158
158
|
)
|
159
159
|
|
160
160
|
LOGU.opt(colors=True).info(" ".join(logit))
|