def send_message_thread(tocall, message, this_message_number, retry_count): cl = client.get_client() line = "{}>APRS::{}:{}{{{}\n".format( CONFIG["aprs"]["login"], tocall, message, str(this_message_number), ) for i in range(retry_count, 0, -1): LOG.debug("DEBUG: send_message_thread msg:ack combos are: ") LOG.debug(pprint.pformat(ack_dict)) if ack_dict[this_message_number] != 1: log_message( "Sending Message", line.rstrip("\n"), message, tocall=tocall, retry_number=i, ) # tn.write(line) cl.sendall(line) # decaying repeats, 31 to 93 second intervals sleeptime = (retry_count - i + 1) * 31 time.sleep(sleeptime) else: break return
def loop(self): """Loop until a message is acked or it gets delayed. We only sleep for 5 seconds between each loop run, so that CTRL-C can exit the app in a short period. Each sleep means the app quitting is blocked until sleep is done. So we keep track of the last send attempt and only send if the last send attempt is old enough. """ cl = client.get_client() tracker = MsgTrack() # lets see if the message is still in the tracking queue msg = tracker.get(self.msg.id) if not msg: # The message has been removed from the tracking queue # So it got acked and we are done. LOG.info("Message Send Complete via Ack.") return False else: send_now = False if msg.last_send_attempt == msg.retry_count: # we reached the send limit, don't send again # TODO(hemna) - Need to put this in a delayed queue? LOG.info("Message Send Complete. Max attempts reached.") return False # Message is still outstanding and needs to be acked. if msg.last_send_time: # Message has a last send time tracking now = datetime.datetime.now() sleeptime = (msg.last_send_attempt + 1) * 31 delta = now - msg.last_send_time if delta > datetime.timedelta(seconds=sleeptime): # It's time to try to send it again send_now = True else: send_now = True if send_now: # no attempt time, so lets send it, and start # tracking the time. log_message( "Sending Message", str(msg).rstrip("\n"), msg.message, tocall=self.msg.tocall, retry_number=msg.last_send_attempt, msg_num=msg.id, ) cl.sendall(str(msg)) stats.APRSDStats().msgs_tx_inc() msg.last_send_time = datetime.datetime.now() msg.last_send_attempt += 1 time.sleep(5) # Make sure we get called again. return True
def send_direct(self): """Send an ack message without a separate thread.""" cl = client.get_client() log_message( "Sending ack", str(self).rstrip("\n"), None, ack=self.id, tocall=self.tocall, fromcall=self.fromcall, ) cl.sendall(str(self))
def send_direct(self): """Send a message without a separate thread.""" cl = client.get_client() log_message( "Sending Message Direct", str(self).rstrip("\n"), self.message, tocall=self.tocall, fromcall=self.fromcall, ) cl.sendall(str(self)) stats.APRSDStats().msgs_tx_inc()
def server(loglevel, quiet, disable_validation, config_file): """Start the aprsd server process.""" signal.signal(signal.SIGINT, signal_handler) click.echo("Load config") config = utils.parse_config(config_file) # Force setting the config to the modules that need it # TODO(Walt): convert these modules to classes that can # Accept the config as a constructor param, instead of this # hacky global setting email.CONFIG = config messaging.CONFIG = config setup_logging(config, loglevel, quiet) LOG.info("APRSD Started version: {}".format(aprsd.__version__)) # TODO(walt): Make email processing/checking optional? # Maybe someone only wants this to process messages with plugins only. valid = email.validate_email_config(config, disable_validation) if not valid: LOG.error("Failed to validate email config options") sys.exit(-1) # start the email thread email.start_thread() # Create the initial PM singleton and Register plugins plugin_manager = plugin.PluginManager(config) plugin_manager.setup_plugins() cl = client.Client(config) # setup and run the main blocking loop while True: # Now use the helper which uses the singleton aprs_client = client.get_client() # 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 aprs_client.consumer(process_packet, raw=False) except aprslib.exceptions.ConnectionDrop: 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 cl.reset()
def send_ack_direct(tocall, ack): """Send an ack message without a separate thread.""" LOG.debug("Send ACK({}:{}) to radio.".format(tocall, ack)) cl = client.get_client() fromcall = CONFIG["aprs"]["login"] line = "{}>APRS::{}:ack{}\n".format(fromcall, tocall, ack) log_message( "Sending ack", line.rstrip("\n"), None, ack=ack, tocall=tocall, fromcall=fromcall, ) cl.sendall(line)
def send_thread(self): """Separate thread to send acks with retries.""" cl = client.get_client() for i in range(self.retry_count, 0, -1): log_message( "Sending ack", str(self).rstrip("\n"), None, ack=self.id, tocall=self.tocall, retry_number=i, ) cl.sendall(str(self)) stats.APRSDStats().ack_tx_inc() # aprs duplicate detection is 30 secs? # (21 only sends first, 28 skips middle) time.sleep(31)
def send_ack_thread(tocall, ack, retry_count): cl = client.get_client() tocall = tocall.ljust(9) # pad to nine chars line = "{}>APRS::{}:ack{}\n".format(CONFIG["aprs"]["login"], tocall, ack) for i in range(retry_count, 0, -1): log_message( "Sending ack", line.rstrip("\n"), None, ack=ack, tocall=tocall, retry_number=i, ) cl.sendall(line) # aprs duplicate detection is 30 secs? # (21 only sends first, 28 skips middle) time.sleep(31)
def send_message_direct(tocall, message, message_number=None): """Send a message without a separate thread.""" cl = client.get_client() if not message_number: this_message_number = 1 else: this_message_number = message_number fromcall = CONFIG["aprs"]["login"] line = "{}>APRS::{}:{}{{{}\n".format( fromcall, tocall, message, str(this_message_number), ) LOG.debug("DEBUG: send_message_thread msg:ack combos are: ") log_message( "Sending Message", line.rstrip("\n"), message, tocall=tocall, fromcall=fromcall ) cl.sendall(line)
def loop(self): """Separate thread to send acks with retries.""" send_now = False if self.ack.last_send_attempt == self.ack.retry_count: # we reached the send limit, don't send again # TODO(hemna) - Need to put this in a delayed queue? LOG.info("Ack Send Complete. Max attempts reached.") return False if self.ack.last_send_time: # Message has a last send time tracking now = datetime.datetime.now() # aprs duplicate detection is 30 secs? # (21 only sends first, 28 skips middle) sleeptime = 31 delta = now - self.ack.last_send_time if delta > datetime.timedelta(seconds=sleeptime): # It's time to try to send it again send_now = True else: LOG.debug("Still wating. {}".format(delta)) else: send_now = True if send_now: cl = client.get_client() log_message( "Sending ack", str(self.ack).rstrip("\n"), None, ack=self.ack.id, tocall=self.ack.tocall, retry_number=self.ack.last_send_attempt, ) cl.sendall(str(self.ack)) stats.APRSDStats().ack_tx_inc() self.ack.last_send_attempt += 1 self.ack.last_send_time = datetime.datetime.now() time.sleep(5)
def send_message(loglevel, quiet, config_file, aprs_login, aprs_password, tocallsign, command): """Send a message to a callsign via APRS_IS.""" global got_ack, got_response click.echo("{} {} {} {}".format(aprs_login, aprs_password, tocallsign, command)) click.echo("Load config") config = utils.parse_config(config_file) if not aprs_login: click.echo("Must set --aprs_login or APRS_LOGIN") return if not aprs_password: click.echo("Must set --aprs-password or APRS_PASSWORD") return config["aprs"]["login"] = aprs_login config["aprs"]["password"] = aprs_password messaging.CONFIG = config setup_logging(config, loglevel, quiet) LOG.info("APRSD Started version: {}".format(aprsd.__version__)) message_number = random.randint(1, 90) if type(command) is tuple: command = " ".join(command) LOG.info("Sending Command '{}'".format(command)) got_ack = False got_response = False def rx_packet(packet): global got_ack, got_response # LOG.debug("Got packet back {}".format(packet)) resp = packet.get("response", None) if resp == "ack": ack_num = packet.get("msgNo") LOG.info("We got ack for our sent message {}".format(ack_num)) messaging.log_packet(packet) got_ack = True else: message = packet.get("message_text", None) LOG.info("We got a new message") fromcall = packet["from"] msg_number = packet.get("msgNo", None) if msg_number: ack = msg_number else: ack = "0" messaging.log_message("Received Message", packet["raw"], message, fromcall=fromcall, ack=ack) got_response = True # Send the ack back? messaging.send_ack_direct(fromcall, ack) if got_ack and got_response: sys.exit(0) cl = client.Client(config) # Send a message # then we setup a consumer to rx messages # We should get an ack back as well as a new message # we should bail after we get the ack and send an ack back for the # message messaging.send_message_direct(tocallsign, command, message_number) try: # This will register a packet consumer with aprslib # When new packets come in the consumer will process # the packet aprs_client = client.get_client() aprs_client.consumer(rx_packet, raw=False) except aprslib.exceptions.ConnectionDrop: 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 cl.reset()