1
0
mirror of https://github.com/craigerl/aprsd.git synced 2024-11-25 01:18:43 -05:00

Compare commits

...

8 Commits

Author SHA1 Message Date
afourney
4313fb8940
Merge 1334eded62 into 8cdbf18bef 2024-10-17 23:22:28 -07:00
8cdbf18bef Add final stages in Dockerfile
This patch adds another final stage in the Dockerfile
2024-10-17 17:10:59 -04:00
a65262d2ff Sort changelog commits by date 2024-10-17 17:10:03 -04:00
9951b12e2d Log closing client connection.
This patch updates the aprsis connection client to add logging
when the close() happens
2024-10-17 17:09:11 -04:00
3e9bf2422a Added packet log distance and new arrows
this patch adds unicode arrows during logging of packet arrows
(tx/rx) and adds distance for GPSPackets
2024-10-17 17:06:28 -04:00
5e9f92dfa6 Added color logging of thread names at keepalive
This patch adds logging of the thread name in color
during keepalive loop output.
2024-10-17 17:04:33 -04:00
5314856101 Removed dumping of the stats on exit
This patch removes the logging of the raw stats dict when the commands
exit.
2024-10-17 17:01:36 -04:00
Adam Fourney
1334eded62 Added an option to disable the loading of the help plugin. 2024-09-26 11:24:16 -07:00
12 changed files with 130 additions and 18 deletions

View File

@ -24,7 +24,7 @@ run: venv ## Create a virtual environment for running aprsd commands
changelog: dev changelog: dev
npm i -g auto-changelog npm i -g auto-changelog
auto-changelog -l false -o ChangeLog.md auto-changelog -l false --sort-commits date -o ChangeLog.md
docs: changelog docs: changelog
m2r --overwrite ChangeLog.md m2r --overwrite ChangeLog.md

View File

@ -33,7 +33,11 @@ class Aprsdis(aprslib.IS):
def stop(self): def stop(self):
self.thread_stop = True self.thread_stop = True
LOG.info("Shutdown Aprsdis client.") LOG.warning("Shutdown Aprsdis client.")
def close(self):
LOG.warning("Closing Aprsdis client.")
super().close()
@wrapt.synchronized(lock) @wrapt.synchronized(lock)
def send(self, packet: core.Packet): def send(self, packet: core.Packet):

View File

@ -42,7 +42,8 @@ def signal_handler(sig, frame):
), ),
) )
time.sleep(5) time.sleep(5)
LOG.info(collector.Collector().collect()) # Last save to disk
collector.Collector().collect()
class APRSDListenThread(rx.APRSDRXThread): class APRSDListenThread(rx.APRSDRXThread):

View File

@ -64,7 +64,7 @@ def signal_handler(sig, frame):
time.sleep(1.5) time.sleep(1.5)
# packets.WatchList().save() # packets.WatchList().save()
# packets.SeenList().save() # packets.SeenList().save()
LOG.info(stats.stats_collector.collect()) stats.stats_collector.collect()
LOG.info("Telling flask to bail.") LOG.info("Telling flask to bail.")
signal.signal(signal.SIGTERM, sys.exit(0)) signal.signal(signal.SIGTERM, sys.exit(0))

View File

