diff --git a/artiq/examples/kasli/device_db.py b/artiq/examples/kasli/device_db.py index a0263ee24..34da8282f 100644 --- a/artiq/examples/kasli/device_db.py +++ b/artiq/examples/kasli/device_db.py @@ -22,6 +22,13 @@ device_db = { "port": 1384, "command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr }, + "core_analyzer": { + "type": "controller", + "host": "::1", + "port_proxy": 1385, + "port": 1386, + "command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr + }, "core_cache": { "type": "local", "module": "artiq.coredevice.cache", diff --git a/artiq/examples/kasli_drtioswitching/device_db.py b/artiq/examples/kasli_drtioswitching/device_db.py index f9486393d..1605e1988 100644 --- a/artiq/examples/kasli_drtioswitching/device_db.py +++ b/artiq/examples/kasli_drtioswitching/device_db.py @@ -20,6 +20,13 @@ device_db = { "port": 1384, "command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr }, + "core_analyzer": { + "type": "controller", + "host": "::1", + "port_proxy": 1385, + "port": 1386, + "command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr + }, "core_cache": { "type": "local", "module": "artiq.coredevice.cache", diff --git a/artiq/examples/kasli_suservo/device_db.py b/artiq/examples/kasli_suservo/device_db.py index c52b82a94..2b3bb8a08 100644 --- a/artiq/examples/kasli_suservo/device_db.py +++ b/artiq/examples/kasli_suservo/device_db.py @@ -20,6 +20,13 @@ device_db = { "port": 1384, "command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr }, + "core_analyzer": { + "type": "controller", + "host": "::1", + "port_proxy": 1385, + "port": 1386, + "command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr + }, "core_cache": { "type": "local", "module": "artiq.coredevice.cache", diff --git a/artiq/examples/kc705_nist_clock/device_db.py b/artiq/examples/kc705_nist_clock/device_db.py index 1930f584b..804d8a163 100644 --- a/artiq/examples/kc705_nist_clock/device_db.py +++ b/artiq/examples/kc705_nist_clock/device_db.py @@ -24,6 +24,13 @@ device_db = { "port": 1384, "command": "aqctl_moninj_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr }, + "core_analyzer": { + "type": "controller", + "host": "::1", + "port_proxy": 1385, + "port": 1386, + "command": "aqctl_coreanalyzer_proxy --port-proxy {port_proxy} --port-control {port} --bind {bind} " + core_addr + }, "core_cache": { "type": "local", "module": "artiq.coredevice.cache", diff --git a/artiq/frontend/aqctl_coreanalyzer_proxy.py b/artiq/frontend/aqctl_coreanalyzer_proxy.py new file mode 100644 index 000000000..fa92bca1d --- /dev/null +++ b/artiq/frontend/aqctl_coreanalyzer_proxy.py @@ -0,0 +1,113 @@ +import argparse +import asyncio +import atexit +import logging +import struct +from sipyco.asyncio_tools import AsyncioServer, SignalHandler, atexit_register_coroutine +from sipyco.pc_rpc import Server +from sipyco import common_args + +from artiq.coredevice.comm_analyzer import get_analyzer_dump + +logger = logging.getLogger(__name__) + + +# simplified version of sipyco Broadcaster +class ProxyServer(AsyncioServer): + def __init__(self, queue_limit=1024): + AsyncioServer.__init__(self) + self._recipients = set() + self._queue_limit = queue_limit + + async def _handle_connection_cr(self, reader, writer): + try: + queue = asyncio.Queue(self._queue_limit) + self._recipients.add(queue) + try: + while True: + dump = await queue.get() + writer.write(dump) + # raise exception on connection error + await writer.drain() + finally: + self._recipients.remove(queue) + except (ConnectionResetError, ConnectionAbortedError, BrokenPipeError): + # receivers disconnecting are a normal occurence + pass + finally: + writer.close() + + def request_dump_cb(self, dump): + encoded_dump = struct.pack(">L", len(dump)) + dump + for recipient in self._recipients: + recipient.put_nowait(encoded_dump) + + +class ProxyControl: + def __init__(self, request_dump_cb, core_addr, core_port=1382): + self.request_dump_cb = request_dump_cb + self.core_addr = core_addr + self.core_port = core_port + + def ping(self): + return True + + def request_dump(self): + try: + dump = get_analyzer_dump(self.core_addr, self.core_port) + self.request_dump_cb(dump) + except: + logger.warning("Failed to get analyzer dump:", exc_info=1) + return False + else: + return True + + +def get_argparser(): + parser = argparse.ArgumentParser( + description="ARTIQ core analyzer proxy") + common_args.verbosity_args(parser) + common_args.simple_network_args(parser, [ + ("proxy", "proxying", 1385), + ("control", "control", 1386) + ]) + parser.add_argument("core_addr", metavar="CORE_ADDR", + help="hostname or IP address of the core device") + return parser + + +def main(): + args = get_argparser().parse_args() + common_args.init_logger_from_args(args) + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + atexit.register(loop.close) + + signal_handler = SignalHandler() + signal_handler.setup() + atexit.register(signal_handler.teardown) + + bind_address = common_args.bind_address_from_args(args) + + proxy_server = ProxyServer() + loop.run_until_complete(proxy_server.start(bind_address, args.port_proxy)) + atexit_register_coroutine(proxy_server.stop, loop=loop) + + controller = ProxyControl(proxy_server.request_dump_cb, args.core_addr) + server = Server({"coreanalyzer_proxy_control": controller}, None, True) + loop.run_until_complete(server.start(bind_address, args.port_control)) + atexit_register_coroutine(server.stop, loop=loop) + + logger.info("ARTIQ core analyzer proxy is ready.") + + _, pending = loop.run_until_complete(asyncio.wait( + [loop.create_task(signal_handler.wait_terminate()), + loop.create_task(server.wait_terminate())], + return_when=asyncio.FIRST_COMPLETED)) + for task in pending: + task.cancel() + + +if __name__ == "__main__": + main() diff --git a/artiq/frontend/artiq_ddb_template.py b/artiq/frontend/artiq_ddb_template.py index eeca02c8e..c795c9073 100755 --- a/artiq/frontend/artiq_ddb_template.py +++ b/artiq/frontend/artiq_ddb_template.py @@ -70,6 +70,13 @@ def process_header(output, description): "port": 1384, "command": "aqctl_moninj_proxy --port-proxy {{port_proxy}} --port-control {{port}} --bind {{bind}} " + core_addr }}, + "core_analyzer": {{ + "type": "controller", + "host": "::1", + "port_proxy": 1385, + "port": 1386, + "command": "aqctl_coreanalyzer_proxy --port-proxy {{port_proxy}} --port-control {{port}} --bind {{bind}} " + core_addr + }}, "core_cache": {{ "type": "local", "module": "artiq.coredevice.cache", diff --git a/doc/manual/default_network_ports.rst b/doc/manual/default_network_ports.rst index 6ed9b4fbf..ddd8f0712 100644 --- a/doc/manual/default_network_ports.rst +++ b/doc/manual/default_network_ports.rst @@ -14,6 +14,10 @@ Default network ports +---------------------------------+--------------+ | Moninj (proxy control) | 1384 | +---------------------------------+--------------+ +| Core analyzer proxy (proxy) | 1385 | ++---------------------------------+--------------+ +| Core analyzer proxy (control) | 1386 | ++---------------------------------+--------------+ | Master (logging input) | 1066 | +---------------------------------+--------------+ | Master (broadcasts) | 1067 | diff --git a/doc/manual/utilities.rst b/doc/manual/utilities.rst index d7642113d..187ad39e4 100644 --- a/doc/manual/utilities.rst +++ b/doc/manual/utilities.rst @@ -139,6 +139,15 @@ Core device RTIO analyzer tool .. _routing-table-tool: +Core device RTIO analyzer proxy +------------------------------- + +:mod:`~artiq.frontend.aqctl_coreanalyzer_proxy` is a tool to distribute the core analyzer dump to several clients such as the dashboard. + +.. argparse:: + :ref: artiq.frontend.aqctl_coreanalyzer_proxy.get_argparser + :prog: aqctl_coreanalyzer_proxy + DRTIO routing table manipulation tool ------------------------------------- diff --git a/setup.py b/setup.py index a38815b22..f7436f582 100755 --- a/setup.py +++ b/setup.py @@ -33,6 +33,7 @@ console_scripts = [ "artiq_route = artiq.frontend.artiq_route:main", "artiq_run = artiq.frontend.artiq_run:main", "artiq_flash = artiq.frontend.artiq_flash:main", + "aqctl_coreanalyzer_proxy = artiq.frontend.aqctl_coreanalyzer_proxy:main", "aqctl_corelog = artiq.frontend.aqctl_corelog:main", "aqctl_moninj_proxy = artiq.frontend.aqctl_moninj_proxy:main", "afws_client = artiq.frontend.afws_client:main",