mirror of
https://github.com/craigerl/aprsd.git
synced 2025-06-17 05:42:36 -04:00
Got TX/RX working with aioax25+direwolf over TCP
This patch gets APRSD fully working with the TCPKISS socket to direwolf.
This commit is contained in:
parent
54c9a6b55a
commit
f4dee4b202
@ -1,7 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from aioax25 import frame as axframe
|
|
||||||
from aioax25 import interface
|
from aioax25 import interface
|
||||||
from aioax25 import kiss as kiss
|
from aioax25 import kiss as kiss
|
||||||
from aioax25.aprs import APRSInterface
|
from aioax25.aprs import APRSInterface
|
||||||
@ -38,7 +37,7 @@ class KISSClient:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
if "tcp" in config["kiss"]:
|
if "tcp" in config["kiss"]:
|
||||||
if config["kiss"]["serial"].get("enabled", False):
|
if config["kiss"]["tcp"].get("enabled", False):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -88,14 +87,15 @@ class Aioax25Client:
|
|||||||
):
|
):
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Setting up KISSTCP Connection to {}:{}".format(
|
"Setting up KISSTCP Connection to {}:{}".format(
|
||||||
self.config["kiss"]["host"],
|
self.config["kiss"]["tcp"]["host"],
|
||||||
self.config["kiss"]["port"],
|
self.config["kiss"]["tcp"]["port"],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self.kissdev = kiss.TCPKISSDevice(
|
self.kissdev = kiss.TCPKISSDevice(
|
||||||
self.config["kiss"]["host"],
|
self.config["kiss"]["tcp"]["host"],
|
||||||
self.config["kiss"]["port"],
|
self.config["kiss"]["tcp"]["port"],
|
||||||
loop=self.loop,
|
loop=self.loop,
|
||||||
|
log=LOG,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.kissdev.open()
|
self.kissdev.open()
|
||||||
@ -107,7 +107,7 @@ class Aioax25Client:
|
|||||||
LOG.debug("Creating APRSInterface")
|
LOG.debug("Creating APRSInterface")
|
||||||
self.aprsint = APRSInterface(
|
self.aprsint = APRSInterface(
|
||||||
ax25int=self.ax25int,
|
ax25int=self.ax25int,
|
||||||
mycall=self.config["ham"]["callsign"],
|
mycall=self.config["kiss"]["callsign"],
|
||||||
log=LOG,
|
log=LOG,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -119,20 +119,17 @@ class Aioax25Client:
|
|||||||
def consumer(self, callback, callsign=None):
|
def consumer(self, callback, callsign=None):
|
||||||
if not callsign:
|
if not callsign:
|
||||||
callsign = self.config["ham"]["callsign"]
|
callsign = self.config["ham"]["callsign"]
|
||||||
self.aprsint.bind(callback=callback, callsign=callsign, regex=True)
|
self.aprsint.bind(callback=callback, callsign="WB4BOR", ssid=12, regex=False)
|
||||||
|
|
||||||
def send(self, msg):
|
def send(self, msg):
|
||||||
"""Send an APRS Message object."""
|
"""Send an APRS Message object."""
|
||||||
payload = msg._filter_for_send()
|
payload = f"{msg._filter_for_send()}"
|
||||||
frame = axframe.AX25UnnumberedInformationFrame(
|
self.aprsint.send_message(
|
||||||
msg.tocall,
|
addressee=msg.tocall,
|
||||||
msg.fromcall.encode("UTF-8"),
|
message=payload,
|
||||||
pid=0xF0,
|
path=["WIDE1-1", "WIDE2-1"],
|
||||||
repeaters=b"WIDE2-1",
|
oneshot=True,
|
||||||
payload=payload,
|
|
||||||
)
|
)
|
||||||
LOG.debug(frame)
|
|
||||||
self.ax25int.transmit(frame)
|
|
||||||
|
|
||||||
|
|
||||||
def get_client():
|
def get_client():
|
||||||
|
@ -469,6 +469,12 @@ def server(
|
|||||||
cl.client
|
cl.client
|
||||||
except LoginError:
|
except LoginError:
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
rx_thread = threads.APRSDRXThread(
|
||||||
|
msg_queues=threads.msg_queues,
|
||||||
|
config=config,
|
||||||
|
)
|
||||||
|
rx_thread.start()
|
||||||
else:
|
else:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"APRS network connection Not Enabled in config. This is"
|
"APRS network connection Not Enabled in config. This is"
|
||||||
@ -486,13 +492,6 @@ def server(
|
|||||||
|
|
||||||
packets.PacketList(config=config)
|
packets.PacketList(config=config)
|
||||||
|
|
||||||
rx_thread = threads.APRSDRXThread(
|
|
||||||
msg_queues=threads.msg_queues,
|
|
||||||
config=config,
|
|
||||||
)
|
|
||||||
|
|
||||||
rx_thread.start()
|
|
||||||
|
|
||||||
if "watch_list" in config["aprsd"] and config["aprsd"]["watch_list"].get(
|
if "watch_list" in config["aprsd"] and config["aprsd"]["watch_list"].get(
|
||||||
"enabled",
|
"enabled",
|
||||||
True,
|
True,
|
||||||
|
@ -489,6 +489,9 @@ class AckMessage(Message):
|
|||||||
self.id,
|
self.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _filter_for_send(self):
|
||||||
|
return f"ack{self.id}"
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
LOG.debug(f"Send ACK({self.tocall}:{self.id}) to radio.")
|
LOG.debug(f"Send ACK({self.tocall}:{self.id}) to radio.")
|
||||||
thread = SendAckThread(self)
|
thread = SendAckThread(self)
|
||||||
|
138
aprsd/threads.py
138
aprsd/threads.py
@ -8,9 +8,7 @@ import tracemalloc
|
|||||||
|
|
||||||
import aprslib
|
import aprslib
|
||||||
|
|
||||||
from aprsd import (
|
from aprsd import client, kissclient, messaging, packets, plugin, stats, utils
|
||||||
client, kissclient, messaging, packets, plugin, stats, trace, utils,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
@ -182,9 +180,10 @@ class APRSDRXThread(APRSDThread):
|
|||||||
|
|
||||||
class APRSDProcessPacketThread(APRSDThread):
|
class APRSDProcessPacketThread(APRSDThread):
|
||||||
|
|
||||||
def __init__(self, packet, config):
|
def __init__(self, packet, config, transport="aprsis"):
|
||||||
self.packet = packet
|
self.packet = packet
|
||||||
self.config = config
|
self.config = config
|
||||||
|
self.transport = transport
|
||||||
name = self.packet["raw"][:10]
|
name = self.packet["raw"][:10]
|
||||||
super().__init__(f"RX_PACKET-{name}")
|
super().__init__(f"RX_PACKET-{name}")
|
||||||
|
|
||||||
@ -239,6 +238,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
|||||||
self.config["aprs"]["login"],
|
self.config["aprs"]["login"],
|
||||||
fromcall,
|
fromcall,
|
||||||
msg_id=msg_id,
|
msg_id=msg_id,
|
||||||
|
transport=self.transport,
|
||||||
)
|
)
|
||||||
ack.send()
|
ack.send()
|
||||||
|
|
||||||
@ -257,6 +257,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
|||||||
self.config["aprs"]["login"],
|
self.config["aprs"]["login"],
|
||||||
fromcall,
|
fromcall,
|
||||||
subreply,
|
subreply,
|
||||||
|
transport=self.transport,
|
||||||
)
|
)
|
||||||
msg.send()
|
msg.send()
|
||||||
|
|
||||||
@ -273,6 +274,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
|||||||
self.config["aprs"]["login"],
|
self.config["aprs"]["login"],
|
||||||
fromcall,
|
fromcall,
|
||||||
reply,
|
reply,
|
||||||
|
transport=self.transport,
|
||||||
)
|
)
|
||||||
msg.send()
|
msg.send()
|
||||||
|
|
||||||
@ -285,6 +287,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
|||||||
self.config["aprs"]["login"],
|
self.config["aprs"]["login"],
|
||||||
fromcall,
|
fromcall,
|
||||||
reply,
|
reply,
|
||||||
|
transport=self.transport,
|
||||||
)
|
)
|
||||||
msg.send()
|
msg.send()
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -296,6 +299,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
|||||||
self.config["aprs"]["login"],
|
self.config["aprs"]["login"],
|
||||||
fromcall,
|
fromcall,
|
||||||
reply,
|
reply,
|
||||||
|
transport=self.transport,
|
||||||
)
|
)
|
||||||
msg.send()
|
msg.send()
|
||||||
|
|
||||||
@ -349,7 +353,7 @@ class KISSRXThread(APRSDThread):
|
|||||||
# and the aprslib developer didn't want to allow a PR to add
|
# and the aprslib developer didn't want to allow a PR to add
|
||||||
# kwargs. :(
|
# kwargs. :(
|
||||||
# https://github.com/rossengeorgiev/aprs-python/pull/56
|
# https://github.com/rossengeorgiev/aprs-python/pull/56
|
||||||
kiss_client.consumer(self.process_packet, callsign="APN382")
|
kiss_client.consumer(self.process_packet, callsign=self.config["kiss"]["callsign"])
|
||||||
kiss_client.loop.run_forever()
|
kiss_client.loop.run_forever()
|
||||||
|
|
||||||
except aprslib.exceptions.ConnectionDrop:
|
except aprslib.exceptions.ConnectionDrop:
|
||||||
@ -361,131 +365,21 @@ class KISSRXThread(APRSDThread):
|
|||||||
client.Client().reset()
|
client.Client().reset()
|
||||||
# Continue to loop
|
# Continue to loop
|
||||||
|
|
||||||
@trace.trace
|
def process_packet(self, interface, frame):
|
||||||
def process_packet(self, interface, frame, match):
|
|
||||||
"""Process a packet recieved from aprs-is server."""
|
"""Process a packet recieved from aprs-is server."""
|
||||||
|
|
||||||
LOG.debug(f"Got an APRS Frame '{frame}'")
|
LOG.debug(f"Got an APRS Frame '{frame}'")
|
||||||
|
# try and nuke the * from the fromcall sign.
|
||||||
|
frame.header._source._ch = False
|
||||||
payload = str(frame.payload.decode())
|
payload = str(frame.payload.decode())
|
||||||
msg = f"{str(frame.header)}:{payload}"
|
msg = f"{str(frame.header)}:{payload}"
|
||||||
|
LOG.debug(f"Decoding {msg}")
|
||||||
|
|
||||||
packet = aprslib.parse(msg)
|
packet = aprslib.parse(msg)
|
||||||
LOG.debug(packet)
|
LOG.debug(packet)
|
||||||
|
thread = APRSDProcessPacketThread(
|
||||||
try:
|
packet=packet, config=self.config,
|
||||||
stats.APRSDStats().msgs_rx_inc()
|
|
||||||
|
|
||||||
msg = packet.get("message_text", None)
|
|
||||||
msg_format = packet.get("format", None)
|
|
||||||
msg_response = packet.get("response", None)
|
|
||||||
if msg_format == "message" and msg:
|
|
||||||
# we want to send the message through the
|
|
||||||
# plugins
|
|
||||||
self.process_message_packet(packet)
|
|
||||||
return
|
|
||||||
elif msg_response == "ack":
|
|
||||||
self.process_ack_packet(packet)
|
|
||||||
return
|
|
||||||
|
|
||||||
if msg_format == "mic-e":
|
|
||||||
# process a mic-e packet
|
|
||||||
self.process_mic_e_packet(packet)
|
|
||||||
return
|
|
||||||
|
|
||||||
except (aprslib.ParseError, aprslib.UnknownFormat) as exp:
|
|
||||||
LOG.exception("Failed to parse packet from aprs-is", exp)
|
|
||||||
|
|
||||||
@trace.trace
|
|
||||||
def process_message_packet(self, packet):
|
|
||||||
LOG.debug("Message packet rx")
|
|
||||||
fromcall = packet["from"]
|
|
||||||
message = packet.get("message_text", None)
|
|
||||||
msg_id = packet.get("msgNo", "0")
|
|
||||||
messaging.log_message(
|
|
||||||
"Received Message",
|
|
||||||
packet["raw"],
|
|
||||||
message,
|
|
||||||
fromcall=fromcall,
|
|
||||||
msg_num=msg_id,
|
|
||||||
)
|
|
||||||
found_command = False
|
|
||||||
# Get singleton of the PM
|
|
||||||
pm = plugin.PluginManager()
|
|
||||||
try:
|
|
||||||
results = pm.run(fromcall=fromcall, message=message, ack=msg_id)
|
|
||||||
for reply in results:
|
|
||||||
found_command = 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 messaging.NULL_MESSAGE:
|
|
||||||
LOG.debug(f"Sending '{reply}'")
|
|
||||||
|
|
||||||
msg = messaging.TextMessage(
|
|
||||||
self.config["aprs"]["login"],
|
|
||||||
fromcall,
|
|
||||||
reply,
|
|
||||||
transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
|
|
||||||
)
|
|
||||||
self.msg_queues["tx"].put(msg)
|
|
||||||
else:
|
|
||||||
LOG.debug("Got NULL MESSAGE from plugin")
|
|
||||||
|
|
||||||
if not found_command:
|
|
||||||
plugins = pm.get_plugins()
|
|
||||||
names = [x.command_name for x in plugins]
|
|
||||||
names.sort()
|
|
||||||
|
|
||||||
# reply = "Usage: {}".format(", ".join(names))
|
|
||||||
reply = "Usage: weather, locate [call], time, fortune, ping"
|
|
||||||
|
|
||||||
msg = messaging.TextMessage(
|
|
||||||
self.config["aprs"]["login"],
|
|
||||||
fromcall,
|
|
||||||
reply,
|
|
||||||
transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
|
|
||||||
)
|
|
||||||
self.msg_queues["tx"].put(msg)
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.exception("Plugin failed!!!", ex)
|
|
||||||
reply = "A Plugin failed! try again?"
|
|
||||||
msg = messaging.TextMessage(
|
|
||||||
self.config["aprs"]["login"],
|
|
||||||
fromcall,
|
|
||||||
reply,
|
|
||||||
transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
|
|
||||||
)
|
|
||||||
self.msg_queues["tx"].put(msg)
|
|
||||||
|
|
||||||
# let any threads do their thing, then ack
|
|
||||||
# send an ack last
|
|
||||||
ack = messaging.AckMessage(
|
|
||||||
self.config["aprs"]["login"],
|
|
||||||
fromcall,
|
|
||||||
msg_id=msg_id,
|
|
||||||
transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
|
transport=messaging.MESSAGE_TRANSPORT_TCPKISS,
|
||||||
)
|
)
|
||||||
self.msg_queues["tx"].put(ack)
|
thread.start()
|
||||||
LOG.debug("Packet processing complete")
|
|
||||||
|
|
||||||
def process_ack_packet(self, packet):
|
|
||||||
ack_num = packet.get("msgNo")
|
|
||||||
LOG.info(f"Got ack for message {ack_num}")
|
|
||||||
messaging.log_message(
|
|
||||||
"ACK",
|
|
||||||
packet["raw"],
|
|
||||||
None,
|
|
||||||
ack=ack_num,
|
|
||||||
fromcall=packet["from"],
|
|
||||||
)
|
|
||||||
tracker = messaging.MsgTrack()
|
|
||||||
tracker.remove(ack_num)
|
|
||||||
stats.APRSDStats().ack_rx_inc()
|
|
||||||
return
|
|
||||||
|
|
||||||
def process_mic_e_packet(self, packet):
|
|
||||||
LOG.info("Mic-E Packet detected. Currenlty unsupported.")
|
|
||||||
messaging.log_packet(packet)
|
|
||||||
stats.APRSDStats().msgs_mice_inc()
|
|
||||||
return
|
return
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
aioax25
|
aioax25>=0.0.10
|
||||||
aprslib
|
aprslib
|
||||||
click
|
click
|
||||||
click-completion
|
click-completion
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# pip-compile requirements.in
|
# pip-compile requirements.in
|
||||||
#
|
#
|
||||||
aioax25==0.0.9
|
aioax25==0.0.10
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
aprslib==0.6.47
|
aprslib==0.6.47
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
|
Loading…
x
Reference in New Issue
Block a user