mirror of
https://github.com/craigerl/aprsd.git
synced 2024-11-10 10:33:31 -05:00
commit
144ad34ae5
@ -137,7 +137,7 @@ class APRSISClient(Client):
|
||||
|
||||
def decode_packet(self, *args, **kwargs):
|
||||
"""APRS lib already decodes this."""
|
||||
return core.Packet.factory(args[0])
|
||||
return core.factory(args[0])
|
||||
|
||||
def setup_connection(self):
|
||||
user = CONF.aprs_network.login
|
||||
@ -238,7 +238,7 @@ class KISSClient(Client):
|
||||
# LOG.debug(f"Decoding {msg}")
|
||||
|
||||
raw = aprslib.parse(str(frame))
|
||||
packet = core.Packet.factory(raw)
|
||||
packet = core.factory(raw)
|
||||
if isinstance(packet, core.ThirdParty):
|
||||
return packet.subpacket
|
||||
else:
|
||||
|
@ -67,7 +67,7 @@ class APRSDFakeClient(metaclass=trace.TraceWrapperMetaclass):
|
||||
# Generate packets here?
|
||||
raw = "GTOWN>APDW16,WIDE1-1,WIDE2-1:}KM6LYW-9>APZ100,TCPIP,GTOWN*::KM6LYW :KM6LYW: 19 Miles SW"
|
||||
pkt_raw = aprslib.parse(raw)
|
||||
pkt = core.Packet.factory(pkt_raw)
|
||||
pkt = core.factory(pkt_raw)
|
||||
callback(packet=pkt)
|
||||
LOG.debug(f"END blocking FAKE consumer {self}")
|
||||
time.sleep(8)
|
||||
|
@ -17,6 +17,7 @@ from rich.console import Console
|
||||
import aprsd
|
||||
from aprsd import cli_helper, client, packets, plugin, stats, threads
|
||||
from aprsd.main import cli
|
||||
from aprsd.packets import log as packet_log
|
||||
from aprsd.rpc import server as rpc_server
|
||||
from aprsd.threads import rx
|
||||
|
||||
@ -53,27 +54,31 @@ class APRSDListenThread(rx.APRSDRXThread):
|
||||
filters = {
|
||||
packets.Packet.__name__: packets.Packet,
|
||||
packets.AckPacket.__name__: packets.AckPacket,
|
||||
packets.BeaconPacket.__name__: packets.BeaconPacket,
|
||||
packets.GPSPacket.__name__: packets.GPSPacket,
|
||||
packets.MessagePacket.__name__: packets.MessagePacket,
|
||||
packets.MicEPacket.__name__: packets.MicEPacket,
|
||||
packets.ObjectPacket.__name__: packets.ObjectPacket,
|
||||
packets.StatusPacket.__name__: packets.StatusPacket,
|
||||
packets.ThirdPartyPacket.__name__: packets.ThirdPartyPacket,
|
||||
packets.WeatherPacket.__name__: packets.WeatherPacket,
|
||||
packets.UnknownPacket.__name__: packets.UnknownPacket,
|
||||
}
|
||||
|
||||
if self.packet_filter:
|
||||
filter_class = filters[self.packet_filter]
|
||||
if isinstance(packet, filter_class):
|
||||
packet.log(header="RX")
|
||||
packet_log.log(packet)
|
||||
if self.plugin_manager:
|
||||
# Don't do anything with the reply
|
||||
# This is the listen only command.
|
||||
self.plugin_manager.run(packet)
|
||||
else:
|
||||
packet_log.log(packet)
|
||||
if self.plugin_manager:
|
||||
# Don't do anything with the reply.
|
||||
# This is the listen only command.
|
||||
self.plugin_manager.run(packet)
|
||||
else:
|
||||
packet.log(header="RX")
|
||||
|
||||
packets.PacketList().rx(packet)
|
||||
|
||||
@ -96,11 +101,16 @@ class APRSDListenThread(rx.APRSDRXThread):
|
||||
"--packet-filter",
|
||||
type=click.Choice(
|
||||
[
|
||||
packets.Packet.__name__,
|
||||
packets.AckPacket.__name__,
|
||||
packets.BeaconPacket.__name__,
|
||||
packets.GPSPacket.__name__,
|
||||
packets.MicEPacket.__name__,
|
||||
packets.MessagePacket.__name__,
|
||||
packets.ObjectPacket.__name__,
|
||||
packets.RejectPacket.__name__,
|
||||
packets.StatusPacket.__name__,
|
||||
packets.ThirdPartyPacket.__name__,
|
||||
packets.UnknownPacket.__name__,
|
||||
packets.WeatherPacket.__name__,
|
||||
],
|
||||
case_sensitive=False,
|
||||
@ -180,7 +190,7 @@ def listen(
|
||||
aprs_client.set_filter(filter)
|
||||
|
||||
keepalive = threads.KeepAliveThread()
|
||||
keepalive.start()
|
||||
# keepalive.start()
|
||||
|
||||
if CONF.rpc_settings.enabled:
|
||||
rpc = rpc_server.APRSDRPCThread()
|
||||
@ -205,6 +215,8 @@ def listen(
|
||||
)
|
||||
LOG.debug("Start APRSDListenThread")
|
||||
listen_thread.start()
|
||||
|
||||
keepalive.start()
|
||||
LOG.debug("keepalive Join")
|
||||
keepalive.join()
|
||||
LOG.debug("listen_thread Join")
|
||||
|
@ -7,7 +7,6 @@ import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
from aprslib import util as aprslib_util
|
||||
import click
|
||||
import flask
|
||||
from flask import request
|
||||
@ -22,7 +21,6 @@ import aprsd
|
||||
from aprsd import (
|
||||
cli_helper, client, packets, plugin_utils, stats, threads, utils,
|
||||
)
|
||||
from aprsd.log import log
|
||||
from aprsd.main import cli
|
||||
from aprsd.threads import aprsd as aprsd_threads
|
||||
from aprsd.threads import rx, tx
|
||||
@ -30,7 +28,7 @@ from aprsd.utils import trace
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
LOG = logging.getLogger()
|
||||
auth = HTTPBasicAuth()
|
||||
users = {}
|
||||
socketio = None
|
||||
@ -335,7 +333,6 @@ class WebChatProcessPacketThread(rx.APRSDProcessPacketThread):
|
||||
|
||||
def process_our_message_packet(self, packet: packets.MessagePacket):
|
||||
global callsign_locations
|
||||
LOG.info(f"process MessagePacket {repr(packet)}")
|
||||
# ok lets see if we have the location for the
|
||||
# person we just sent a message to.
|
||||
from_call = packet.get("from_call").upper()
|
||||
@ -541,10 +538,10 @@ class SendMessageNamespace(Namespace):
|
||||
|
||||
def on_gps(self, data):
|
||||
LOG.debug(f"WS on_GPS: {data}")
|
||||
lat = aprslib_util.latitude_to_ddm(data["latitude"])
|
||||
long = aprslib_util.longitude_to_ddm(data["longitude"])
|
||||
LOG.debug(f"Lat DDM {lat}")
|
||||
LOG.debug(f"Long DDM {long}")
|
||||
lat = data["latitude"]
|
||||
long = data["longitude"]
|
||||
LOG.debug(f"Lat {lat}")
|
||||
LOG.debug(f"Long {long}")
|
||||
|
||||
tx.send(
|
||||
packets.GPSPacket(
|
||||
@ -572,8 +569,6 @@ class SendMessageNamespace(Namespace):
|
||||
def init_flask(loglevel, quiet):
|
||||
global socketio, flask_app
|
||||
|
||||
log.setup_logging(loglevel, quiet)
|
||||
|
||||
socketio = SocketIO(
|
||||
flask_app, logger=False, engineio_logger=False,
|
||||
async_mode="threading",
|
||||
@ -624,7 +619,7 @@ def webchat(ctx, flush, port):
|
||||
LOG.info(msg)
|
||||
LOG.info(f"APRSD Started version: {aprsd.__version__}")
|
||||
|
||||
CONF.log_opt_values(LOG, logging.DEBUG)
|
||||
CONF.log_opt_values(logging.getLogger(), logging.DEBUG)
|
||||
user = CONF.admin.user
|
||||
users[user] = generate_password_hash(CONF.admin.password)
|
||||
if not port:
|
||||
|
@ -101,6 +101,14 @@ aprsd_opts = [
|
||||
default=None,
|
||||
help="Longitude for the GPS Beacon button. If not set, the button will not be enabled.",
|
||||
),
|
||||
cfg.StrOpt(
|
||||
"log_packet_format",
|
||||
choices=["compact", "multiline", "both"],
|
||||
default="compact",
|
||||
help="When logging packets 'compact' will use a single line formatted for each packet."
|
||||
"'multiline' will use multiple lines for each packet and is the traditional format."
|
||||
"both will log both compact and multiline.",
|
||||
),
|
||||
]
|
||||
|
||||
watch_list_opts = [
|
||||
@ -225,6 +233,11 @@ webchat_opts = [
|
||||
default=None,
|
||||
help="Longitude for the GPS Beacon button. If not set, the button will not be enabled.",
|
||||
),
|
||||
cfg.BoolOpt(
|
||||
"disable_url_request_logging",
|
||||
default=False,
|
||||
help="Disable the logging of url requests in the webchat command.",
|
||||
),
|
||||
]
|
||||
|
||||
registry_opts = [
|
||||
|
@ -10,7 +10,8 @@ from aprsd.conf import log as conf_log
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
# LOG = logging.getLogger("APRSD")
|
||||
LOG = logger
|
||||
logging_queue = queue.Queue()
|
||||
|
||||
|
||||
@ -35,6 +36,7 @@ class InterceptHandler(logging.Handler):
|
||||
# to disable log to stdout, but still log to file
|
||||
# use the --quiet option on the cmdln
|
||||
def setup_logging(loglevel=None, quiet=False):
|
||||
print(f"setup_logging: loglevel={loglevel}, quiet={quiet}")
|
||||
if not loglevel:
|
||||
log_level = CONF.logging.log_level
|
||||
else:
|
||||
@ -53,9 +55,13 @@ def setup_logging(loglevel=None, quiet=False):
|
||||
"aprslib.parsing",
|
||||
"aprslib.exceptions",
|
||||
]
|
||||
webserver_list = [
|
||||
"werkzeug",
|
||||
"werkzeug._internal",
|
||||
]
|
||||
|
||||
# We don't really want to see the aprslib parsing debug output.
|
||||
disable_list = imap_list + aprslib_list
|
||||
disable_list = imap_list + aprslib_list + webserver_list
|
||||
|
||||
# remove every other logger's handlers
|
||||
# and propagate to root logger
|
||||
@ -66,17 +72,29 @@ def setup_logging(loglevel=None, quiet=False):
|
||||
else:
|
||||
logging.getLogger(name).propagate = True
|
||||
|
||||
if CONF.webchat.disable_url_request_logging:
|
||||
for name in webserver_list:
|
||||
logging.getLogger(name).handlers = []
|
||||
logging.getLogger(name).propagate = True
|
||||
logging.getLogger(name).setLevel(logging.ERROR)
|
||||
|
||||
handlers = [
|
||||
{
|
||||
"sink": sys.stdout, "serialize": False,
|
||||
"sink": sys.stdout,
|
||||
"serialize": False,
|
||||
"format": CONF.logging.logformat,
|
||||
"colorize": True,
|
||||
"level": log_level,
|
||||
},
|
||||
]
|
||||
if CONF.logging.logfile:
|
||||
handlers.append(
|
||||
{
|
||||
"sink": CONF.logging.logfile, "serialize": False,
|
||||
"sink": CONF.logging.logfile,
|
||||
"serialize": False,
|
||||
"format": CONF.logging.logformat,
|
||||
"colorize": False,
|
||||
"level": log_level,
|
||||
},
|
||||
)
|
||||
|
||||
@ -90,8 +108,11 @@ def setup_logging(loglevel=None, quiet=False):
|
||||
{
|
||||
"sink": qh, "serialize": False,
|
||||
"format": CONF.logging.logformat,
|
||||
"level": log_level,
|
||||
"colorize": False,
|
||||
},
|
||||
)
|
||||
|
||||
# configure loguru
|
||||
logger.configure(handlers=handlers)
|
||||
logger.level("DEBUG", color="<fg #BABABA>")
|
||||
|
@ -1,6 +1,7 @@
|
||||
from aprsd.packets.core import ( # noqa: F401
|
||||
AckPacket, BeaconPacket, GPSPacket, MessagePacket, MicEPacket, Packet,
|
||||
RejectPacket, StatusPacket, WeatherPacket,
|
||||
AckPacket, BeaconPacket, BulletinPacket, GPSPacket, MessagePacket,
|
||||
MicEPacket, ObjectPacket, Packet, RejectPacket, StatusPacket,
|
||||
ThirdPartyPacket, UnknownPacket, WeatherPacket, factory,
|
||||
)
|
||||
from aprsd.packets.packet_list import PacketList # noqa: F401
|
||||
from aprsd.packets.seen_list import SeenList # noqa: F401
|
||||
|
File diff suppressed because it is too large
Load Diff
128
aprsd/packets/log.py
Normal file
128
aprsd/packets/log.py
Normal file
@ -0,0 +1,128 @@
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from loguru import logger
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd.packets.core import AckPacket, RejectPacket
|
||||
|
||||
|
||||
LOG = logging.getLogger()
|
||||
LOGU = logger
|
||||
CONF = cfg.CONF
|
||||
|
||||
FROM_COLOR = "fg #C70039"
|
||||
TO_COLOR = "fg #D033FF"
|
||||
TX_COLOR = "red"
|
||||
RX_COLOR = "green"
|
||||
PACKET_COLOR = "cyan"
|
||||
|
||||
|
||||
def log_multiline(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> None:
|
||||
"""LOG a packet to the logfile."""
|
||||
if CONF.log_packet_format == "compact":
|
||||
return
|
||||
# asdict(packet)
|
||||
logit = ["\n"]
|
||||
name = packet.__class__.__name__
|
||||
if header:
|
||||
if tx:
|
||||
header_str = f"<{TX_COLOR}>TX</{TX_COLOR}>"
|
||||
logit.append(
|
||||
f"{header_str}________(<{PACKET_COLOR}>{name}</{PACKET_COLOR}> "
|
||||
f"TX:{packet.send_count + 1} of {packet.retry_count})",
|
||||
)
|
||||
else:
|
||||
header_str = f"<{RX_COLOR}>RX</{RX_COLOR}>"
|
||||
logit.append(
|
||||
f"{header_str}________(<{PACKET_COLOR}>{name}</{PACKET_COLOR}>)",
|
||||
)
|
||||
|
||||
else:
|
||||
header_str = ""
|
||||
logit.append(f"__________(<{PACKET_COLOR}>{name}</{PACKET_COLOR}>)")
|
||||
# log_list.append(f" Packet : {packet.__class__.__name__}")
|
||||
if packet.msgNo:
|
||||
logit.append(f" Msg # : {packet.msgNo}")
|
||||
if packet.from_call:
|
||||
logit.append(f" From : <{FROM_COLOR}>{packet.from_call}</{FROM_COLOR}>")
|
||||
if packet.to_call:
|
||||
logit.append(f" To : <{TO_COLOR}>{packet.to_call}</{TO_COLOR}>")
|
||||
if hasattr(packet, "path") and packet.path:
|
||||
logit.append(f" Path : {'=>'.join(packet.path)}")
|
||||
if hasattr(packet, "via") and packet.via:
|
||||
logit.append(f" VIA : {packet.via}")
|
||||
|
||||
if not isinstance(packet, AckPacket) and not isinstance(packet, RejectPacket):
|
||||
msg = packet.human_info
|
||||
|
||||
if msg:
|
||||
msg = msg.replace("<", "\\<")
|
||||
logit.append(f" Info : <light-yellow><b>{msg}</b></light-yellow>")
|
||||
|
||||
if hasattr(packet, "comment") and packet.comment:
|
||||
logit.append(f" Comment : {packet.comment}")
|
||||
|
||||
raw = packet.raw.replace("<", "\\<")
|
||||
logit.append(f" Raw : <fg #828282>{raw}</fg #828282>")
|
||||
logit.append(f"{header_str}________(<{PACKET_COLOR}>{name}</{PACKET_COLOR}>)")
|
||||
|
||||
LOGU.opt(colors=True).info("\n".join(logit))
|
||||
LOG.debug(repr(packet))
|
||||
|
||||
|
||||
def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> None:
|
||||
if CONF.log_packet_format == "multiline":
|
||||
log_multiline(packet, tx, header)
|
||||
return
|
||||
|
||||
logit = []
|
||||
name = packet.__class__.__name__
|
||||
|
||||
if header:
|
||||
if tx:
|
||||
via_color = "red"
|
||||
arrow = f"<{via_color}>-></{via_color}>"
|
||||
logit.append(
|
||||
f"<red>TX {arrow}</red> "
|
||||
f"<cyan>{name}</cyan>"
|
||||
f":{packet.msgNo}"
|
||||
f" ({packet.send_count + 1} of {packet.retry_count})",
|
||||
)
|
||||
else:
|
||||
via_color = "fg #828282"
|
||||
arrow = f"<{via_color}>-></{via_color}>"
|
||||
left_arrow = f"<{via_color}><-</{via_color}>"
|
||||
logit.append(
|
||||
f"<fg #1AA730>RX</fg #1AA730> {left_arrow} "
|
||||
f"<cyan>{name}</cyan>"
|
||||
f":{packet.msgNo}",
|
||||
)
|
||||
else:
|
||||
via_color = "green"
|
||||
arrow = f"<{via_color}>-></{via_color}>"
|
||||
logit.append(
|
||||
f"<cyan>{name}</cyan>"
|
||||
f":{packet.msgNo}",
|
||||
)
|
||||
|
||||
tmp = None
|
||||
if packet.path:
|
||||
tmp = f"{arrow}".join(packet.path) + f"{arrow} "
|
||||
|
||||
logit.append(
|
||||
f"<{FROM_COLOR}>{packet.from_call}</{FROM_COLOR}> {arrow}"
|
||||
f"{tmp if tmp else ' '}"
|
||||
f"<{TO_COLOR}>{packet.to_call}</{TO_COLOR}>",
|
||||
)
|
||||
|
||||
if not isinstance(packet, AckPacket) and not isinstance(packet, RejectPacket):
|
||||
logit.append(":")
|
||||
msg = packet.human_info
|
||||
|
||||
if msg:
|
||||
msg = msg.replace("<", "\\<")
|
||||
logit.append(f"<light-yellow><b>{msg}</b></light-yellow>")
|
||||
|
||||
LOGU.opt(colors=True).info(" ".join(logit))
|
||||
log_multiline(packet, tx, header)
|
@ -1,4 +1,5 @@
|
||||
# The base plugin class
|
||||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
import importlib
|
||||
import inspect
|
||||
@ -42,7 +43,7 @@ class APRSDPluginSpec:
|
||||
"""A hook specification namespace."""
|
||||
|
||||
@hookspec
|
||||
def filter(self, packet: packets.core.Packet):
|
||||
def filter(self, packet: type[packets.Packet]):
|
||||
"""My special little hook that you can customize."""
|
||||
|
||||
|
||||
@ -65,7 +66,7 @@ class APRSDPluginBase(metaclass=abc.ABCMeta):
|
||||
self.threads = self.create_threads() or []
|
||||
self.start_threads()
|
||||
|
||||
def start_threads(self):
|
||||
def start_threads(self) -> None:
|
||||
if self.enabled and self.threads:
|
||||
if not isinstance(self.threads, list):
|
||||
self.threads = [self.threads]
|
||||
@ -90,10 +91,10 @@ class APRSDPluginBase(metaclass=abc.ABCMeta):
|
||||
)
|
||||
|
||||
@property
|
||||
def message_count(self):
|
||||
def message_count(self) -> int:
|
||||
return self.message_counter
|
||||
|
||||
def help(self):
|
||||
def help(self) -> str:
|
||||
return "Help!"
|
||||
|
||||
@abc.abstractmethod
|
||||
@ -118,11 +119,11 @@ class APRSDPluginBase(metaclass=abc.ABCMeta):
|
||||
thread.stop()
|
||||
|
||||
@abc.abstractmethod
|
||||
def filter(self, packet: packets.core.Packet):
|
||||
def filter(self, packet: type[packets.Packet]) -> str | packets.MessagePacket:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def process(self, packet: packets.core.Packet):
|
||||
def process(self, packet: type[packets.Packet]):
|
||||
"""This is called when the filter passes."""
|
||||
|
||||
|
||||
@ -154,7 +155,7 @@ class APRSDWatchListPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
||||
LOG.warning("Watch list enabled, but no callsigns set.")
|
||||
|
||||
@hookimpl
|
||||
def filter(self, packet: packets.core.Packet):
|
||||
def filter(self, packet: type[packets.Packet]) -> str | packets.MessagePacket:
|
||||
result = packets.NULL_MESSAGE
|
||||
if self.enabled:
|
||||
wl = watch_list.WatchList()
|
||||
@ -206,14 +207,14 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
||||
self.enabled = True
|
||||
|
||||
@hookimpl
|
||||
def filter(self, packet: packets.core.MessagePacket):
|
||||
LOG.info(f"{self.__class__.__name__} called")
|
||||
def filter(self, packet: packets.MessagePacket) -> str | packets.MessagePacket:
|
||||
LOG.debug(f"{self.__class__.__name__} called")
|
||||
if not self.enabled:
|
||||
result = f"{self.__class__.__name__} isn't enabled"
|
||||
LOG.warning(result)
|
||||
return result
|
||||
|
||||
if not isinstance(packet, packets.core.MessagePacket):
|
||||
if not isinstance(packet, packets.MessagePacket):
|
||||
LOG.warning(f"{self.__class__.__name__} Got a {packet.__class__.__name__} ignoring")
|
||||
return packets.NULL_MESSAGE
|
||||
|
||||
@ -226,7 +227,7 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
||||
# and is an APRS message format and has a message.
|
||||
if (
|
||||
tocall == CONF.callsign
|
||||
and isinstance(packet, packets.core.MessagePacket)
|
||||
and isinstance(packet, packets.MessagePacket)
|
||||
and message
|
||||
):
|
||||
if re.search(self.command_regex, message, re.IGNORECASE):
|
||||
@ -269,7 +270,7 @@ class HelpPlugin(APRSDRegexCommandPluginBase):
|
||||
def help(self):
|
||||
return "Help: send APRS help or help <plugin>"
|
||||
|
||||
def process(self, packet: packets.core.MessagePacket):
|
||||
def process(self, packet: packets.MessagePacket):
|
||||
LOG.info("HelpPlugin")
|
||||
# fromcall = packet.get("from")
|
||||
message = packet.message_text
|
||||
@ -469,12 +470,12 @@ class PluginManager:
|
||||
|
||||
LOG.info("Completed Plugin Loading.")
|
||||
|
||||
def run(self, packet: packets.core.MessagePacket):
|
||||
def run(self, packet: packets.MessagePacket):
|
||||
"""Execute all the plugins run method."""
|
||||
with self.lock:
|
||||
return self._pluggy_pm.hook.filter(packet=packet)
|
||||
|
||||
def run_watchlist(self, packet: packets.core.Packet):
|
||||
def run_watchlist(self, packet: packets.Packet):
|
||||
with self.lock:
|
||||
return self._watchlist_pm.hook.filter(packet=packet)
|
||||
|
||||
|
@ -23,9 +23,7 @@ class VersionPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
# fromcall = packet.get("from")
|
||||
# message = packet.get("message_text", None)
|
||||
# ack = packet.get("msgNo", "0")
|
||||
stats_obj = stats.APRSDStats()
|
||||
s = stats_obj.stats()
|
||||
print(s)
|
||||
s = stats.APRSDStats().stats()
|
||||
return "APRSD ver:{} uptime:{}".format(
|
||||
aprsd.__version__,
|
||||
s["aprsd"]["uptime"],
|
||||
|
@ -110,7 +110,6 @@ class USMetarPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin):
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
print("FISTY")
|
||||
fromcall = packet.get("from")
|
||||
message = packet.get("message_text", None)
|
||||
# ack = packet.get("msgNo", "0")
|
||||
|
@ -174,7 +174,6 @@ class APRSDStats:
|
||||
def email_thread_update(self):
|
||||
self._email_thread_last_time = datetime.datetime.now()
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def stats(self):
|
||||
now = datetime.datetime.now()
|
||||
if self._email_thread_last_time:
|
||||
|
@ -2,6 +2,7 @@ import abc
|
||||
import datetime
|
||||
import logging
|
||||
import threading
|
||||
from typing import List
|
||||
|
||||
import wrapt
|
||||
|
||||
@ -9,42 +10,6 @@ import wrapt
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
class APRSDThreadList:
|
||||
"""Singleton class that keeps track of application wide threads."""
|
||||
|
||||
_instance = None
|
||||
|
||||
threads_list = []
|
||||
lock = threading.Lock()
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
cls.threads_list = []
|
||||
return cls._instance
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def add(self, thread_obj):
|
||||
self.threads_list.append(thread_obj)
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def remove(self, thread_obj):
|
||||
self.threads_list.remove(thread_obj)
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def stop_all(self):
|
||||
"""Iterate over all threads and call stop on them."""
|
||||
for th in self.threads_list:
|
||||
LOG.info(f"Stopping Thread {th.name}")
|
||||
if hasattr(th, "packet"):
|
||||
LOG.info(F"{th.name} packet {th.packet}")
|
||||
th.stop()
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def __len__(self):
|
||||
return len(self.threads_list)
|
||||
|
||||
|
||||
class APRSDThread(threading.Thread, metaclass=abc.ABCMeta):
|
||||
|
||||
def __init__(self, name):
|
||||
@ -86,3 +51,39 @@ class APRSDThread(threading.Thread, metaclass=abc.ABCMeta):
|
||||
self._cleanup()
|
||||
APRSDThreadList().remove(self)
|
||||
LOG.debug("Exiting")
|
||||
|
||||
|
||||
class APRSDThreadList:
|
||||
"""Singleton class that keeps track of application wide threads."""
|
||||
|
||||
_instance = None
|
||||
|
||||
threads_list: List[APRSDThread] = []
|
||||
lock = threading.Lock()
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
cls.threads_list = []
|
||||
return cls._instance
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def add(self, thread_obj):
|
||||
self.threads_list.append(thread_obj)
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def remove(self, thread_obj):
|
||||
self.threads_list.remove(thread_obj)
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def stop_all(self):
|
||||
"""Iterate over all threads and call stop on them."""
|
||||
for th in self.threads_list:
|
||||
LOG.info(f"Stopping Thread {th.name}")
|
||||
if hasattr(th, "packet"):
|
||||
LOG.info(F"{th.name} packet {th.packet}")
|
||||
th.stop()
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def __len__(self):
|
||||
return len(self.threads_list)
|
||||
|
@ -7,6 +7,7 @@ import aprslib
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd import client, packets, plugin
|
||||
from aprsd.packets import log as packet_log
|
||||
from aprsd.threads import APRSDThread, tx
|
||||
|
||||
|
||||
@ -80,7 +81,7 @@ class APRSDDupeRXThread(APRSDRXThread):
|
||||
"""
|
||||
packet = self._client.decode_packet(*args, **kwargs)
|
||||
# LOG.debug(raw)
|
||||
packet.log(header="RX")
|
||||
packet_log.log(packet)
|
||||
|
||||
if isinstance(packet, packets.AckPacket):
|
||||
# We don't need to drop AckPackets, those should be
|
||||
@ -142,14 +143,14 @@ class APRSDProcessPacketThread(APRSDThread):
|
||||
def process_ack_packet(self, packet):
|
||||
"""We got an ack for a message, no need to resend it."""
|
||||
ack_num = packet.msgNo
|
||||
LOG.info(f"Got ack for message {ack_num}")
|
||||
LOG.debug(f"Got ack for message {ack_num}")
|
||||
pkt_tracker = packets.PacketTrack()
|
||||
pkt_tracker.remove(ack_num)
|
||||
|
||||
def process_reject_packet(self, packet):
|
||||
"""We got a reject message for a packet. Stop sending the message."""
|
||||
ack_num = packet.msgNo
|
||||
LOG.info(f"Got REJECT for message {ack_num}")
|
||||
LOG.debug(f"Got REJECT for message {ack_num}")
|
||||
pkt_tracker = packets.PacketTrack()
|
||||
pkt_tracker.remove(ack_num)
|
||||
|
||||
|
@ -10,7 +10,9 @@ from rush.stores import dictionary
|
||||
from aprsd import client
|
||||
from aprsd import conf # noqa
|
||||
from aprsd import threads as aprsd_threads
|
||||
from aprsd.packets import core, tracker
|
||||
from aprsd.packets import core
|
||||
from aprsd.packets import log as packet_log
|
||||
from aprsd.packets import tracker
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -74,7 +76,7 @@ def _send_direct(packet, aprs_client=None):
|
||||
cl = client.factory.create()
|
||||
|
||||
packet.update_timestamp()
|
||||
packet.log(header="TX")
|
||||
packet_log.log(packet, tx=True)
|
||||
cl.send(packet)
|
||||
|
||||
|
||||
@ -163,7 +165,7 @@ class SendAckThread(aprsd_threads.APRSDThread):
|
||||
if self.packet.send_count == self.packet.retry_count:
|
||||
# we reached the send limit, don't send again
|
||||
# TODO(hemna) - Need to put this in a delayed queue?
|
||||
LOG.info(
|
||||
LOG.debug(
|
||||
f"{self.packet.__class__.__name__}"
|
||||
f"({self.packet.msgNo}) "
|
||||
"Send Complete. Max attempts reached"
|
||||
|
@ -1,9 +1,13 @@
|
||||
from multiprocessing import RawValue
|
||||
import random
|
||||
import threading
|
||||
|
||||
import wrapt
|
||||
|
||||
|
||||
MAX_PACKET_ID = 9999
|
||||
|
||||
|
||||
class PacketCounter:
|
||||
"""
|
||||
Global Packet id counter class.
|
||||
@ -17,19 +21,18 @@ class PacketCounter:
|
||||
"""
|
||||
|
||||
_instance = None
|
||||
max_count = 9999
|
||||
lock = threading.Lock()
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
"""Make this a singleton class."""
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls, *args, **kwargs)
|
||||
cls._instance.val = RawValue("i", 1)
|
||||
cls._instance.val = RawValue("i", random.randint(1, MAX_PACKET_ID))
|
||||
return cls._instance
|
||||
|
||||
@wrapt.synchronized(lock)
|
||||
def increment(self):
|
||||
if self.val.value == self.max_count:
|
||||
if self.val.value == MAX_PACKET_ID:
|
||||
self.val.value = 1
|
||||
else:
|
||||
self.val.value += 1
|
||||
|
@ -37,7 +37,7 @@ function start_update() {
|
||||
update_stats(data);
|
||||
},
|
||||
complete: function() {
|
||||
setTimeout(statsworker, 10000);
|
||||
setTimeout(statsworker, 60000);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
@ -22,7 +22,7 @@ CONF = cfg.CONF
|
||||
LOG = logging.getLogger("gunicorn.access")
|
||||
|
||||
auth = HTTPBasicAuth()
|
||||
users = {}
|
||||
users: dict[str, str] = {}
|
||||
app = Flask(
|
||||
"aprsd",
|
||||
static_url_path="/static",
|
||||
|
@ -4,209 +4,80 @@
|
||||
#
|
||||
# pip-compile --annotation-style=line dev-requirements.in
|
||||
#
|
||||
add-trailing-comma==3.1.0
|
||||
# via gray
|
||||
alabaster==0.7.16
|
||||
# via sphinx
|
||||
autoflake==1.5.3
|
||||
# via gray
|
||||
babel==2.14.0
|
||||
# via sphinx
|
||||
black==24.3.0
|
||||
# via gray
|
||||
build==1.1.1
|
||||
# via pip-tools
|
||||
cachetools==5.3.3
|
||||
# via tox
|
||||
certifi==2024.2.2
|
||||
# via requests
|
||||
cfgv==3.4.0
|
||||
# via pre-commit
|
||||
chardet==5.2.0
|
||||
# via tox
|
||||
charset-normalizer==3.3.2
|
||||
# via requests
|
||||
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.4.3
|
||||
# via pytest-cov
|
||||
distlib==0.3.8
|
||||
# via virtualenv
|
||||
docutils==0.20.1
|
||||
# via sphinx
|
||||
exceptiongroup==1.2.0
|
||||
# via pytest
|
||||
filelock==3.13.1
|
||||
# via
|
||||
# tox
|
||||
# virtualenv
|
||||
fixit==2.1.0
|
||||
# via gray
|
||||
flake8==7.0.0
|
||||
# via
|
||||
# -r dev-requirements.in
|
||||
# pep8-naming
|
||||
gray==0.14.0
|
||||
# via -r dev-requirements.in
|
||||
identify==2.5.35
|
||||
# via pre-commit
|
||||
idna==3.6
|
||||
# via requests
|
||||
imagesize==1.4.1
|
||||
# via sphinx
|
||||
iniconfig==2.0.0
|
||||
# via pytest
|
||||
isort==5.13.2
|
||||
# via
|
||||
# -r dev-requirements.in
|
||||
# gray
|
||||
jinja2==3.1.3
|
||||
# via sphinx
|
||||
libcst==1.2.0
|
||||
# via fixit
|
||||
markupsafe==2.1.5
|
||||
# via jinja2
|
||||
mccabe==0.7.0
|
||||
# via flake8
|
||||
moreorless==0.4.0
|
||||
# via fixit
|
||||
mypy==1.8.0
|
||||
# via -r dev-requirements.in
|
||||
mypy-extensions==1.0.0
|
||||
# via
|
||||
# black
|
||||
# mypy
|
||||
# typing-inspect
|
||||
nodeenv==1.8.0
|
||||
# via pre-commit
|
||||
packaging==23.2
|
||||
# via
|
||||
# black
|
||||
# build
|
||||
# fixit
|
||||
# pyproject-api
|
||||
# pytest
|
||||
# sphinx
|
||||
# tox
|
||||
pathspec==0.12.1
|
||||
# via
|
||||
# black
|
||||
# trailrunner
|
||||
pep8-naming==0.13.3
|
||||
# via -r dev-requirements.in
|
||||
pip-tools==7.4.1
|
||||
# via -r dev-requirements.in
|
||||
platformdirs==4.2.0
|
||||
# via
|
||||
# black
|
||||
# tox
|
||||
# virtualenv
|
||||
pluggy==1.4.0
|
||||
# via
|
||||
# pytest
|
||||
# tox
|
||||
pre-commit==3.6.2
|
||||
# via -r dev-requirements.in
|
||||
pycodestyle==2.11.1
|
||||
# via flake8
|
||||
pyflakes==3.2.0
|
||||
# via
|
||||
# autoflake
|
||||
# flake8
|
||||
pygments==2.17.2
|
||||
# via
|
||||
# rich
|
||||
# sphinx
|
||||
pyproject-api==1.6.1
|
||||
# via tox
|
||||
pyproject-hooks==1.0.0
|
||||
# via
|
||||
# build
|
||||
# pip-tools
|
||||
pytest==8.0.2
|
||||
# via
|
||||
# -r dev-requirements.in
|
||||
# pytest-cov
|
||||
pytest-cov==4.1.0
|
||||
# via -r dev-requirements.in
|
||||
pyupgrade==3.15.1
|
||||
# via gray
|
||||
pyyaml==6.0.1
|
||||
# via
|
||||
# libcst
|
||||
# pre-commit
|
||||
requests==2.31.0
|
||||
# via sphinx
|
||||
rich==12.6.0
|
||||
# via gray
|
||||
snowballstemmer==2.2.0
|
||||
# via sphinx
|
||||
sphinx==7.2.6
|
||||
# via -r dev-requirements.in
|
||||
sphinxcontrib-applehelp==1.0.8
|
||||
# via sphinx
|
||||
sphinxcontrib-devhelp==1.0.6
|
||||
# via sphinx
|
||||
sphinxcontrib-htmlhelp==2.0.5
|
||||
# via sphinx
|
||||
sphinxcontrib-jsmath==1.0.1
|
||||
# via sphinx
|
||||
sphinxcontrib-qthelp==1.0.7
|
||||
# via sphinx
|
||||
sphinxcontrib-serializinghtml==1.1.10
|
||||
# via sphinx
|
||||
tokenize-rt==5.2.0
|
||||
# via
|
||||
# add-trailing-comma
|
||||
# pyupgrade
|
||||
toml==0.10.2
|
||||
# via autoflake
|
||||
tomli==2.0.1
|
||||
# via
|
||||
# black
|
||||
# build
|
||||
# coverage
|
||||
# fixit
|
||||
# mypy
|
||||
# pip-tools
|
||||
# pyproject-api
|
||||
# pyproject-hooks
|
||||
# pytest
|
||||
# tox
|
||||
tox==4.14.0
|
||||
# via -r dev-requirements.in
|
||||
trailrunner==1.4.0
|
||||
# via fixit
|
||||
typing-extensions==4.10.0
|
||||
# via
|
||||
# black
|
||||
# libcst
|
||||
# mypy
|
||||
# typing-inspect
|
||||
typing-inspect==0.9.0
|
||||
# via libcst
|
||||
unify==0.5
|
||||
# via gray
|
||||
untokenize==0.1.1
|
||||
# via unify
|
||||
urllib3==2.2.1
|
||||
# via requests
|
||||
virtualenv==20.25.1
|
||||
# via
|
||||
# pre-commit
|
||||
# tox
|
||||
wheel==0.42.0
|
||||
# via pip-tools
|
||||
add-trailing-comma==3.1.0 # via gray
|
||||
alabaster==0.7.16 # via sphinx
|
||||
autoflake==1.5.3 # via gray
|
||||
babel==2.14.0 # via sphinx
|
||||
black==24.3.0 # via gray
|
||||
build==1.1.1 # via pip-tools
|
||||
cachetools==5.3.3 # via tox
|
||||
certifi==2024.2.2 # via requests
|
||||
cfgv==3.4.0 # via pre-commit
|
||||
chardet==5.2.0 # via tox
|
||||
charset-normalizer==3.3.2 # via requests
|
||||
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.4.4 # via pytest-cov
|
||||
distlib==0.3.8 # via virtualenv
|
||||
docutils==0.20.1 # via sphinx
|
||||
exceptiongroup==1.2.0 # via pytest
|
||||
filelock==3.13.1 # via tox, virtualenv
|
||||
fixit==2.1.0 # via gray
|
||||
flake8==7.0.0 # via -r dev-requirements.in, pep8-naming
|
||||
gray==0.14.0 # via -r dev-requirements.in
|
||||
identify==2.5.35 # via pre-commit
|
||||
idna==3.6 # via requests
|
||||
imagesize==1.4.1 # via sphinx
|
||||
iniconfig==2.0.0 # via pytest
|
||||
isort==5.13.2 # via -r dev-requirements.in, gray
|
||||
jinja2==3.1.3 # via sphinx
|
||||
libcst==1.2.0 # via fixit
|
||||
markupsafe==2.1.5 # via jinja2
|
||||
mccabe==0.7.0 # via flake8
|
||||
moreorless==0.4.0 # via fixit
|
||||
mypy==1.9.0 # via -r dev-requirements.in
|
||||
mypy-extensions==1.0.0 # via black, mypy, typing-inspect
|
||||
nodeenv==1.8.0 # via pre-commit
|
||||
packaging==24.0 # via black, build, fixit, pyproject-api, pytest, sphinx, tox
|
||||
pathspec==0.12.1 # via black, trailrunner
|
||||
pep8-naming==0.13.3 # via -r dev-requirements.in
|
||||
pip-tools==7.4.1 # via -r dev-requirements.in
|
||||
platformdirs==4.2.0 # via black, tox, virtualenv
|
||||
pluggy==1.4.0 # via pytest, tox
|
||||
pre-commit==3.6.2 # via -r dev-requirements.in
|
||||
pycodestyle==2.11.1 # via flake8
|
||||
pyflakes==3.2.0 # via autoflake, flake8
|
||||
pygments==2.17.2 # via rich, sphinx
|
||||
pyproject-api==1.6.1 # via tox
|
||||
pyproject-hooks==1.0.0 # via build, pip-tools
|
||||
pytest==8.1.1 # via -r dev-requirements.in, pytest-cov
|
||||
pytest-cov==4.1.0 # via -r dev-requirements.in
|
||||
pyupgrade==3.15.1 # via gray
|
||||
pyyaml==6.0.1 # via libcst, pre-commit
|
||||
requests==2.31.0 # via sphinx
|
||||
rich==12.6.0 # via gray
|
||||
snowballstemmer==2.2.0 # via sphinx
|
||||
sphinx==7.2.6 # via -r dev-requirements.in
|
||||
sphinxcontrib-applehelp==1.0.8 # via sphinx
|
||||
sphinxcontrib-devhelp==1.0.6 # via sphinx
|
||||
sphinxcontrib-htmlhelp==2.0.5 # via sphinx
|
||||
sphinxcontrib-jsmath==1.0.1 # via sphinx
|
||||
sphinxcontrib-qthelp==1.0.7 # via sphinx
|
||||
sphinxcontrib-serializinghtml==1.1.10 # via sphinx
|
||||
tokenize-rt==5.2.0 # via add-trailing-comma, pyupgrade
|
||||
toml==0.10.2 # via autoflake
|
||||
tomli==2.0.1 # via black, build, coverage, fixit, mypy, pip-tools, pyproject-api, pyproject-hooks, pytest, tox
|
||||
tox==4.14.1 # via -r dev-requirements.in
|
||||
trailrunner==1.4.0 # via fixit
|
||||
typing-extensions==4.10.0 # via black, libcst, mypy, typing-inspect
|
||||
typing-inspect==0.9.0 # via libcst
|
||||
unify==0.5 # via gray
|
||||
untokenize==0.1.1 # via unify
|
||||
urllib3==2.2.1 # via requests
|
||||
virtualenv==20.25.1 # via pre-commit, tox
|
||||
wheel==0.43.0 # via pip-tools
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# pip
|
||||
|
@ -28,7 +28,6 @@ wrapt
|
||||
kiss3
|
||||
attrs
|
||||
dataclasses
|
||||
dacite2
|
||||
oslo.config
|
||||
rpyc>=6.0.0
|
||||
# Pin this here so it doesn't require a compile on
|
||||
|
@ -17,7 +17,6 @@ click==8.1.7 # via -r requirements.in, click-completion, click-para
|
||||
click-completion==0.5.2 # via -r requirements.in
|
||||
click-params==0.5.0 # via -r requirements.in
|
||||
commonmark==0.9.1 # via rich
|
||||
dacite2==2.0.0 # via -r requirements.in
|
||||
dataclasses==0.6 # via -r requirements.in
|
||||
dataclasses-json==0.6.4 # via -r requirements.in
|
||||
debtcollector==3.0.0 # via oslo-config
|
||||
@ -34,7 +33,7 @@ greenlet==3.0.3 # via eventlet, gevent
|
||||
h11==0.14.0 # via wsproto
|
||||
idna==3.6 # via requests
|
||||
imapclient==3.0.1 # via -r requirements.in
|
||||
importlib-metadata==7.0.1 # via ax253, kiss3
|
||||
importlib-metadata==7.0.2 # via ax253, kiss3
|
||||
itsdangerous==2.1.2 # via flask
|
||||
jinja2==3.1.3 # via click-completion, flask
|
||||
kiss3==8.0.0 # via -r requirements.in
|
||||
@ -45,7 +44,7 @@ mypy-extensions==1.0.0 # via typing-inspect
|
||||
netaddr==1.2.1 # via oslo-config
|
||||
oslo-config==9.4.0 # via -r requirements.in
|
||||
oslo-i18n==6.3.0 # via oslo-config
|
||||
packaging==23.2 # via marshmallow
|
||||
packaging==24.0 # via marshmallow
|
||||
pbr==6.0.0 # via -r requirements.in, oslo-i18n, stevedore
|
||||
pluggy==1.4.0 # via -r requirements.in
|
||||
plumbum==1.8.2 # via rpyc
|
||||
@ -76,7 +75,7 @@ validators==0.22.0 # via click-params
|
||||
werkzeug==3.0.1 # via -r requirements.in, flask
|
||||
wrapt==1.16.0 # via -r requirements.in, debtcollector, deprecated
|
||||
wsproto==1.2.0 # via simple-websocket
|
||||
zipp==3.17.0 # via importlib-metadata
|
||||
zipp==3.18.1 # via importlib-metadata
|
||||
zope-event==5.0 # via gevent
|
||||
zope-interface==6.2 # via gevent
|
||||
|
||||
|
@ -51,11 +51,8 @@ class TestSendMessageCommand(unittest.TestCase):
|
||||
):
|
||||
self.config_and_init()
|
||||
mock_socketio.emit = mock.MagicMock()
|
||||
packet = fake.fake_packet(
|
||||
message="blah",
|
||||
msg_number=1,
|
||||
message_format=core.PACKET_TYPE_ACK,
|
||||
)
|
||||
# Create an ACK packet
|
||||
packet = fake.fake_ack_packet()
|
||||
mock_queue = mock.MagicMock()
|
||||
socketio = mock.MagicMock()
|
||||
wcp = webchat.WebChatProcessPacketThread(mock_queue, socketio)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from aprsd import packets, plugin, threads
|
||||
from aprsd import plugin, threads
|
||||
from aprsd.packets import core
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ def fake_packet(
|
||||
message=None,
|
||||
msg_number=None,
|
||||
message_format=core.PACKET_TYPE_MESSAGE,
|
||||
response=None,
|
||||
):
|
||||
packet_dict = {
|
||||
"from": fromcall,
|
||||
@ -27,7 +28,17 @@ def fake_packet(
|
||||
if msg_number:
|
||||
packet_dict["msgNo"] = str(msg_number)
|
||||
|
||||
return packets.Packet.factory(packet_dict)
|
||||
if response:
|
||||
packet_dict["response"] = response
|
||||
|
||||
return core.factory(packet_dict)
|
||||
|
||||
|
||||
def fake_ack_packet():
|
||||
return fake_packet(
|
||||
msg_number=12,
|
||||
response=core.PACKET_TYPE_ACK,
|
||||
)
|
||||
|
||||
|
||||
class FakeBaseNoThreadsPlugin(plugin.APRSDPluginBase):
|
||||
|
@ -11,7 +11,7 @@ from .. import fake, test_plugin
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestUSWeatherPluginPlugin(test_plugin.TestPlugin):
|
||||
class TestUSWeatherPlugin(test_plugin.TestPlugin):
|
||||
|
||||
def test_not_enabled_missing_aprs_fi_key(self):
|
||||
# When the aprs.fi api key isn't set, then
|
||||
|
@ -1,13 +1,16 @@
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import aprslib
|
||||
from aprslib import util as aprslib_util
|
||||
|
||||
from aprsd import packets
|
||||
from aprsd.packets import core
|
||||
|
||||
from . import fake
|
||||
|
||||
|
||||
class TestPluginBase(unittest.TestCase):
|
||||
class TestPacketBase(unittest.TestCase):
|
||||
|
||||
def _fake_dict(
|
||||
self,
|
||||
@ -55,7 +58,7 @@ class TestPluginBase(unittest.TestCase):
|
||||
|
||||
def test_packet_factory(self):
|
||||
pkt_dict = self._fake_dict()
|
||||
pkt = packets.Packet.factory(pkt_dict)
|
||||
pkt = packets.factory(pkt_dict)
|
||||
|
||||
self.assertIsInstance(pkt, packets.MessagePacket)
|
||||
self.assertEqual(fake.FAKE_FROM_CALLSIGN, pkt.from_call)
|
||||
@ -71,7 +74,7 @@ class TestPluginBase(unittest.TestCase):
|
||||
"comment": "Home!",
|
||||
}
|
||||
pkt_dict["format"] = core.PACKET_TYPE_UNCOMPRESSED
|
||||
pkt = packets.Packet.factory(pkt_dict)
|
||||
pkt = packets.factory(pkt_dict)
|
||||
self.assertIsInstance(pkt, packets.WeatherPacket)
|
||||
|
||||
@mock.patch("aprsd.packets.core.GPSPacket._build_time_zulu")
|
||||
@ -100,3 +103,183 @@ class TestPluginBase(unittest.TestCase):
|
||||
wx.prepare()
|
||||
expected = "KFAKE>KMINE,WIDE1-1,WIDE2-1:@221450z0.0/0.0_000/000g000t000r001p000P000h00b00000"
|
||||
self.assertEqual(expected, wx.raw)
|
||||
|
||||
def test_beacon_factory(self):
|
||||
"""Test to ensure a beacon packet is created."""
|
||||
packet_raw = "WB4BOR-12>APZ100,WIDE2-1:@161647z3724.15N107847.58W$ APRSD WebChat"
|
||||
packet_dict = aprslib.parse(packet_raw)
|
||||
packet = packets.factory(packet_dict)
|
||||
self.assertIsInstance(packet, packets.BeaconPacket)
|
||||
|
||||
packet_raw = "kd8mey-10>APRS,TCPIP*,qAC,T2SYDNEY:=4247.80N/08539.00WrPHG1210/Making 220 Great Again Allstar# 552191"
|
||||
packet_dict = aprslib.parse(packet_raw)
|
||||
packet = packets.factory(packet_dict)
|
||||
self.assertIsInstance(packet, packets.BeaconPacket)
|
||||
|
||||
def test_reject_factory(self):
|
||||
"""Test to ensure a reject packet is created."""
|
||||
packet_raw = "HB9FDL-1>APK102,HB9FM-4*,WIDE2,qAR,HB9FEF-11::REPEAT :rej4139"
|
||||
packet_dict = aprslib.parse(packet_raw)
|
||||
packet = packets.factory(packet_dict)
|
||||
self.assertIsInstance(packet, packets.RejectPacket)
|
||||
|
||||
self.assertEqual("4139", packet.msgNo)
|
||||
self.assertEqual("HB9FDL-1", packet.from_call)
|
||||
self.assertEqual("REPEAT", packet.to_call)
|
||||
self.assertEqual("reject", packet.packet_type)
|
||||
self.assertIsNone(packet.payload)
|
||||
|
||||
def test_thirdparty_factory(self):
|
||||
"""Test to ensure a third party packet is created."""
|
||||
packet_raw = "GTOWN>APDW16,WIDE1-1,WIDE2-1:}KM6LYW-9>APZ100,TCPIP,GTOWN*::KM6LYW :KM6LYW: 19 Miles SW"
|
||||
packet_dict = aprslib.parse(packet_raw)
|
||||
packet = packets.factory(packet_dict)
|
||||
self.assertIsInstance(packet, packets.ThirdPartyPacket)
|
||||
|
||||
def test_weather_factory(self):
|
||||
"""Test to ensure a weather packet is created."""
|
||||
packet_raw = "FW9222>APRS,TCPXX*,qAX,CWOP-6:@122025z2953.94N/08423.77W_232/003g006t084r000p032P000h80b10157L745.DsWLL"
|
||||
packet_dict = aprslib.parse(packet_raw)
|
||||
packet = packets.factory(packet_dict)
|
||||
self.assertIsInstance(packet, packets.WeatherPacket)
|
||||
|
||||
self.assertEqual(28.88888888888889, packet.temperature)
|
||||
self.assertEqual(0.0, packet.rain_1h)
|
||||
self.assertEqual(1015.7, packet.pressure)
|
||||
self.assertEqual(80, packet.humidity)
|
||||
self.assertEqual(745, packet.luminosity)
|
||||
self.assertEqual(3.0, packet.wind_speed)
|
||||
self.assertEqual(232, packet.wind_direction)
|
||||
self.assertEqual(6.0, packet.wind_gust)
|
||||
self.assertEqual(29.899, packet.latitude)
|
||||
self.assertEqual(-84.39616666666667, packet.longitude)
|
||||
|
||||
def test_mice_factory(self):
|
||||
packet_raw = 'kh2sr-15>S7TSYR,WIDE1-1,WIDE2-1,qAO,KO6KL-1:`1`7\x1c\x1c.#/`"4,}QuirkyQRP 4.6V 35.3C S06'
|
||||
packet_dict = aprslib.parse(packet_raw)
|
||||
packet = packets.factory(packet_dict)
|
||||
self.assertIsInstance(packet, packets.MicEPacket)
|
||||
|
||||
# Packet with telemetry and DAO
|
||||
# http://www.aprs.org/datum.txt
|
||||
packet_raw = 'KD9YIL>T0PX9W,WIDE1-1,WIDE2-1,qAO,NU9R-10:`sB,l#P>/\'"6+}|#*%U\'a|!whl!|3'
|
||||
packet_dict = aprslib.parse(packet_raw)
|
||||
packet = packets.factory(packet_dict)
|
||||
self.assertIsInstance(packet, packets.MicEPacket)
|
||||
|
||||
def test_ack_format(self):
|
||||
"""Test the ack packet format."""
|
||||
ack = packets.AckPacket(
|
||||
from_call=fake.FAKE_FROM_CALLSIGN,
|
||||
to_call=fake.FAKE_TO_CALLSIGN,
|
||||
msgNo=123,
|
||||
)
|
||||
|
||||
expected = f"{fake.FAKE_FROM_CALLSIGN}>APZ100::{fake.FAKE_TO_CALLSIGN:<9}:ack123"
|
||||
self.assertEqual(expected, str(ack))
|
||||
|
||||
def test_reject_format(self):
|
||||
"""Test the reject packet format."""
|
||||
reject = packets.RejectPacket(
|
||||
from_call=fake.FAKE_FROM_CALLSIGN,
|
||||
to_call=fake.FAKE_TO_CALLSIGN,
|
||||
msgNo=123,
|
||||
)
|
||||
|
||||
expected = f"{fake.FAKE_FROM_CALLSIGN}>APZ100::{fake.FAKE_TO_CALLSIGN:<9}:rej123"
|
||||
self.assertEqual(expected, str(reject))
|
||||
|
||||
def test_beacon_format(self):
|
||||
"""Test the beacon packet format."""
|
||||
lat = 28.123456
|
||||
lon = -80.123456
|
||||
ts = 1711219496.6426
|
||||
comment = "My Beacon Comment"
|
||||
packet = packets.BeaconPacket(
|
||||
from_call=fake.FAKE_FROM_CALLSIGN,
|
||||
to_call=fake.FAKE_TO_CALLSIGN,
|
||||
latitude=lat,
|
||||
longitude=lon,
|
||||
timestamp=ts,
|
||||
symbol=">",
|
||||
comment=comment,
|
||||
)
|
||||
|
||||
expected_lat = aprslib_util.latitude_to_ddm(lat)
|
||||
expected_lon = aprslib_util.longitude_to_ddm(lon)
|
||||
expected = f"KFAKE>APZ100:@231844z{expected_lat}/{expected_lon}>{comment}"
|
||||
self.assertEqual(expected, str(packet))
|
||||
|
||||
def test_beacon_format_no_comment(self):
|
||||
"""Test the beacon packet format."""
|
||||
lat = 28.123456
|
||||
lon = -80.123456
|
||||
ts = 1711219496.6426
|
||||
packet = packets.BeaconPacket(
|
||||
from_call=fake.FAKE_FROM_CALLSIGN,
|
||||
to_call=fake.FAKE_TO_CALLSIGN,
|
||||
latitude=lat,
|
||||
longitude=lon,
|
||||
timestamp=ts,
|
||||
symbol=">",
|
||||
)
|
||||
empty_comment = "APRSD Beacon"
|
||||
|
||||
expected_lat = aprslib_util.latitude_to_ddm(lat)
|
||||
expected_lon = aprslib_util.longitude_to_ddm(lon)
|
||||
expected = f"KFAKE>APZ100:@231844z{expected_lat}/{expected_lon}>{empty_comment}"
|
||||
self.assertEqual(expected, str(packet))
|
||||
|
||||
def test_bulletin_format(self):
|
||||
"""Test the bulletin packet format."""
|
||||
# bulletin id = 0
|
||||
bid = 0
|
||||
packet = packets.BulletinPacket(
|
||||
from_call=fake.FAKE_FROM_CALLSIGN,
|
||||
message_text="My Bulletin Message",
|
||||
bid=0,
|
||||
)
|
||||
|
||||
expected = f"{fake.FAKE_FROM_CALLSIGN}>APZ100::BLN{bid:<9}:{packet.message_text}"
|
||||
self.assertEqual(expected, str(packet))
|
||||
|
||||
# bulletin id = 1
|
||||
bid = 1
|
||||
txt = "((((((( CX2SA - Salto Uruguay ))))))) http://www.cx2sa.org"
|
||||
packet = packets.BulletinPacket(
|
||||
from_call=fake.FAKE_FROM_CALLSIGN,
|
||||
message_text=txt,
|
||||
bid=1,
|
||||
)
|
||||
|
||||
expected = f"{fake.FAKE_FROM_CALLSIGN}>APZ100::BLN{bid:<9}:{txt}"
|
||||
self.assertEqual(expected, str(packet))
|
||||
|
||||
def test_message_format(self):
|
||||
"""Test the message packet format."""
|
||||
|
||||
message = "My Message"
|
||||
msgno = "ABX"
|
||||
packet = packets.MessagePacket(
|
||||
from_call=fake.FAKE_FROM_CALLSIGN,
|
||||
to_call=fake.FAKE_TO_CALLSIGN,
|
||||
message_text=message,
|
||||
msgNo=msgno,
|
||||
)
|
||||
|
||||
expected = f"{fake.FAKE_FROM_CALLSIGN}>APZ100::{fake.FAKE_TO_CALLSIGN:<9}:{message}{{{msgno}"
|
||||
self.assertEqual(expected, str(packet))
|
||||
|
||||
# test with bad words
|
||||
# Currently fails with mixed case
|
||||
message = "My cunt piss fuck shIt text"
|
||||
exp_msg = "My **** **** **** **** text"
|
||||
msgno = "ABX"
|
||||
packet = packets.MessagePacket(
|
||||
from_call=fake.FAKE_FROM_CALLSIGN,
|
||||
to_call=fake.FAKE_TO_CALLSIGN,
|
||||
message_text=message,
|
||||
msgNo=msgno,
|
||||
)
|
||||
expected = f"{fake.FAKE_FROM_CALLSIGN}>APZ100::{fake.FAKE_TO_CALLSIGN:<9}:{exp_msg}{{{msgno}"
|
||||
self.assertEqual(expected, str(packet))
|
||||
|
@ -45,7 +45,6 @@ class TestPluginManager(unittest.TestCase):
|
||||
self.assertEqual([], plugin_list)
|
||||
pm.setup_plugins()
|
||||
plugin_list = pm.get_plugins()
|
||||
print(plugin_list)
|
||||
self.assertIsInstance(plugin_list, list)
|
||||
self.assertIsInstance(
|
||||
plugin_list[0],
|
||||
@ -163,9 +162,7 @@ class TestPluginBase(TestPlugin):
|
||||
self.assertEqual(expected, actual)
|
||||
mock_process.assert_not_called()
|
||||
|
||||
packet = fake.fake_packet(
|
||||
message_format=core.PACKET_TYPE_ACK,
|
||||
)
|
||||
packet = fake.fake_ack_packet()
|
||||
expected = packets.NULL_MESSAGE
|
||||
actual = p.filter(packet)
|
||||
self.assertEqual(expected, actual)
|
||||
|
Loading…
Reference in New Issue
Block a user