@ -136,6 +136,11 @@ aprsd_opts = [
default=True, default=True,
help="Set this to False, to disable logging of packets to the log file.", 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 = [ watch_list_opts = [

View File

@ -83,7 +83,7 @@ def signal_handler(sig, frame):
packets.WatchList().save() packets.WatchList().save()
packets.SeenList().save() packets.SeenList().save()
packets.PacketList().save() packets.PacketList().save()
LOG.info(collector.Collector().collect()) collector.Collector().collect()
# signal.signal(signal.SIGTERM, sys.exit(0)) # signal.signal(signal.SIGTERM, sys.exit(0))
# sys.exit(0) # sys.exit(0)

View File

@ -1,10 +1,12 @@
import logging import logging
from typing import Optional from typing import Optional
from geopy.distance import geodesic
from loguru import logger from loguru import logger
from oslo_config import cfg from oslo_config import cfg
from aprsd.packets.core import AckPacket, RejectPacket from aprsd import utils
from aprsd.packets.core import AckPacket, GPSPacket, RejectPacket
LOG = logging.getLogger() LOG = logging.getLogger()
@ -16,6 +18,8 @@ TO_COLOR = "fg #D033FF"
TX_COLOR = "red" TX_COLOR = "red"
RX_COLOR = "green" RX_COLOR = "green"
PACKET_COLOR = "cyan" PACKET_COLOR = "cyan"
DISTANCE_COLOR = "fg #FF5733"
DEGREES_COLOR = "fg #FFA900"
def log_multiline(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> None: def log_multiline(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> None:
@ -97,19 +101,21 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
if header: if header:
if tx: if tx:
via_color = "red" via_color = "red"
arrow = f"<{via_color}>-></{via_color}>" # arrow = f"<{via_color}>-></{via_color}>"
arrow = f"<{via_color}>\u2192</{via_color}>"
logit.append( logit.append(
f"<red>TX {arrow}</red> " f"<red>TX\u2191</red> "
f"<cyan>{name}</cyan>" f"<cyan>{name}</cyan>"
f":{packet.msgNo}" f":{packet.msgNo}"
f" ({packet.send_count + 1} of {pkt_max_send_count})", f" ({packet.send_count + 1} of {pkt_max_send_count})",
) )
else: else:
via_color = "fg #828282" via_color = "fg #1AA730"
arrow = f"<{via_color}>-></{via_color}>" #arrow = f"<{via_color}>-></{via_color}>"
left_arrow = f"<{via_color}><-</{via_color}>" arrow = f"<{via_color}>\u2192</{via_color}>"
f"<{via_color}><-</{via_color}>"
logit.append( logit.append(
f"<fg #1AA730>RX</fg #1AA730> {left_arrow} " f"<fg #1AA730>RX\u2193</fg #1AA730> "
f"<cyan>{name}</cyan>" f"<cyan>{name}</cyan>"
f":{packet.msgNo}", f":{packet.msgNo}",
) )
@ -139,5 +145,19 @@ def log(packet, tx: Optional[bool] = False, header: Optional[bool] = True) -> No
msg = msg.replace("<", "\\<") msg = msg.replace("<", "\\<")
logit.append(f"<light-yellow><b>{msg}</b></light-yellow>") logit.append(f"<light-yellow><b>{msg}</b></light-yellow>")
# is there distance information?
if isinstance(packet, GPSPacket) and CONF.latitude and CONF.longitude:
my_coords = (CONF.latitude, CONF.longitude)
packet_coords = (packet.latitude, packet.longitude)
try:
bearing = utils.calculate_initial_compass_bearing(my_coords, packet_coords)
except Exception as e:
LOG.error(f"Failed to calculate bearing: {e}")
bearing = 0
logit.append(
f" : <{DEGREES_COLOR}>{utils.degrees_to_cardinal(bearing, full_string=True)}</{DEGREES_COLOR}>"
f"<{DISTANCE_COLOR}>@{geodesic(my_coords, packet_coords).miles:.2f}miles</{DISTANCE_COLOR}>",
)
LOGU.opt(colors=True).info(" ".join(logit)) LOGU.opt(colors=True).info(" ".join(logit))
log_multiline(packet, tx, header) log_multiline(packet, tx, header)

View File

@ -472,9 +472,13 @@ class PluginManager:
del self._pluggy_pm del self._pluggy_pm
self.setup_plugins() 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.""" """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") LOG.info("Loading APRSD Plugins")
# Help plugin is always enabled. # Help plugin is always enabled.
if load_help_plugin: if load_help_plugin:

View File

@ -3,6 +3,7 @@ import logging
import time import time
import tracemalloc import tracemalloc
from loguru import logger
from oslo_config import cfg from oslo_config import cfg
from aprsd import packets, utils from aprsd import packets, utils
@ -14,6 +15,7 @@ from aprsd.threads import APRSDThread, APRSDThreadList
CONF = cfg.CONF CONF = cfg.CONF
LOG = logging.getLogger("APRSD") LOG = logging.getLogger("APRSD")
LOGU = logger
class KeepAliveThread(APRSDThread): class KeepAliveThread(APRSDThread):
@ -87,7 +89,12 @@ class KeepAliveThread(APRSDThread):
key = thread["name"] key = thread["name"]
if not alive: if not alive:
LOG.error(f"Thread {thread}") LOG.error(f"Thread {thread}")
LOG.info(f"{key: <15} Alive? {str(alive): <5} {str(age): <20}")
thread_hex = f"fg {utils.hex_from_name(key)}"
t_name = f"<{thread_hex}>{key:<15}</{thread_hex}>"
thread_msg = f"{t_name} Alive? {str(alive): <5} {str(age): <20}"
LOGU.opt(colors=True).info(thread_msg)
# LOG.info(f"{key: <15} Alive? {str(alive): <5} {str(age): <20}")
# check the APRS connection # check the APRS connection
cl = client_factory.create() cl = client_factory.create()

View File

@ -328,8 +328,22 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
# If the message was for us and we didn't have a # If the message was for us and we didn't have a
# response, then we send a usage statement. # response, then we send a usage statement.
if to_call == CONF.callsign and not replied: 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( tx.send(
packets.MessagePacket( packets.MessagePacket(
from_call=CONF.callsign, from_call=CONF.callsign,

View File

@ -2,6 +2,7 @@
import errno import errno
import functools import functools
import math
import os import os
import re import re
import sys import sys
@ -82,6 +83,16 @@ def rgb_from_name(name):
return red, green, blue return red, green, blue
def hextriplet(colortuple):
"""Convert a color tuple to a hex triplet."""
return "#" + "".join(f"{i:02X}" for i in colortuple)
def hex_from_name(name):
"""Create a hex color from a string."""
return hextriplet(rgb_from_name(name))
def human_size(bytes, units=None): def human_size(bytes, units=None):
"""Returns a human readable string representation of bytes""" """Returns a human readable string representation of bytes"""
if not units: if not units:
@ -161,3 +172,47 @@ def load_entry_points(group):
except Exception as e: except Exception as e:
print(f"Extension {ep.name} of group {group} failed to load with {e}", file=sys.stderr) print(f"Extension {ep.name} of group {group} failed to load with {e}", file=sys.stderr)
print(traceback.format_exc(), file=sys.stderr) print(traceback.format_exc(), file=sys.stderr)
def calculate_initial_compass_bearing(start, end):
if (type(start) != tuple) or (type(end) != tuple):
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]))
x = math.sin(diffLong) * math.cos(lat2)
y = math.cos(lat1) * math.sin(lat2) - (
math.sin(lat1)
* math.cos(lat2) * math.cos(diffLong)
)
initial_bearing = math.atan2(x, y)
# Now we have the initial bearing but math.atan2 return values
# from -180° to + 180° which is not what we want for a compass bearing
# The solution is to normalize the initial bearing as shown below
initial_bearing = math.degrees(initial_bearing)
compass_bearing = (initial_bearing + 360) % 360
return compass_bearing
def degrees_to_cardinal(bearing, full_string=False):
if full_string:
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 = [
"N", "NNE", "NE", "ENE", "E", "ESE",
"SE", "SSE", "S", "SSW", "SW", "WSW",
"W", "WNW", "NW", "NNW", "N",
]
cardinal = DIRECTIONS[round(bearing / 22.5)]
return cardinal

View File

@ -1,4 +1,4 @@
FROM python:3.11-slim as build FROM python:3.11-slim AS build
ARG VERSION=3.4.0 ARG VERSION=3.4.0
# pass this in as 'dev' if you want to install from github repo vs pypi # pass this in as 'dev' if you want to install from github repo vs pypi
@ -40,7 +40,7 @@ RUN set -ex \
### Final stage ### Final stage
FROM build as final FROM build AS install
WORKDIR /app WORKDIR /app
RUN pip3 install -U pip RUN pip3 install -U pip
@ -64,6 +64,8 @@ RUN aprsd --version
ADD bin/setup.sh /app ADD bin/setup.sh /app
ADD bin/admin.sh /app ADD bin/admin.sh /app
FROM install AS final
# For the web admin interface # For the web admin interface
EXPOSE 8001 EXPOSE 8001