mirror of
https://github.com/craigerl/aprsd.git
synced 2024-11-25 01:18:43 -05:00
Compare commits
8 Commits
6a5250ad1d
...
4313fb8940
Author | SHA1 | Date | |
---|---|---|---|
|
4313fb8940 | ||
8cdbf18bef | |||
a65262d2ff | |||
9951b12e2d | |||
3e9bf2422a | |||
5e9f92dfa6 | |||
5314856101 | |||
|
1334eded62 |
2
Makefile
2
Makefile
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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 = [
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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()
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user