1
0
mirror of https://github.com/craigerl/aprsd.git synced 2024-11-24 08:58:49 -05:00

Compare commits

...

5 Commits

Author SHA1 Message Date
afourney
a66554b0a1
Merge 1334eded62 into 14c0a699cb 2024-10-18 11:40:12 -07:00
14c0a699cb Cleanup test failures 2024-10-18 12:25:16 -04:00
c12c42b876 cleaned up some requirements
we don't really need gevent, eventlet.
those are only needed for the web admin interface
2024-10-18 12:25:06 -04:00
765e02f5b3 Collector cleanup 2024-10-18 12:07:02 -04:00
Adam Fourney
1334eded62 Added an option to disable the loading of the help plugin. 2024-09-26 11:24:16 -07:00
20 changed files with 802 additions and 800 deletions

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@ from aprsd import cli_helper, packets
from aprsd import conf # noqa : F401
from aprsd.client import client_factory
from aprsd.main import cli
import aprsd.packets # noqa : F401
from aprsd.packets import collector
from aprsd.threads import tx
@ -94,10 +95,6 @@ def send_message(
else:
LOG.info(f"L'{aprs_login}' To'{tocallsign}' C'{command}'")
packets.PacketList()
packets.WatchList()
packets.SeenList()
got_ack = False
got_response = False

View File

@ -8,7 +8,7 @@ from oslo_config import cfg
import aprsd
from aprsd import cli_helper
from aprsd import main as aprsd_main
from aprsd import packets, plugin, threads, utils
from aprsd import plugin, threads, utils
from aprsd.client import client_factory
from aprsd.main import cli
from aprsd.packets import collector as packet_collector
@ -87,29 +87,24 @@ def server(ctx, flush):
LOG.error("APRS client is not properly configured in config file.")
sys.exit(-1)
# Now load the msgTrack from disk if any
packets.PacketList()
if flush:
LOG.debug("Deleting saved MsgTrack.")
packets.PacketTrack().flush()
packets.WatchList().flush()
packets.SeenList().flush()
packets.PacketList().flush()
else:
# Try and load saved MsgTrack list
LOG.debug("Loading saved MsgTrack object.")
packets.PacketTrack().load()
packets.WatchList().load()
packets.SeenList().load()
packets.PacketList().load()
keepalive = keep_alive.KeepAliveThread()
keepalive.start()
if not CONF.enable_seen_list:
# just deregister the class from the packet collector
packet_collector.PacketCollector().unregister(seen_list.SeenList)
# Now load the msgTrack from disk if any
if flush:
LOG.debug("Flushing All packet tracking objects.")
packet_collector.PacketCollector().flush()
else:
# Try and load saved MsgTrack list
LOG.debug("Loading saved packet tracking data.")
packet_collector.PacketCollector().load()
# Now start all the main processing threads.
keepalive = keep_alive.KeepAliveThread()
keepalive.start()
stats_store_thread = stats_thread.APRSDStatsStoreThread()
stats_store_thread.start()

View File

@ -62,8 +62,6 @@ def signal_handler(sig, frame):
threads.APRSDThreadList().stop_all()
if "subprocess" not in str(frame):
time.sleep(1.5)
# packets.WatchList().save()
# packets.SeenList().save()
stats.stats_collector.collect()
LOG.info("Telling flask to bail.")
signal.signal(signal.SIGTERM, sys.exit(0))
@ -647,11 +645,6 @@ def webchat(ctx, flush, port):
LOG.error("APRS client is not properly configured in config file.")
sys.exit(-1)
packets.PacketList()
packets.PacketTrack()
packets.WatchList()
packets.SeenList()
keepalive = keep_alive.KeepAliveThread()
LOG.info("Start KeepAliveThread")
keepalive.start()

View File

@ -136,6 +136,11 @@ aprsd_opts = [
default=True,
help="Set this to False, to disable logging of packets to the log file.",
),
cfg.BoolOpt(
"load_help_plugin",
default=True,
help="Set this to False to disable the help plugin.",
),
]
watch_list_opts = [

View File

@ -1,4 +0,0 @@
# What to return from a plugin if we have processed the message
# and it's ok, but don't send a usage string back
# REMOVE THIS FILE

View File

@ -1,3 +1,4 @@
from aprsd.packets import collector
from aprsd.packets.core import ( # noqa: F401
AckPacket, BeaconPacket, BulletinPacket, GPSPacket, MessagePacket,
MicEPacket, ObjectPacket, Packet, RejectPacket, StatusPacket,
@ -9,4 +10,11 @@ from aprsd.packets.tracker import PacketTrack # noqa: F401
from aprsd.packets.watch_list import WatchList # noqa: F401
# Register all the packet tracking objects.
collector.PacketCollector().register(PacketList)
collector.PacketCollector().register(SeenList)
collector.PacketCollector().register(PacketTrack)
collector.PacketCollector().register(WatchList)
NULL_MESSAGE = -1

View File

@ -20,6 +20,14 @@ class PacketMonitor(Protocol):
"""When we send a packet out the network."""
...
def flush(self) -> None:
"""Flush out any data."""
...
def load(self) -> None:
"""Load any data."""
...
@singleton
class PacketCollector:
@ -27,30 +35,45 @@ class PacketCollector:
self.monitors: list[Callable] = []
def register(self, monitor: Callable) -> None:
if not isinstance(monitor, PacketMonitor):
raise TypeError(f"Monitor {monitor} is not a PacketMonitor")
self.monitors.append(monitor)
def unregister(self, monitor: Callable) -> None:
if not isinstance(monitor, PacketMonitor):
raise TypeError(f"Monitor {monitor} is not a PacketMonitor")
self.monitors.remove(monitor)
def rx(self, packet: type[core.Packet]) -> None:
for name in self.monitors:
cls = name()
if isinstance(cls, PacketMonitor):
try:
cls.rx(packet)
except Exception as e:
LOG.error(f"Error in monitor {name} (rx): {e}")
else:
raise TypeError(f"Monitor {name} is not a PacketMonitor")
try:
cls.rx(packet)
except Exception as e:
LOG.error(f"Error in monitor {name} (rx): {e}")
def tx(self, packet: type[core.Packet]) -> None:
for name in self.monitors:
cls = name()
if isinstance(cls, PacketMonitor):
try:
cls.tx(packet)
except Exception as e:
LOG.error(f"Error in monitor {name} (tx): {e}")
else:
raise TypeError(f"Monitor {name} is not a PacketMonitor")
try:
cls.tx(packet)
except Exception as e:
LOG.error(f"Error in monitor {name} (tx): {e}")
def flush(self):
"""Call flush on the objects. This is used to flush out any data."""
for name in self.monitors:
cls = name()
try:
cls.flush()
except Exception as e:
LOG.error(f"Error in monitor {name} (flush): {e}")
def load(self):
"""Call load on the objects. This is used to load any data."""
for name in self.monitors:
cls = name()
try:
cls.load()
except Exception as e:
LOG.error(f"Error in monitor {name} (load): {e}")

View File

@ -101,7 +101,6 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
if header:
if tx:
via_color = "red"
# arrow = f"<{via_color}>-></{via_color}>"
arrow = f"<{via_color}>\u2192</{via_color}>"
logit.append(
f"<red>TX\u2191</red> "
@ -111,7 +110,6 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
)
else:
via_color = "fg #1AA730"
#arrow = f"<{via_color}>-></{via_color}>"
arrow = f"<{via_color}>\u2192</{via_color}>"
f"<{via_color}><-</{via_color}>"
logit.append(

View File

@ -3,7 +3,7 @@ import logging
from oslo_config import cfg
from aprsd.packets import collector, core
from aprsd.packets import core
from aprsd.utils import objectstore
@ -108,9 +108,3 @@ class PacketList(objectstore.ObjectStoreMixin):
"packets": pkts,
}
return stats
# Now register the PacketList with the collector
# every packet we RX and TX goes through the collector
# for processing for whatever reason is needed.
collector.PacketCollector().register(PacketList)

View File

@ -3,7 +3,7 @@ import logging
from oslo_config import cfg
from aprsd.packets import collector, core
from aprsd.packets import core
from aprsd.utils import objectstore
@ -47,8 +47,3 @@ class SeenList(objectstore.ObjectStoreMixin):
def tx(self, packet: type[core.Packet]):
"""We don't care about TX packets."""
# Register with the packet collector so we can process the packet
# when we get it off the client (network)
collector.PacketCollector().register(SeenList)

View File

@ -3,7 +3,7 @@ import logging
from oslo_config import cfg
from aprsd.packets import collector, core
from aprsd.packets import core
from aprsd.utils import objectstore
@ -101,9 +101,3 @@ class PacketTrack(objectstore.ObjectStoreMixin):
del self.data[key]
except KeyError:
pass
# Now register the PacketList with the collector
# every packet we RX and TX goes through the collector
# for processing for whatever reason is needed.
collector.PacketCollector().register(PacketTrack)

View File

@ -4,7 +4,7 @@ import logging
from oslo_config import cfg
from aprsd import utils
from aprsd.packets import collector, core
from aprsd.packets import core
from aprsd.utils import objectstore
@ -117,6 +117,3 @@ class WatchList(objectstore.ObjectStoreMixin):
return False
else:
return False
collector.PacketCollector().register(WatchList)

View File

@ -472,9 +472,13 @@ class PluginManager:
del self._pluggy_pm
self.setup_plugins()
def setup_plugins(self, load_help_plugin=True):
def setup_plugins(self, load_help_plugin=None):
"""Create the plugin manager and register plugins."""
# If load_help_plugin is not specified, load it from the config
if load_help_plugin is None:
load_help_plugin = CONF.load_help_plugin
LOG.info("Loading APRSD Plugins")
# Help plugin is always enabled.
if load_help_plugin:

View File

@ -25,14 +25,13 @@ class Collector:
stats = {}
for name in self.producers:
cls = name()
if isinstance(cls, StatsProducer):
try:
stats[cls.__class__.__name__] = cls.stats(serializable=serializable).copy()
except Exception as e:
LOG.error(f"Error in producer {name} (stats): {e}")
else:
raise TypeError(f"{cls} is not an instance of StatsProducer")
try:
stats[cls.__class__.__name__] = cls.stats(serializable=serializable).copy()
except Exception as e:
LOG.error(f"Error in producer {name} (stats): {e}")
return stats
def register_producer(self, producer_name: Callable):
if not isinstance(producer_name, StatsProducer):
raise TypeError(f"Producer {producer_name} is not a StatsProducer")
self.producers.append(producer_name)

View File

@ -328,8 +328,22 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
# If the message was for us and we didn't have a
# response, then we send a usage statement.
if to_call == CONF.callsign and not replied:
LOG.warning("Sending help!")
message_text = "Unknown command! Send 'help' message for help"
# Is the help plugin installed?
help_available = False
for p in pm.get_message_plugins():
if isinstance(p, plugin.HelpPlugin):
help_available = True
break
# Tailor the messages accordingly
if help_available:
LOG.warning("Sending help!")
message_text = "Unknown command! Send 'help' message for help"
else:
LOG.warning("Unknown command!")
message_text = "Unknown command!"
tx.send(
packets.MessagePacket(
from_call=CONF.callsign,

View File

@ -175,18 +175,18 @@ def load_entry_points(group):
def calculate_initial_compass_bearing(start, end):
if (type(start) != tuple) or (type(end) != tuple):
if (type(start) != tuple) or (type(end) != tuple): # noqa: E721
raise TypeError("Only tuples are supported as arguments")
lat1 = math.radians(float(start[0]))
lat2 = math.radians(float(end[0]))
diffLong = math.radians(float(end[1]) - float(start[1]))
diff_long = math.radians(float(end[1]) - float(start[1]))
x = math.sin(diffLong) * math.cos(lat2)
x = math.sin(diff_long) * math.cos(lat2)
y = math.cos(lat1) * math.sin(lat2) - (
math.sin(lat1)
* math.cos(lat2) * math.cos(diffLong)
* math.cos(lat2) * math.cos(diff_long)
)
initial_bearing = math.atan2(x, y)
@ -202,17 +202,17 @@ def calculate_initial_compass_bearing(start, end):
def degrees_to_cardinal(bearing, full_string=False):
if full_string:
DIRECTIONS = [
directions = [
"North", "North-Northeast", "Northeast", "East-Northeast", "East", "East-Southeast",
"Southeast", "South-Southeast", "South", "South-Southwest", "Southwest", "West-Southwest",
"West", "West-Northwest", "Northwest", "North-Northwest", "North",
]
else:
DIRECTIONS = [
directions = [
"N", "NNE", "NE", "ENE", "E", "ESE",
"SE", "SSE", "S", "SSW", "SW", "WSW",
"W", "WNW", "NW", "NNW", "N",
]
cardinal = DIRECTIONS[round(bearing / 22.5)]
cardinal = directions[round(bearing / 22.5)]
return cardinal

View File

@ -8,23 +8,23 @@ add-trailing-comma==3.1.0 # via gray
alabaster==1.0.0 # via sphinx
autoflake==1.5.3 # via gray
babel==2.16.0 # via sphinx
black==24.8.0 # via gray
build==1.2.2 # via -r requirements-dev.in, check-manifest, pip-tools
black==24.10.0 # via gray
build==1.2.2.post1 # via -r requirements-dev.in, check-manifest, pip-tools
cachetools==5.5.0 # via tox
certifi==2024.8.30 # via requests
cfgv==3.4.0 # via pre-commit
chardet==5.2.0 # via tox
charset-normalizer==3.3.2 # via requests
check-manifest==0.49 # via -r requirements-dev.in
charset-normalizer==3.4.0 # via requests
check-manifest==0.50 # via -r requirements-dev.in
click==8.1.7 # via black, fixit, moreorless, pip-tools
colorama==0.4.6 # via tox
commonmark==0.9.1 # via rich
configargparse==1.7 # via gray
coverage[toml]==7.6.1 # via pytest-cov
distlib==0.3.8 # via virtualenv
coverage[toml]==7.6.3 # via pytest-cov
distlib==0.3.9 # via virtualenv
docutils==0.21.2 # via m2r, sphinx
exceptiongroup==1.2.2 # via pytest
filelock==3.16.0 # via tox, virtualenv
filelock==3.16.1 # via tox, virtualenv
fixit==2.1.0 # via gray
flake8==7.1.1 # via -r requirements-dev.in, pep8-naming
gray==0.15.0 # via -r requirements-dev.in
@ -34,35 +34,35 @@ imagesize==1.4.1 # via sphinx
iniconfig==2.0.0 # via pytest
isort==5.13.2 # via -r requirements-dev.in, gray
jinja2==3.1.4 # via sphinx
libcst==1.4.0 # via fixit
libcst==1.5.0 # via fixit
m2r==0.3.1 # via -r requirements-dev.in
markupsafe==2.1.5 # via jinja2
markupsafe==3.0.2 # via jinja2
mccabe==0.7.0 # via flake8
mistune==0.8.4 # via m2r
moreorless==0.4.0 # via fixit
mypy==1.11.2 # via -r requirements-dev.in
mypy==1.12.0 # via -r requirements-dev.in
mypy-extensions==1.0.0 # via black, mypy
nodeenv==1.9.1 # via pre-commit
packaging==24.1 # via black, build, fixit, pyproject-api, pytest, sphinx, tox
pathspec==0.12.1 # via black, trailrunner
pep8-naming==0.14.1 # via -r requirements-dev.in
pip-tools==7.4.1 # via -r requirements-dev.in
platformdirs==4.3.3 # via black, tox, virtualenv
platformdirs==4.3.6 # via black, tox, virtualenv
pluggy==1.5.0 # via pytest, tox
pre-commit==3.8.0 # via -r requirements-dev.in
pre-commit==4.0.1 # via -r requirements-dev.in
pycodestyle==2.12.1 # via flake8
pyflakes==3.2.0 # via autoflake, flake8
pygments==2.18.0 # via rich, sphinx
pyproject-api==1.7.1 # via tox
pyproject-hooks==1.1.0 # via build, pip-tools
pyproject-api==1.8.0 # via tox
pyproject-hooks==1.2.0 # via build, pip-tools
pytest==8.3.3 # via -r requirements-dev.in, pytest-cov
pytest-cov==5.0.0 # via -r requirements-dev.in
pyupgrade==3.17.0 # via gray
pyupgrade==3.18.0 # via gray
pyyaml==6.0.2 # via libcst, pre-commit
requests==2.32.3 # via sphinx
rich==12.6.0 # via gray
snowballstemmer==2.2.0 # via sphinx
sphinx==8.0.2 # via -r requirements-dev.in
sphinx==8.1.3 # via -r requirements-dev.in
sphinxcontrib-applehelp==2.0.0 # via sphinx
sphinxcontrib-devhelp==2.0.0 # via sphinx
sphinxcontrib-htmlhelp==2.1.0 # via sphinx
@ -71,14 +71,14 @@ sphinxcontrib-qthelp==2.0.0 # via sphinx
sphinxcontrib-serializinghtml==2.0.0 # via sphinx
tokenize-rt==6.0.0 # via add-trailing-comma, pyupgrade
toml==0.10.2 # via autoflake
tomli==2.0.1 # via black, build, check-manifest, coverage, fixit, mypy, pip-tools, pyproject-api, pytest, sphinx, tox
tox==4.18.1 # via -r requirements-dev.in
tomli==2.0.2 # via black, build, check-manifest, coverage, fixit, mypy, pip-tools, pyproject-api, pytest, sphinx, tox
tox==4.23.0 # via -r requirements-dev.in
trailrunner==1.4.0 # via fixit
typing-extensions==4.12.2 # via black, mypy
typing-extensions==4.12.2 # via black, mypy, tox
unify==0.5 # via gray
untokenize==0.1.1 # via unify
urllib3==2.2.3 # via requests
virtualenv==20.26.4 # via pre-commit, tox
virtualenv==20.27.0 # via pre-commit, tox
wheel==0.44.0 # via -r requirements-dev.in, pip-tools
# The following packages are considered to be unsafe in a requirements file:

View File

@ -5,12 +5,10 @@ click
click-params
dataclasses
dataclasses-json
eventlet
flask
flask-httpauth
flask-socketio
geopy
gevent
imapclient
kiss3
loguru
@ -18,7 +16,6 @@ oslo.config
pluggy
python-socketio
pyyaml
pytz
requests
# Pinned due to gray needing 12.6.0
rich~=12.6.0
@ -30,3 +27,4 @@ thesmuggler
tzlocal
update_checker
wrapt
pytz

View File

@ -9,10 +9,10 @@ attrs==24.2.0 # via ax253, kiss3, rush
ax253==0.1.5.post1 # via kiss3
beautifulsoup4==4.12.3 # via -r requirements.in
bidict==0.23.1 # via python-socketio
bitarray==2.9.2 # via ax253, kiss3
bitarray==3.0.0 # via ax253, kiss3
blinker==1.8.2 # via flask
certifi==2024.8.30 # via requests
charset-normalizer==3.3.2 # via requests
charset-normalizer==3.4.0 # via requests
click==8.1.7 # via -r requirements.in, click-params, flask
click-params==0.5.0 # via -r requirements.in
commonmark==0.9.1 # via rich
@ -20,15 +20,11 @@ dataclasses==0.6 # via -r requirements.in
dataclasses-json==0.6.7 # via -r requirements.in
debtcollector==3.0.0 # via oslo-config
deprecated==1.2.14 # via click-params
dnspython==2.6.1 # via eventlet
eventlet==0.37.0 # via -r requirements.in
flask==3.0.3 # via -r requirements.in, flask-httpauth, flask-socketio
flask-httpauth==4.8.0 # via -r requirements.in
flask-socketio==5.3.7 # via -r requirements.in
flask-socketio==5.4.1 # via -r requirements.in
geographiclib==2.0 # via geopy
geopy==2.4.1 # via -r requirements.in
gevent==24.2.1 # via -r requirements.in
greenlet==3.1.0 # via eventlet, gevent
h11==0.14.0 # via wsproto
idna==3.10 # via requests
imapclient==3.0.1 # via -r requirements.in
@ -37,8 +33,8 @@ itsdangerous==2.2.0 # via flask
jinja2==3.1.4 # via flask
kiss3==8.0.0 # via -r requirements.in
loguru==0.7.2 # via -r requirements.in
markupsafe==2.1.5 # via jinja2, werkzeug
marshmallow==3.22.0 # via dataclasses-json
markupsafe==3.0.2 # via jinja2, werkzeug
marshmallow==3.23.0 # via dataclasses-json
mypy-extensions==1.0.0 # via typing-inspect
netaddr==1.3.0 # via oslo-config
oslo-config==9.6.0 # via -r requirements.in
@ -49,7 +45,7 @@ pluggy==1.5.0 # via -r requirements.in
pygments==2.18.0 # via rich
pyserial==3.5 # via pyserial-asyncio
pyserial-asyncio==0.6 # via kiss3
python-engineio==4.9.1 # via python-socketio
python-engineio==4.10.1 # via python-socketio
python-socketio==5.11.4 # via -r requirements.in, flask-socketio
pytz==2024.2 # via -r requirements.in
pyyaml==6.0.2 # via -r requirements.in, oslo-config
@ -58,7 +54,7 @@ rfc3986==2.0.0 # via oslo-config
rich==12.6.0 # via -r requirements.in
rush==2021.4.0 # via -r requirements.in
shellingham==1.5.4 # via -r requirements.in
simple-websocket==1.0.0 # via python-engineio
simple-websocket==1.1.0 # via python-engineio
six==1.16.0 # via -r requirements.in
soupsieve==2.6 # via beautifulsoup4
stevedore==5.3.0 # via oslo-config
@ -74,8 +70,3 @@ werkzeug==3.0.4 # via flask
wrapt==1.16.0 # via -r requirements.in, debtcollector, deprecated
wsproto==1.2.0 # via simple-websocket
zipp==3.20.2 # via importlib-metadata
zope-event==5.0 # via gevent
zope-interface==7.0.3 # via gevent
# The following packages are considered to be unsafe in a requirements file:
# setuptools