From ad79ed1261702ae0f278e91d7369de02b20a0af3 Mon Sep 17 00:00:00 2001 From: Hemna Date: Thu, 28 Jul 2022 16:24:25 -0400 Subject: [PATCH] Use new aprsd.callsign as the main callsign This patch changes how aprsd identifies itself when connected to any client, which is not relying on the login for each client. There are 3 supported clients currently aprsis, tcpkiss serialkiss. Each client has their own potential login/callsign to connect to the remote. This patch tells aprsd to use the new config option aprsd.callsign as a means to identify itself. It will accept packets as and reply as regardless of which client object is being used to connect to the remote. Note: this breaks backwards compatibility. This patch now requires the new config option aprsd: callsign: --- aprsd/client.py | 4 ++-- aprsd/clients/kiss.py | 37 +++++++++++++++++++++++++++++-------- aprsd/cmds/webchat.py | 32 +++++++++++++++++--------------- aprsd/config.py | 35 +++++++++++++++++++++++++++++++---- aprsd/threads/keep_alive.py | 2 +- aprsd/threads/rx.py | 18 +++++++++--------- 6 files changed, 89 insertions(+), 39 deletions(-) diff --git a/aprsd/client.py b/aprsd/client.py index f17e027..71a8ea1 100644 --- a/aprsd/client.py +++ b/aprsd/client.py @@ -156,8 +156,8 @@ class KISSClient(Client): # Ensure that the config vars are correctly set if KISSClient.is_enabled(config): config.check_option( - "kiss.callsign", - default_fail=aprsd_config.DEFAULT_CONFIG_DICT["kiss"]["callsign"], + "aprsd.callsign", + default_fail=aprsd_config.DEFAULT_CONFIG_DICT["aprsd"]["callsign"], ) transport = KISSClient.transport(config) if transport == TRANSPORT_SERIALKISS: diff --git a/aprsd/clients/kiss.py b/aprsd/clients/kiss.py index 7ea1ce6..7facca7 100644 --- a/aprsd/clients/kiss.py +++ b/aprsd/clients/kiss.py @@ -32,6 +32,7 @@ class Aioax25Client: device=self.config["kiss"]["serial"]["device"], baudrate=self.config["kiss"]["serial"].get("baudrate", 9600), loop=self.loop, + log=LOG, ) elif "tcp" in self.config["kiss"] and self.config["kiss"]["tcp"].get( "enabled", @@ -50,30 +51,32 @@ class Aioax25Client: log=LOG, ) - self.kissdev.open() - self.kissport0 = self.kissdev[0] - LOG.debug("Creating AX25Interface") - self.ax25int = interface.AX25Interface(kissport=self.kissport0, loop=self.loop) + self.ax25int = interface.AX25Interface( + kissport=self.kissdev[0], + loop=self.loop, + log=LOG, + ) LOG.debug("Creating APRSInterface") self.aprsint = APRSInterface( ax25int=self.ax25int, - mycall=self.config["kiss"]["callsign"], + mycall=self.config["aprsd"]["callsign"], log=LOG, ) + self.kissdev.open() def stop(self): LOG.debug(self.kissdev) - self.kissdev._close() self.loop.stop() + self.kissdev.close() def set_filter(self, filter): # This does nothing right now. pass - def consumer(self, callback, blocking=True, immortal=False, raw=False): - callsign = self.config["kiss"]["callsign"] + def consumer(self, callback, blocking=False, immortal=False, raw=False): + callsign = self.config["aprsd"]["callsign"] call = callsign.split("-") if len(call) > 1: callsign = call[0] @@ -81,10 +84,28 @@ class Aioax25Client: else: ssid = 0 self.aprsint.bind(callback=callback, callsign=callsign, ssid=ssid, regex=False) + + # async def set_after(fut, delay, value): + # # Sleep for *delay* seconds. + # await asyncio.sleep(delay) + # + # # Set *value* as a result of *fut* Future. + # fut.set_result(value) + # + # async def my_wait(fut): + # await fut + # + # fut = self.loop.create_future() + # self.loop.create_task( + # set_after(fut, 5, "nothing") + # ) + LOG.debug("RUN FOREVER") self.loop.run_forever() + # my_wait(fut) def send(self, msg): """Send an APRS Message object.""" + LOG.debug(f"Send {msg} TO KISS") payload = f"{msg._filter_for_send()}" self.aprsint.send_message( addressee=msg.tocall, diff --git a/aprsd/cmds/webchat.py b/aprsd/cmds/webchat.py index fc518a8..3aa85ad 100644 --- a/aprsd/cmds/webchat.py +++ b/aprsd/cmds/webchat.py @@ -93,19 +93,22 @@ class SentMessages(objectstore.ObjectStoreMixin): @wrapt.synchronized(lock) def set_status(self, id, status): - self.data[id]["last_update"] = str(datetime.datetime.now()) - self.data[id]["status"] = status + if id in self.data: + self.data[id]["last_update"] = str(datetime.datetime.now()) + self.data[id]["status"] = status @wrapt.synchronized(lock) def ack(self, id): """The message got an ack!""" - self.data[id]["last_update"] = str(datetime.datetime.now()) - self.data[id]["ack"] = True + if id in self.data: + self.data[id]["last_update"] = str(datetime.datetime.now()) + self.data[id]["ack"] = True @wrapt.synchronized(lock) def reply(self, id, packet): """We got a packet back from the sent message.""" - self.data[id]["reply"] = packet + if id in self.data: + self.data[id]["reply"] = packet # HTTPBasicAuth doesn't work on a class method. @@ -126,7 +129,6 @@ class WebChatRXThread(rx.APRSDRXThread): self.connected = connected def loop(self): - # setup the consumer of messages and block until a messages msg = None try: @@ -227,15 +229,16 @@ class WebChatTXThread(aprsd_thread.APRSDThread): self.got_ack = True def process_packet(self, packet): + LOG.info(f"process PACKET {packet}") tocall = packet.get("addresse", None) fromcall = packet["from"] msg = packet.get("message_text", None) msg_id = packet.get("msgNo", "0") msg_response = packet.get("response", None) - if tocall == self.config["aprs"]["login"] and msg_response == "ack": + if tocall == self.config["aprsd"]["callsign"] and msg_response == "ack": self.process_ack_packet(packet) - elif tocall == self.config["aprs"]["login"]: + elif tocall == self.config["aprsd"]["callsign"]: messaging.log_message( "Received Message", packet["raw"], @@ -246,11 +249,11 @@ class WebChatTXThread(aprsd_thread.APRSDThread): # let any threads do their thing, then ack # send an ack last ack = messaging.AckMessage( - self.config["aprs"]["login"], + self.config["aprsd"]["callsign"], fromcall, msg_id=msg_id, ) - self.msg_queues["tx"].put(ack) + ack.send() packets.PacketList().add(packet) stats.APRSDStats().msgs_rx_inc() @@ -299,7 +302,7 @@ class WebChatFlask(flask_classful.FlaskView): ) else: # We might be connected to a KISS socket? - if client.KISSClient.kiss_enabled(self.config): + if client.KISSClient.is_enabled(self.config): transport = client.KISSClient.transport(self.config) if transport == client.TRANSPORT_TCPKISS: aprs_connection = ( @@ -324,7 +327,7 @@ class WebChatFlask(flask_classful.FlaskView): "index.html", initial_stats=stats, aprs_connection=aprs_connection, - callsign=self.config["aprs"]["login"], + callsign=self.config["aprsd"]["callsign"], version=aprsd.__version__, ) @@ -335,7 +338,6 @@ class WebChatFlask(flask_classful.FlaskView): info = msgs.get_all() return json.dumps(info) - @trace.trace def _stats(self): stats_obj = stats.APRSDStats() now = datetime.datetime.now() @@ -406,8 +408,8 @@ class SendMessageNamespace(Namespace): "sent", SentMessages().get(self.msg.id), namespace="/sendmsg", ) - - self._msg_queues["tx"].put(msg) + msg.send() + # self._msg_queues["tx"].put(msg) def handle_message(self, data): LOG.debug(f"WS Data {data}") diff --git a/aprsd/config.py b/aprsd/config.py index 93b56ea..c96abf6 100644 --- a/aprsd/config.py +++ b/aprsd/config.py @@ -57,13 +57,13 @@ DEFAULT_CONFIG_DICT = { "ham": {"callsign": "NOCALL"}, "aprs": { "enabled": True, + # Only used as the login for aprsis. "login": "CALLSIGN", "password": "00000", "host": "rotate.aprs2.net", "port": 14580, }, "kiss": { - "callsign": "NOCALL", "tcp": { "enabled": False, "host": "direwolf.ip.address", @@ -76,11 +76,14 @@ DEFAULT_CONFIG_DICT = { }, }, "aprsd": { + # Callsign to use for all packets to/from aprsd instance + # regardless of the client (aprsis vs kiss) + "callsign": "NOCALL", "logfile": "/tmp/aprsd.log", "logformat": DEFAULT_LOG_FORMAT, "dateformat": DEFAULT_DATE_FORMAT, "save_location": DEFAULT_CONFIG_DIR, - "rich_logging": False, + "rich_logging": True, "trace": False, "enabled_plugins": CORE_MESSAGE_PLUGINS, "units": "imperial", @@ -177,16 +180,35 @@ class Config(collections.UserDict): if not self.exists(path): if type(path) is list: path = ".".join(path) - raise exception.MissingConfigOption(path) + raise exception.MissingConfigOptionException(path) val = self.get(path) if val == default_fail: # We have to fail and bail if the user hasn't edited # this config option. - raise exception.ConfigOptionBogusDefaultException(path, default_fail) + raise exception.ConfigOptionBogusDefaultException( + path, default_fail, + ) def add_config_comments(raw_yaml): + end_idx = utils.end_substr(raw_yaml, "ham:") + if end_idx != -1: + # lets insert a comment + raw_yaml = utils.insert_str( + raw_yaml, + "\n # Callsign that owns this instance of APRSD.", + end_idx, + ) + end_idx = utils.end_substr(raw_yaml, "aprsd:") + if end_idx != -1: + # lets insert a comment + raw_yaml = utils.insert_str( + raw_yaml, + "\n # Callsign to use for all APRSD Packets as the to/from." + "\n # regardless of client type (aprsis vs tcpkiss vs serial)", + end_idx, + ) end_idx = utils.end_substr(raw_yaml, "aprs:") if end_idx != -1: # lets insert a comment @@ -326,6 +348,11 @@ def parse_config(config_file): config, ["aprsd"], ) + check_option( + config, + "aprsd.callsign", + default_fail=DEFAULT_CONFIG_DICT["aprsd"]["callsign"], + ) # Ensure they change the admin password if config.get("aprsd.web.enabled") is True: diff --git a/aprsd/threads/keep_alive.py b/aprsd/threads/keep_alive.py index 3b70eeb..95c7ea0 100644 --- a/aprsd/threads/keep_alive.py +++ b/aprsd/threads/keep_alive.py @@ -41,7 +41,7 @@ class KeepAliveThread(APRSDThread): stats_obj.set_memory_peak(peak) try: - login = self.config["aprs"]["login"] + login = self.config["aprsd"]["callsign"] except KeyError: login = self.config["ham"]["callsign"] diff --git a/aprsd/threads/rx.py b/aprsd/threads/rx.py index 5046e20..b28dbff 100644 --- a/aprsd/threads/rx.py +++ b/aprsd/threads/rx.py @@ -101,7 +101,7 @@ class APRSDProcessPacketThread(APRSDThread): # We don't put ack packets destined for us through the # plugins. - if tocall == self.config["aprs"]["login"] and msg_response == "ack": + if tocall == self.config["aprsd"]["callsign"] and msg_response == "ack": self.process_ack_packet(packet) else: # It's not an ACK for us, so lets run it through @@ -115,12 +115,12 @@ class APRSDProcessPacketThread(APRSDThread): ) # Only ack messages that were sent directly to us - if tocall == self.config["aprs"]["login"]: + if tocall == self.config["aprsd"]["callsign"]: stats.APRSDStats().msgs_rx_inc() # let any threads do their thing, then ack # send an ack last ack = messaging.AckMessage( - self.config["aprs"]["login"], + self.config["aprsd"]["callsign"], fromcall, msg_id=msg_id, ) @@ -142,7 +142,7 @@ class APRSDProcessPacketThread(APRSDThread): subreply.send() else: msg = messaging.TextMessage( - self.config["aprs"]["login"], + self.config["aprsd"]["callsign"], fromcall, subreply, ) @@ -162,7 +162,7 @@ class APRSDProcessPacketThread(APRSDThread): LOG.debug(f"Sending '{reply}'") msg = messaging.TextMessage( - self.config["aprs"]["login"], + self.config["aprsd"]["callsign"], fromcall, reply, ) @@ -170,10 +170,10 @@ class APRSDProcessPacketThread(APRSDThread): # If the message was for us and we didn't have a # response, then we send a usage statement. - if tocall == self.config["aprs"]["login"] and not replied: + if tocall == self.config["aprsd"]["callsign"] and not replied: LOG.warning("Sending help!") msg = messaging.TextMessage( - self.config["aprs"]["login"], + self.config["aprsd"]["callsign"], fromcall, "Unknown command! Send 'help' message for help", ) @@ -182,10 +182,10 @@ class APRSDProcessPacketThread(APRSDThread): LOG.error("Plugin failed!!!") LOG.exception(ex) # Do we need to send a reply? - if tocall == self.config["aprs"]["login"]: + if tocall == self.config["aprsd"]["callsign"]: reply = "A Plugin failed! try again?" msg = messaging.TextMessage( - self.config["aprs"]["login"], + self.config["aprsd"]["callsign"], fromcall, reply, )