def index(self): stats = self._stats() LOG.debug( "watch list? {}".format(self.config["aprsd"]["watch_list"], ), ) wl = packets.WatchList() if wl.is_enabled(): watch_count = len(wl) watch_age = wl.max_delta() else: watch_count = 0 watch_age = 0 sl = packets.SeenList() seen_count = len(sl) pm = plugin.PluginManager() plugins = pm.get_plugins() plugin_count = len(plugins) if self.config["aprs"].get("enabled", True): transport = "aprs-is" aprs_connection = ( "APRS-IS Server: <a href='http://status.aprs2.net' >" "{}</a>".format(stats["stats"]["aprs-is"]["server"])) else: # We might be connected to a KISS socket? if client.KISSClient.kiss_enabled(self.config): transport = client.KISSClient.transport(self.config) if transport == client.TRANSPORT_TCPKISS: aprs_connection = ("TCPKISS://{}:{}".format( self.config["kiss"]["tcp"]["host"], self.config["kiss"]["tcp"]["port"], )) elif transport == client.TRANSPORT_SERIALKISS: aprs_connection = ("SerialKISS://{}@{} baud".format( self.config["kiss"]["serial"]["device"], self.config["kiss"]["serial"]["baudrate"], )) stats["transport"] = transport stats["aprs_connection"] = aprs_connection return flask.render_template( "index.html", initial_stats=stats, aprs_connection=aprs_connection, callsign=self.config["aprs"]["login"], version=aprsd.__version__, config_json=json.dumps(self.config.data), watch_count=watch_count, watch_age=watch_age, seen_count=seen_count, plugin_count=plugin_count, )
def config_and_init(self, config=None): if not config: self.config = aprsd_config.Config(aprsd_config.DEFAULT_CONFIG_DICT) self.config["ham"]["callsign"] = self.fromcall self.config["aprs"]["login"] = fake.FAKE_TO_CALLSIGN self.config["services"]["aprs.fi"]["apiKey"] = "something" else: self.config = config # Inintialize the stats object with the config stats.APRSDStats(self.config) packets.WatchList(config=self.config) packets.SeenList(config=self.config) messaging.MsgTrack(config=self.config)
def signal_handler(sig, frame): global flask_enabled click.echo("signal_handler: called") threads.APRSDThreadList().stop_all() if "subprocess" not in str(frame): LOG.info( "Ctrl+C, Sending all threads exit! Can take up to 10 seconds {}". format(datetime.datetime.now(), ), ) time.sleep(1.5) messaging.MsgTrack().save() packets.WatchList().save() packets.SeenList().save() LOG.info(stats.APRSDStats()) # signal.signal(signal.SIGTERM, sys.exit(0)) # sys.exit(0) if flask_enabled: signal.signal(signal.SIGTERM, sys.exit(0))
def send_message( ctx, aprs_login, aprs_password, no_ack, wait_response, raw, tocallsign, command, ): """Send a message to a callsign via APRS_IS.""" global got_ack, got_response config = ctx.obj["config"] quiet = ctx.obj["quiet"] if not aprs_login: if not config.exists("aprs.login"): click.echo("Must set --aprs_login or APRS_LOGIN") ctx.exit(-1) return else: config["aprs"]["login"] = aprs_login if not aprs_password: if not config.exists("aprs.password"): click.echo("Must set --aprs-password or APRS_PASSWORD") ctx.exit(-1) return else: config["aprs"]["password"] = aprs_password LOG.info(f"APRSD LISTEN Started version: {aprsd.__version__}") if type(command) is tuple: command = " ".join(command) if not quiet: if raw: LOG.info(f"L'{aprs_login}' R'{raw}'") else: LOG.info(f"L'{aprs_login}' To'{tocallsign}' C'{command}'") packets.PacketList(config=config) packets.WatchList(config=config) packets.SeenList(config=config) 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(f"We got ack for our sent message {ack_num}") messaging.log_packet(packet) got_ack = True else: message = packet.get("message_text", None) fromcall = packet["from"] msg_number = packet.get("msgNo", "0") messaging.log_message( "Received Message", packet["raw"], message, fromcall=fromcall, ack=msg_number, ) got_response = True # Send the ack back? ack = messaging.AckMessage( config["aprs"]["login"], fromcall, msg_id=msg_number, ) ack.send_direct() if got_ack: if wait_response: if got_response: sys.exit(0) else: sys.exit(0) try: client.ClientFactory.setup(config) client.factory.create().client except LoginError: sys.exit(-1) # 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 if raw: msg = messaging.RawMessage(raw) msg.send_direct() sys.exit(0) else: msg = messaging.TextMessage(aprs_login, tocallsign, command) msg.send_direct() if no_ack: sys.exit(0) try: # This will register a packet consumer with aprslib # When new packets come in the consumer will process # the packet aprs_client = client.factory.create().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 aprs_client.reset()
def stats(self): now = datetime.datetime.now() if self._email_thread_last_time: last_update = str(now - self._email_thread_last_time) else: last_update = "never" if self._aprsis_keepalive: last_aprsis_keepalive = str(now - self._aprsis_keepalive) else: last_aprsis_keepalive = "never" pm = plugin.PluginManager() plugins = pm.get_plugins() plugin_stats = {} def full_name_with_qualname(obj): return "{}.{}".format( obj.__class__.__module__, obj.__class__.__qualname__, ) for p in plugins: plugin_stats[full_name_with_qualname(p)] = { "enabled": p.enabled, "rx": p.rx_count, "tx": p.tx_count, "version": p.version, } wl = packets.WatchList() sl = packets.SeenList() stats = { "aprsd": { "version": aprsd.__version__, "uptime": utils.strfdelta(self.uptime), "memory_current": self.memory, "memory_current_str": utils.human_size(self.memory), "memory_peak": self.memory_peak, "memory_peak_str": utils.human_size(self.memory_peak), "watch_list": wl.get_all(), "seen_list": sl.get_all(), }, "aprs-is": { "server": self.aprsis_server, "callsign": self.config["aprs"]["login"], "last_update": last_aprsis_keepalive, }, "messages": { "tracked": self.msgs_tracked, "sent": self.msgs_tx, "recieved": self.msgs_rx, "ack_sent": self.ack_tx, "ack_recieved": self.ack_rx, "mic-e recieved": self.msgs_mice_rx, }, "email": { "enabled": self.config["aprsd"]["email"]["enabled"], "sent": self._email_tx, "recieved": self._email_rx, "thread_last_update": last_update, }, "plugins": plugin_stats, } return stats
def server(ctx, flush): """Start the aprsd server gateway process.""" ctx.obj["config_file"] loglevel = ctx.obj["loglevel"] quiet = ctx.obj["quiet"] config = ctx.obj["config"] signal.signal(signal.SIGINT, aprsd_main.signal_handler) signal.signal(signal.SIGTERM, aprsd_main.signal_handler) if not quiet: click.echo("Load config") level, msg = utils._check_version() if level: LOG.warning(msg) else: LOG.info(msg) LOG.info(f"APRSD Started version: {aprsd.__version__}") flat_config = utils.flatten_dict(config) LOG.info("Using CONFIG values:") for x in flat_config: if "password" in x or "aprsd.web.users.admin" in x: LOG.info(f"{x} = XXXXXXXXXXXXXXXXXXX") else: LOG.info(f"{x} = {flat_config[x]}") if config["aprsd"].get("trace", False): trace.setup_tracing(["method", "api"]) stats.APRSDStats(config) # Initialize the client factory and create # The correct client object ready for use client.ClientFactory.setup(config) # Make sure we have 1 client transport enabled if not client.factory.is_client_enabled(): LOG.error("No Clients are enabled in config.") sys.exit(-1) if not client.factory.is_client_configured(): LOG.error("APRS client is not properly configured in config file.") sys.exit(-1) # Creates the client object LOG.info("Creating client connection") client.factory.create().client # Now load the msgTrack from disk if any packets.PacketList(config=config) if flush: LOG.debug("Deleting saved MsgTrack.") messaging.MsgTrack(config=config).flush() packets.WatchList(config=config) packets.SeenList(config=config) else: # Try and load saved MsgTrack list LOG.debug("Loading saved MsgTrack object.") messaging.MsgTrack(config=config).load() packets.WatchList(config=config).load() packets.SeenList(config=config).load() # Create the initial PM singleton and Register plugins LOG.info("Loading Plugin Manager and registering plugins") plugin_manager = plugin.PluginManager(config) plugin_manager.setup_plugins() rx_thread = threads.APRSDRXThread( msg_queues=threads.msg_queues, config=config, ) rx_thread.start() messaging.MsgTrack().restart() keepalive = threads.KeepAliveThread(config=config) keepalive.start() web_enabled = config.get("aprsd.web.enabled", default=False) if web_enabled: aprsd_main.flask_enabled = True (socketio, app) = flask.init_flask(config, loglevel, quiet) socketio.run( app, host=config["aprsd"]["web"]["host"], port=config["aprsd"]["web"]["port"], ) # If there are items in the msgTracker, then save them LOG.info("APRSD Exiting.") return 0
def listen( ctx, aprs_login, aprs_password, filter, ): """Listen to packets on the APRS-IS Network based on FILTER. FILTER is the APRS Filter to use.\n see http://www.aprs-is.net/javAPRSFilter.aspx\n r/lat/lon/dist - Range Filter Pass posits and objects within dist km from lat/lon.\n p/aa/bb/cc... - Prefix Filter Pass traffic with fromCall that start with aa or bb or cc.\n b/call1/call2... - Budlist Filter Pass all traffic from exact call: call1, call2, ... (* wild card allowed) \n o/obj1/obj2... - Object Filter Pass all objects with the exact name of obj1, obj2, ... (* wild card allowed)\n """ config = ctx.obj["config"] if not aprs_login: click.echo(ctx.get_help()) click.echo("") ctx.fail("Must set --aprs_login or APRS_LOGIN") ctx.exit() if not aprs_password: click.echo(ctx.get_help()) click.echo("") ctx.fail("Must set --aprs-password or APRS_PASSWORD") ctx.exit() config["aprs"]["login"] = aprs_login config["aprs"]["password"] = aprs_password LOG.info(f"APRSD Listen Started version: {aprsd.__version__}") flat_config = utils.flatten_dict(config) LOG.info("Using CONFIG values:") for x in flat_config: if "password" in x or "aprsd.web.users.admin" in x: LOG.info(f"{x} = XXXXXXXXXXXXXXXXXXX") else: LOG.info(f"{x} = {flat_config[x]}") stats.APRSDStats(config) # Try and load saved MsgTrack list LOG.debug("Loading saved MsgTrack object.") messaging.MsgTrack(config=config).load() packets.WatchList(config=config).load() packets.SeenList(config=config).load() @trace.trace def rx_packet(packet): resp = packet.get("response", None) if resp == "ack": ack_num = packet.get("msgNo") console.log(f"We saw an ACK {ack_num} Ignoring") messaging.log_packet(packet) else: message = packet.get("message_text", None) fromcall = packet["from"] msg_number = packet.get("msgNo", "0") messaging.log_message( "Received Message", packet["raw"], message, fromcall=fromcall, ack=msg_number, console=console, ) # Initialize the client factory and create # The correct client object ready for use client.ClientFactory.setup(config) # Make sure we have 1 client transport enabled if not client.factory.is_client_enabled(): LOG.error("No Clients are enabled in config.") sys.exit(-1) # Creates the client object LOG.info("Creating client connection") client.factory.create().client aprs_client = client.factory.create().client LOG.debug(f"Filter by '{filter}'") aprs_client.set_filter(filter) while True: try: # This will register a packet consumer with aprslib # When new packets come in the consumer will process # the packet with console.status("Listening for packets"): 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 aprs_client.reset() except aprslib.exceptions.UnknownFormat: LOG.error("Got a Bad packet")
def test_plugin( ctx, aprs_login, plugin_path, load_all, number, message, ): """Test an individual APRSD plugin given a python path.""" config = ctx.obj["config"] if not aprs_login: if not config.exists("aprs.login"): click.echo("Must set --aprs_login or APRS_LOGIN") ctx.exit(-1) return else: fromcall = config.get("aprs.login") else: fromcall = aprs_login if not plugin_path: click.echo(ctx.get_help()) click.echo("") ctx.fail("Failed to provide -p option to test a plugin") return if type(message) is tuple: message = " ".join(message) if config["aprsd"].get("trace", False): trace.setup_tracing(["method", "api"]) client.Client(config) stats.APRSDStats(config) messaging.MsgTrack(config=config) packets.WatchList(config=config) packets.SeenList(config=config) pm = plugin.PluginManager(config) if load_all: pm.setup_plugins() else: pm._init() obj = pm._create_class(plugin_path, plugin.APRSDPluginBase, config=config) if not obj: click.echo(ctx.get_help()) click.echo("") ctx.fail(f"Failed to create object from plugin path '{plugin_path}'") ctx.exit() # Register the plugin they wanted tested. LOG.info( "Testing plugin {} Version {}".format( obj.__class__, obj.version, ), ) pm._pluggy_pm.register(obj) login = config["aprs"]["login"] packet = { "from": fromcall, "addresse": login, "message_text": message, "format": "message", "msgNo": 1, } LOG.info(f"P'{plugin_path}' F'{fromcall}' C'{message}'") for x in range(number): reply = pm.run(packet) # Plugin might have threads, so lets stop them so we can exit. # obj.stop_threads() LOG.info(f"Result{x} = '{reply}'") pm.stop()