1
0
mirror of https://github.com/craigerl/aprsd.git synced 2025-11-17 21:53:26 -05:00
Hemna c201c93b5d Cleaned up packet transmit class attributes
This patch cleans up the Packet class attributes used to
keep track of how many times packets have been sent and
the last time they were sent.  This is used by the PacketTracker
and the tx threads for transmitting packets
2022-12-17 18:06:24 -05:00

241 lines
8.7 KiB
Python

import abc
import logging
import time
import aprslib
from aprsd import client, packets, plugin, stats
from aprsd.threads import APRSDThread
LOG = logging.getLogger("APRSD")
class APRSDRXThread(APRSDThread):
def __init__(self, msg_queues, config):
super().__init__("RX_MSG")
self.msg_queues = msg_queues
self.config = config
self._client = client.factory.create()
def stop(self):
self.thread_stop = True
client.factory.create().client.stop()
def loop(self):
# setup the consumer of messages and block until a messages
try:
# This will register a packet consumer with aprslib
# When new packets come in the consumer will process
# the packet
# Do a partial here because the consumer signature doesn't allow
# For kwargs to be passed in to the consumer func we declare
# and the aprslib developer didn't want to allow a PR to add
# kwargs. :(
# https://github.com/rossengeorgiev/aprs-python/pull/56
self._client.client.consumer(
self.process_packet, raw=False, blocking=False,
)
except (
aprslib.exceptions.ConnectionDrop,
aprslib.exceptions.ConnectionError,
):
LOG.error("Connection dropped, reconnecting")
time.sleep(5)
# Force the deletion of the client object connected to aprs
# This will cause a reconnect, next time client.get_client()
# is called
self._client.reset()
# Continue to loop
return True
@abc.abstractmethod
def process_packet(self, *args, **kwargs):
pass
class APRSDPluginRXThread(APRSDRXThread):
"""Process received packets.
This is the main APRSD Server command thread that
receives packets from APRIS and then sends them for
processing in the PluginProcessPacketThread.
"""
def process_packet(self, *args, **kwargs):
packet = self._client.decode_packet(*args, **kwargs)
# LOG.debug(raw)
packet.log(header="RX")
thread = APRSDPluginProcessPacketThread(
config=self.config,
packet=packet,
)
thread.start()
class APRSDProcessPacketThread(APRSDThread):
"""Base class for processing received packets.
This is the base class for processing packets coming from
the consumer. This base class handles sending ack packets and
will ack a message before sending the packet to the subclass
for processing."""
def __init__(self, config, packet):
self.config = config
self.packet = packet
name = self.packet.raw[:10]
super().__init__(f"RXPKT-{name}")
self._loop_cnt = 1
def process_ack_packet(self, packet):
ack_num = packet.msgNo
LOG.info(f"Got ack for message {ack_num}")
pkt_tracker = packets.PacketTrack()
pkt_tracker.remove(ack_num)
stats.APRSDStats().ack_rx_inc()
return
def loop(self):
"""Process a packet received from aprs-is server."""
LOG.debug(f"RXPKT-LOOP {self._loop_cnt}")
packet = self.packet
packets.PacketList().add(packet)
our_call = self.config["aprsd"]["callsign"].lower()
from_call = packet.from_call
if packet.addresse:
to_call = packet.addresse
else:
to_call = packet.to_call
msg_id = packet.msgNo
# We don't put ack packets destined for us through the
# plugins.
wl = packets.WatchList()
wl.update_seen(packet)
if (
isinstance(packet, packets.AckPacket)
and packet.addresse.lower() == our_call
):
self.process_ack_packet(packet)
else:
# Only ack messages that were sent directly to us
if isinstance(packet, packets.MessagePacket):
if to_call and to_call.lower() == our_call:
# It's a MessagePacket and it's for us!
stats.APRSDStats().msgs_rx_inc()
# let any threads do their thing, then ack
# send an ack last
ack_pkt = packets.AckPacket(
from_call=self.config["aprsd"]["callsign"],
to_call=from_call,
msgNo=msg_id,
)
ack_pkt.send()
self.process_our_message_packet(packet)
else:
# Packet wasn't meant for us!
self.process_other_packet(packet, for_us=False)
else:
self.process_other_packet(
packet, for_us=(to_call.lower() == our_call),
)
LOG.debug("Packet processing complete")
return False
@abc.abstractmethod
def process_our_message_packet(self, *args, **kwargs):
"""Process a MessagePacket destined for us!"""
def process_other_packet(self, packet, for_us=False):
"""Process an APRS Packet that isn't a message or ack"""
if not for_us:
LOG.info("Got a packet not meant for us.")
else:
LOG.info("Got a non AckPacket/MessagePacket")
LOG.info(packet)
class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
"""Process the packet through the plugin manager.
This is the main aprsd server plugin processing thread."""
def process_our_message_packet(self, packet):
"""Send the packet through the plugins."""
from_call = packet.from_call
if packet.addresse:
to_call = packet.addresse
else:
to_call = None
# msg = packet.get("message_text", None)
# packet.get("msgNo", "0")
# packet.get("response", None)
pm = plugin.PluginManager()
try:
results = pm.run(packet)
replied = False
for reply in results:
if isinstance(reply, list):
# one of the plugins wants to send multiple messages
replied = True
for subreply in reply:
LOG.debug(f"Sending '{subreply}'")
if isinstance(subreply, packets.Packet):
subreply.send()
else:
msg_pkt = packets.MessagePacket(
from_call=self.config["aprsd"]["callsign"],
to_call=from_call,
message_text=subreply,
)
msg_pkt.send()
elif isinstance(reply, packets.Packet):
# We have a message based object.
reply.send()
replied = True
else:
replied = True
# A plugin can return a null message flag which signals
# us that they processed the message correctly, but have
# nothing to reply with, so we avoid replying with a
# usage string
if reply is not packets.NULL_MESSAGE:
LOG.debug(f"Sending '{reply}'")
msg_pkt = packets.MessagePacket(
from_call=self.config["aprsd"]["callsign"],
to_call=from_call,
message_text=reply,
)
LOG.warning("Calling msg_pkg.send()")
msg_pkt.send()
LOG.warning("Calling msg_pkg.send() --- DONE")
# If the message was for us and we didn't have a
# response, then we send a usage statement.
if to_call == self.config["aprsd"]["callsign"] and not replied:
LOG.warning("Sending help!")
msg_pkt = packets.MessagePacket(
from_call=self.config["aprsd"]["callsign"],
to_call=from_call,
message_text="Unknown command! Send 'help' message for help",
)
msg_pkt.send()
except Exception as ex:
LOG.error("Plugin failed!!!")
LOG.exception(ex)
# Do we need to send a reply?
if to_call == self.config["aprsd"]["callsign"]:
reply = "A Plugin failed! try again?"
msg_pkt = packets.MessagePacket(
from_call=self.config["aprsd"]["callsign"],
to_call=from_call,
message_text=reply,
)
msg_pkt.send()
LOG.debug("Completed process_our_message_packet")