class RTResults: def __init__(self): self.current_groups = None @asyncio.coroutine def sub_connect(self, host, port): self.sets_subscriber = Subscriber("rt_results", self.init_groups, self.on_mod) yield from self.sets_subscriber.connect(host, port) @asyncio.coroutine def sub_close(self): yield from self.sets_subscriber.close() def init_groups(self, init): if self.current_groups is not None: self.current_groups.delete() self.current_groups = _Groups(init) return self.current_groups def on_mod(self, mod): if mod["action"] != "init" and len(mod["path"]) >= 3: path = mod["path"] group = self.current_groups[path[0]] if path[1] == "data": group.on_data_modified(path[2])
def _do(self): try: subscriber = Subscriber("devices", self.controller_db.sync_struct_init) while True: try: def set_host_filter(): s = subscriber.writer.get_extra_info("socket") localhost = s.getsockname()[0] self.controller_db.set_host_filter(localhost) yield from subscriber.connect(self.server, self.port, set_host_filter) try: yield from asyncio.wait_for(subscriber.receive_task, None) finally: yield from subscriber.close() except (ConnectionAbortedError, ConnectionError, ConnectionRefusedError, ConnectionResetError) as e: logger.warning("Connection to master failed (%s: %s)", e.__class__.__name__, str(e)) else: logger.warning("Connection to master lost") logger.warning("Retrying in %.1f seconds", self.retry_master) yield from asyncio.sleep(self.retry_master) except asyncio.CancelledError: pass finally: yield from self.controller_db.current_controllers.shutdown()
def __init__(self): self.ttl_dock = _MonInjDock("TTL") self.dds_dock = _MonInjDock("DDS") self.subscriber = Subscriber("devices", self.init_devices) self.dm = _DeviceManager(self.send_to_device, dict()) self.transport = None
def subscribe(self): if self.args.embed is None: self.subscriber = Subscriber("datasets", self.sub_init, self.sub_mod) self.loop.run_until_complete( self.subscriber.connect(self.args.server, self.args.port)) else: self.ipc.subscribe(self.datasets, self.sub_init, self.sub_mod)
class SimpleApplet: def __init__(self, main_widget_class, cmd_description=None): self.main_widget_class = main_widget_class self.argparser = argparse.ArgumentParser(description=cmd_description) group = self.argparser.add_argument_group("data server") group.add_argument( "--server", default="::1", help="hostname or IP to connect to") group.add_argument( "--port", default=3250, type=int, help="TCP port to connect to") self._arggroup_datasets = self.argparser.add_argument_group("datasets") def add_dataset(self, name, help=None): if help is None: self._arggroup_datasets.add_argument(name) else: self._arggroup_datasets.add_argument(name, help=help) def args_init(self): self.args = self.argparser.parse_args() def quamash_init(self): app = QtWidgets.QApplication([]) self.loop = QEventLoop(app) asyncio.set_event_loop(self.loop) def create_main_widget(self): self.main_widget = self.main_widget_class(self.args) self.main_widget.show() def sub_init(self, data): self.data = data return data def sub_mod(self, mod): self.main_widget.data_changed(self.data, mod) def create_subscriber(self): self.subscriber = Subscriber("datasets", self.sub_init, self.sub_mod) self.loop.run_until_complete(self.subscriber.connect( self.args.server, self.args.port)) def run(self): self.args_init() self.quamash_init() try: self.create_main_widget() self.create_subscriber() try: self.loop.run_forever() finally: self.loop.run_until_complete(self.subscriber.close()) finally: self.loop.close()
def sub_connect(self, host, port): self.queue_subscriber = Subscriber("queue", self.init_queue_store) yield from self.queue_subscriber.connect(host, port) try: self.timed_subscriber = Subscriber("timed", self.init_timed_store) yield from self.timed_subscriber.connect(host, port) except: yield from self.queue_subscriber.close() raise
def sub_connect(self, host, port): self.parameters_subscriber = Subscriber("parameters", self.init_parameters_store) yield from self.parameters_subscriber.connect(host, port) try: self.lastchanges_subscriber = Subscriber( "parameters_simplehist", self.init_lastchanges_store) yield from self.lastchanges_subscriber.connect(host, port) except: yield from self.parameters_subscriber.close() raise
class ParametersWindow(Window): def __init__(self, **kwargs): Window.__init__(self, title="Parameters", default_size=(500, 500), **kwargs) notebook = Gtk.Notebook() self.add(notebook) self.parameters_store = Gtk.ListStore(str, str) tree = Gtk.TreeView(self.parameters_store) for i, title in enumerate(["Parameter", "Value"]): renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn(title, renderer, text=i) tree.append_column(column) scroll = Gtk.ScrolledWindow() scroll.add(tree) notebook.insert_page(scroll, Gtk.Label("Current values"), -1) self.lastchanges_store = Gtk.ListStore(str, str, str) tree = Gtk.TreeView(self.lastchanges_store) for i, title in enumerate(["Time", "Parameter", "Value"]): renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn(title, renderer, text=i) tree.append_column(column) scroll = Gtk.ScrolledWindow() scroll.add(tree) notebook.insert_page(scroll, Gtk.Label("Last changes"), -1) @asyncio.coroutine def sub_connect(self, host, port): self.parameters_subscriber = Subscriber("parameters", self.init_parameters_store) yield from self.parameters_subscriber.connect(host, port) try: self.lastchanges_subscriber = Subscriber( "parameters_simplehist", self.init_lastchanges_store) yield from self.lastchanges_subscriber.connect(host, port) except: yield from self.parameters_subscriber.close() raise @asyncio.coroutine def sub_close(self): yield from self.lastchanges_subscriber.close() yield from self.parameters_subscriber.close() def init_parameters_store(self, init): return _ParameterStoreSyncer(self.parameters_store, init) def init_lastchanges_store(self, init): return _LastChangesStoreSyncer(self.lastchanges_store, init)
def subscribe(self): # We want to subscribe only to the experiment-local datasets for our RID # (but always, even if using IPC – this can be optimised later). self.subscriber = Subscriber("datasets_rid_{}".format(self.args.rid), self.sub_init, self.sub_mod) self.loop.run_until_complete( self.subscriber.connect(self.args.server, self.args.port)) # Make sure we still respond to non-dataset messages like `terminate` in # embed mode. if self.embed is not None: def ignore(*args): pass self.ipc.subscribe([], ignore, ignore)
class ParametersDock(dockarea.Dock): def __init__(self): dockarea.Dock.__init__(self, "Parameters", size=(400, 300)) grid = LayoutWidget() self.addWidget(grid) self.search = QtGui.QLineEdit() self.search.setPlaceholderText("search...") self.search.editingFinished.connect(self._search_parameters) grid.addWidget(self.search, 0, 0) self.table = QtGui.QTableView() self.table.setSelectionMode(QtGui.QAbstractItemView.NoSelection) self.table.horizontalHeader().setResizeMode( QtGui.QHeaderView.ResizeToContents) grid.addWidget(self.table, 1, 0) def get_parameter(self, key): return self.table_model.backing_store[key] def _search_parameters(self): model = self.table.model() parentIndex = model.index(0, 0) numRows = model.rowCount(parentIndex) for row in range(numRows): index = model.index(row, 0) parameter = model.data(index, QtCore.Qt.DisplayRole) if parameter.startswith(self.search.displayText()): self.table.showRow(row) else: self.table.hideRow(row) @asyncio.coroutine def sub_connect(self, host, port): self.subscriber = Subscriber("parameters", self.init_parameters_model) yield from self.subscriber.connect(host, port) @asyncio.coroutine def sub_close(self): yield from self.subscriber.close() def init_parameters_model(self, init): self.table_model = ParametersModel(self.table, init) self.table.setModel(self.table_model) return self.table_model
def subscribe(self): if self.args.embed is None: self.subscriber = Subscriber("datasets", self.sub_init, self.sub_mod) self.loop.run_until_complete(self.subscriber.connect( self.args.server, self.args.port)) else: self.ipc.subscribe(self.datasets, self.sub_init, self.sub_mod)
class ScheduleDock(dockarea.Dock): def __init__(self, status_bar, schedule_ctl): dockarea.Dock.__init__(self, "Schedule", size=(1000, 300)) self.status_bar = status_bar self.schedule_ctl = schedule_ctl self.table = QtGui.QTableView() self.table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) self.table.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.table.horizontalHeader().setResizeMode( QtGui.QHeaderView.ResizeToContents) self.table.verticalHeader().setResizeMode( QtGui.QHeaderView.ResizeToContents) self.addWidget(self.table) self.table.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) delete_action = QtGui.QAction("Delete", self.table) delete_action.triggered.connect(self.delete_clicked) self.table.addAction(delete_action) @asyncio.coroutine def sub_connect(self, host, port): self.subscriber = Subscriber("schedule", self.init_schedule_model) yield from self.subscriber.connect(host, port) @asyncio.coroutine def sub_close(self): yield from self.subscriber.close() def init_schedule_model(self, init): self.table_model = _ScheduleModel(self.table, init) self.table.setModel(self.table_model) return self.table_model @asyncio.coroutine def delete(self, rid): yield from self.schedule_ctl.delete(rid) def delete_clicked(self): idx = self.table.selectedIndexes() if idx: row = idx[0].row() rid = self.table_model.row_to_key[row] self.status_bar.showMessage("Deleted RID {}".format(rid)) asyncio.async(self.delete(rid))
def _show_list(args, notifier_name, display_fun): l = [] def init_l(x): l[:] = x return l subscriber = Subscriber(notifier_name, init_l, lambda mod: display_fun(l)) _run_subscriber(args.server, args.port, subscriber)
def _do(self): subscriber = Subscriber( "parameters", partial(Parameters, self.filter_function, self.writer)) while True: try: yield from subscriber.connect(self.server, self.port) try: yield from asyncio.wait_for(subscriber.receive_task, None) finally: yield from subscriber.close() except (ConnectionAbortedError, ConnectionError, ConnectionRefusedError, ConnectionResetError) as e: logger.warning("Connection to master failed (%s: %s)", e.__class__.__name__, str(e)) else: logger.warning("Connection to master lost") logger.warning("Retrying in %.1f seconds", self.retry) yield from asyncio.sleep(self.retry)
def _show_dict(args, notifier_name, display_fun): d = dict() def init_d(x): d.clear() d.update(x) return d subscriber = Subscriber(notifier_name, init_d, lambda mod: display_fun(d)) _run_subscriber(args.server, args.port, subscriber)
class LogDock(dockarea.Dock): def __init__(self): dockarea.Dock.__init__(self, "Log", size=(1000, 300)) self.log = QtGui.QTableView() self.log.setSelectionMode(QtGui.QAbstractItemView.NoSelection) self.log.horizontalHeader().setResizeMode( QtGui.QHeaderView.ResizeToContents) self.log.setHorizontalScrollMode( QtGui.QAbstractItemView.ScrollPerPixel) self.log.setShowGrid(False) self.log.setTextElideMode(QtCore.Qt.ElideNone) self.addWidget(self.log) self.scroll_at_bottom = False @asyncio.coroutine def sub_connect(self, host, port): self.subscriber = Subscriber("log", self.init_log_model) yield from self.subscriber.connect(host, port) @asyncio.coroutine def sub_close(self): yield from self.subscriber.close() def rows_inserted_before(self): scrollbar = self.log.verticalScrollBar() self.scroll_at_bottom = scrollbar.value() == scrollbar.maximum() def rows_inserted_after(self): if self.scroll_at_bottom: self.log.scrollToBottom() def init_log_model(self, init): table_model = _LogModel(self.log, init) self.log.setModel(table_model) table_model.rowsAboutToBeInserted.connect(self.rows_inserted_before) table_model.rowsInserted.connect(self.rows_inserted_after) return table_model
class NdscanApplet(SimpleApplet): def __init__(self): # Use a small update delay by default to avoid lagging out the UI by # continuous redraws for plots with a large number of points. (20 ms # is a pretty arbitrary choice for a latency not perceptible by the # user in a normal use case). super().__init__(_MainWidget, default_update_delay=20e-3) self.argparser.add_argument( "--port-control", default=3251, type=int, help="TCP port for master control commands") self.argparser.add_argument("--rid", help="RID of the experiment to plot") def subscribe(self): # We want to subscribe only to the experiment-local datasets for our RID # (but always, even if using IPC – this can be optimised later). self.subscriber = Subscriber("datasets_rid_{}".format(self.args.rid), self.sub_init, self.sub_mod) self.loop.run_until_complete( self.subscriber.connect(self.args.server, self.args.port)) # Make sure we still respond to non-dataset messages like `terminate` in # embed mode. if self.embed is not None: def ignore(*args): pass self.ipc.subscribe([], ignore, ignore) def unsubscribe(self): self.loop.run_until_complete(self.subscriber.close()) def filter_mod(self, *args): return True
def __init__(self): self.ttl_dock = _MonInjDock("TTL") self.dds_dock = _MonInjDock("DDS") self.subscriber = Subscriber("devices", self.init_devices) self.dm = None self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.socket.bind(("", 0)) # Never ceasing to disappoint, asyncio has an issue about UDP # not being supported on Windows (ProactorEventLoop) open since 2014. self.loop = asyncio.get_event_loop() self.thread = threading.Thread(target=self.receiver_thread, daemon=True) self.thread.start()
def __init__(self): self.ttl_dock = _MonInjDock("TTL") self.dds_dock = _MonInjDock("DDS") self.dac_dock = _MonInjDock("DAC") self.dm = _DeviceManager() self.dm.ttl_cb = lambda: self.ttl_dock.layout_widgets( self.dm.ttl_widgets.values()) self.dm.dds_cb = lambda: self.dds_dock.layout_widgets( self.dm.dds_widgets.values()) self.dm.dac_cb = lambda: self.dac_dock.layout_widgets( self.dm.dac_widgets.values()) self.subscriber = Subscriber("devices", self.dm.init_ddb, self.dm.notify)
async def _do(self): subscriber = Subscriber( "datasets", partial(Datasets, self.filter_function, self.writer)) while True: try: await subscriber.connect(self.server, self.port) try: await asyncio.wait_for(subscriber.receive_task, None) finally: await subscriber.close() except (ConnectionAbortedError, ConnectionError, ConnectionRefusedError, ConnectionResetError) as e: logger.warning("Connection to master failed (%s: %s)", e.__class__.__name__, str(e)) else: logger.warning("Connection to master lost") logger.warning("Retrying in %.1f seconds", self.retry) await asyncio.sleep(self.retry)
async def _do(self): subscriber = Subscriber("schedule", target_builder=self.writer.init, notify_cb=self.writer.notify_cb, disconnect_cb=self.writer.disconnect_cb) while True: try: await subscriber.connect(self.server, self.port) try: await asyncio.wait_for(subscriber.receive_task, None) finally: await subscriber.close() except (ConnectionAbortedError, ConnectionError, ConnectionRefusedError, ConnectionResetError) as e: logger.warning("Connection to master failed (%s: %s)", e.__class__.__name__, str(e)) else: logger.warning("Connection to master lost") logger.warning("Retrying in %.1f seconds", self.retry) await asyncio.sleep(self.retry)
class SimpleApplet: def __init__(self, main_widget_class, cmd_description=None, default_update_delay=0.0): self.main_widget_class = main_widget_class self.argparser = argparse.ArgumentParser(description=cmd_description) self.argparser.add_argument( "--update-delay", type=float, default=default_update_delay, help="time to wait after a mod (buffering other mods) " "before updating (default: %(default).2f)") group = self.argparser.add_argument_group("standalone mode (default)") group.add_argument("--server", default="::1", help="hostname or IP of the master to connect to " "for dataset notifications " "(ignored in embedded mode)") group.add_argument("--port", default=3250, type=int, help="TCP port to connect to") self.argparser.add_argument("--embed", default=None, help="embed into GUI", metavar="IPC_ADDRESS") self._arggroup_datasets = self.argparser.add_argument_group("datasets") self.dataset_args = set() def add_dataset(self, name, help=None, required=True): kwargs = dict() if help is not None: kwargs["help"] = help if required: self._arggroup_datasets.add_argument(name, **kwargs) else: self._arggroup_datasets.add_argument("--" + name, **kwargs) self.dataset_args.add(name) def args_init(self): self.args = self.argparser.parse_args() self.datasets = { getattr(self.args, arg.replace("-", "_")) for arg in self.dataset_args } def quamash_init(self): app = QtWidgets.QApplication([]) self.loop = QEventLoop(app) asyncio.set_event_loop(self.loop) def ipc_init(self): if self.args.embed is not None: self.ipc = AppletIPCClient(self.args.embed) self.loop.run_until_complete(self.ipc.connect()) def ipc_close(self): if self.args.embed is not None: self.ipc.close() def create_main_widget(self): self.main_widget = self.main_widget_class(self.args) if self.args.embed is not None: self.ipc.set_close_cb(self.main_widget.close) if os.name == "nt": # HACK: if the window has a frame, there will be garbage # (usually white) displayed at its right and bottom borders # after it is embedded. self.main_widget.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.main_widget.show() win_id = int(self.main_widget.winId()) self.loop.run_until_complete(self.ipc.embed(win_id)) else: # HACK: # Qt window embedding is ridiculously buggy, and empirical # testing has shown that the following procedure must be # followed exactly on Linux: # 1. applet creates widget # 2. applet creates native window without showing it, and # gets its ID # 3. applet sends the ID to host, host embeds the widget # 4. applet shows the widget # 5. parent resizes the widget win_id = int(self.main_widget.winId()) self.loop.run_until_complete(self.ipc.embed(win_id)) self.main_widget.show() self.ipc.fix_initial_size() else: self.main_widget.show() def sub_init(self, data): self.data = data return data def filter_mod(self, mod): if self.args.embed is not None: # the parent already filters for us return True if mod["action"] == "init": return True if mod["path"]: return mod["path"][0] in self.datasets elif mod["action"] in {"setitem", "delitem"}: return mod["key"] in self.datasets else: return False def emit_data_changed(self, data, mod_buffer): self.main_widget.data_changed(data, mod_buffer) def flush_mod_buffer(self): self.emit_data_changed(self.data, self.mod_buffer) del self.mod_buffer def sub_mod(self, mod): if not self.filter_mod(mod): return if self.args.update_delay: if hasattr(self, "mod_buffer"): self.mod_buffer.append(mod) else: self.mod_buffer = [mod] asyncio.get_event_loop().call_later(self.args.update_delay, self.flush_mod_buffer) else: self.emit_data_changed(self.data, [mod]) def subscribe(self): if self.args.embed is None: self.subscriber = Subscriber("datasets", self.sub_init, self.sub_mod) self.loop.run_until_complete( self.subscriber.connect(self.args.server, self.args.port)) else: self.ipc.subscribe(self.datasets, self.sub_init, self.sub_mod) def unsubscribe(self): if self.args.embed is None: self.loop.run_until_complete(self.subscriber.close()) def run(self): self.args_init() self.quamash_init() try: self.ipc_init() try: self.create_main_widget() self.subscribe() try: self.loop.run_forever() finally: self.unsubscribe() finally: self.ipc_close() finally: self.loop.close()
def __init__(self): self.ttl_dock = _MonInjDock("TTL") self.dds_dock = _MonInjDock("DDS") self.subscriber = Subscriber("devices", self.init_devices) self.dm = None
def sub_connect(self, host, port): self.explist_subscriber = Subscriber("explist", self.init_explist_store) yield from self.explist_subscriber.connect(host, port)
class ExplorerWindow(Window): def __init__(self, exit_fn, schedule_ctl, repository, layout_dict=dict()): self.schedule_ctl = schedule_ctl self.repository = repository Window.__init__(self, title="Explorer", default_size=(800, 570), layout_dict=layout_dict) self.connect("delete-event", exit_fn) topvbox = Gtk.VBox(spacing=6) self.add(topvbox) menubar = Gtk.MenuBar() topvbox.pack_start(menubar, False, False, 0) top_menuitem = Gtk.MenuItem("Windows") menu = Gtk.Menu() menuitem = Gtk.MenuItem("Scheduler") menu.append(menuitem) menuitem = Gtk.MenuItem("Parameters") menu.append(menuitem) menu.append(Gtk.SeparatorMenuItem()) menuitem = Gtk.MenuItem("Quit") menuitem.connect("activate", exit_fn) menu.append(menuitem) top_menuitem.set_submenu(menu) menubar.append(top_menuitem) top_menuitem = Gtk.MenuItem("Registry") menu = Gtk.Menu() menuitem = Gtk.MenuItem("Run selected") menuitem.connect("activate", self.run) menu.append(menuitem) menu.append(Gtk.SeparatorMenuItem()) menuitem = Gtk.MenuItem("Add experiment") menu.append(menuitem) menuitem = Gtk.MenuItem("Remove experiment") menu.append(menuitem) top_menuitem.set_submenu(menu) menubar.append(top_menuitem) self.pane = Gtk.HPaned( position=getitem(layout_dict, "pane_position", 180)) topvbox.pack_start(self.pane, True, True, 0) explistvbox = Gtk.VBox(spacing=6) self.pane.pack1(explistvbox) self.explist_store = Gtk.ListStore(str) self.explist_tree = Gtk.TreeView(self.explist_store) renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Registered experiments", renderer, text=0) self.explist_tree.append_column(column) self.explist_tree.connect("row-activated", self.explist_row_activated) self.explist_tree.set_activate_on_single_click(True) scroll = Gtk.ScrolledWindow() scroll.add(self.explist_tree) explistvbox.pack_start(scroll, True, True, 0) button = Gtk.Button("Run") button.connect("clicked", self.run) explistvbox.pack_start(button, False, False, 0) self.pane_contents = Gtk.Label("") self.pane.pack2(self.pane_contents) def get_layout_dict(self): r = Window.get_layout_dict(self) r["pane_position"] = self.pane.get_position() return r @asyncio.coroutine def sub_connect(self, host, port): self.explist_subscriber = Subscriber("explist", self.init_explist_store) yield from self.explist_subscriber.connect(host, port) @asyncio.coroutine def sub_close(self): yield from self.explist_subscriber.close() def set_pane_contents(self, widget): self.pane_contents.destroy() self.pane_contents = widget self.pane.pack2(self.pane_contents) self.pane_contents.show_all() def init_explist_store(self, init): self.explist_syncer = _ExplistStoreSyncer(self.explist_store, init, keep_data=True) return self.explist_syncer def explist_row_activated(self, widget, index, column): self.controls = None name = self.explist_store[index][0] gui_file = self.explist_syncer.data[name]["gui_file"] if gui_file is None: self.set_pane_contents(Gtk.Label("No GUI controls")) else: asyncio.Task(self.load_gui_file(gui_file)) @asyncio.coroutine def load_gui_file(self, gui_file): gui_mod_data = yield from self.repository.get_data(gui_file) gui_mod = dict() exec(gui_mod_data, gui_mod) self.controls = gui_mod["Controls"]() yield from self.controls.build(self.repository.get_data) self.set_pane_contents(self.controls.get_top_widget()) def run(self, widget): store, selected = self.explist_tree.get_selection().get_selected() if selected is not None: name = store[selected][0] data = self.explist_syncer.data[name] if self.controls is None: arguments = {} else: arguments = self.controls.get_arguments() run_params = { "file": data["file"], "unit": data["unit"], "arguments": arguments, "rtr_group": data["file"] } asyncio.Task(self.schedule_ctl.run_queued(run_params, None))
class SchedulerWindow(Window): def __init__(self, schedule_ctl, **kwargs): self.schedule_ctl = schedule_ctl Window.__init__(self, title="Scheduler", default_size=(720, 570), **kwargs) topvbox = Gtk.VBox(spacing=6) self.add(topvbox) hbox = Gtk.HBox(spacing=6) enable = Gtk.Switch(active=True) label = Gtk.Label("Run experiments") hbox.pack_start(label, False, False, 0) hbox.pack_start(enable, False, False, 0) topvbox.pack_start(hbox, False, False, 0) notebook = Gtk.Notebook() topvbox.pack_start(notebook, True, True, 0) self.queue_store = Gtk.ListStore(int, str, str, str, str) self.queue_tree = Gtk.TreeView(self.queue_store) for i, title in enumerate( ["RID", "File", "Unit", "Timeout", "Arguments"]): renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn(title, renderer, text=i) self.queue_tree.append_column(column) scroll = Gtk.ScrolledWindow() scroll.add(self.queue_tree) vbox = Gtk.VBox(spacing=6) vbox.pack_start(scroll, True, True, 0) hbox = Gtk.HBox(spacing=6) button = Gtk.Button("Find") hbox.pack_start(button, True, True, 0) button = Gtk.Button("Move up") hbox.pack_start(button, True, True, 0) button = Gtk.Button("Move down") hbox.pack_start(button, True, True, 0) button = Gtk.Button("Remove") button.connect("clicked", self.remove_queued) hbox.pack_start(button, True, True, 0) vbox.pack_start(hbox, False, False, 0) vbox.set_border_width(6) notebook.insert_page(vbox, Gtk.Label("Queue"), -1) self.timed_store = Gtk.ListStore(str, int, str, str, str, str) self.timed_tree = Gtk.TreeView(self.timed_store) for i, title in enumerate( ["Next run", "TRID", "File", "Unit", "Timeout", "Arguments"]): renderer = Gtk.CellRendererText() column = Gtk.TreeViewColumn(title, renderer, text=i) self.timed_tree.append_column(column) scroll = Gtk.ScrolledWindow() scroll.add(self.timed_tree) vbox = Gtk.VBox(spacing=6) vbox.pack_start(scroll, True, True, 0) button = Gtk.Button("Remove") button.connect("clicked", self.remove_timed) vbox.pack_start(button, False, False, 0) vbox.set_border_width(6) notebook.insert_page(vbox, Gtk.Label("Timed schedule"), -1) def remove_queued(self, widget): store, selected = self.queue_tree.get_selection().get_selected() if selected is not None: rid = store[selected][0] asyncio.Task(self.schedule_ctl.cancel_queued(rid)) def remove_timed(self, widget): store, selected = self.timed_tree.get_selection().get_selected() if selected is not None: trid = store[selected][1] asyncio.Task(self.schedule_ctl.cancel_timed(trid)) @asyncio.coroutine def sub_connect(self, host, port): self.queue_subscriber = Subscriber("queue", self.init_queue_store) yield from self.queue_subscriber.connect(host, port) try: self.timed_subscriber = Subscriber("timed", self.init_timed_store) yield from self.timed_subscriber.connect(host, port) except: yield from self.queue_subscriber.close() raise @asyncio.coroutine def sub_close(self): yield from self.timed_subscriber.close() yield from self.queue_subscriber.close() def init_queue_store(self, init): return _QueueStoreSyncer(self.queue_store, init) def init_timed_store(self, init): return _TimedStoreSyncer(self.timed_store, init)
def create_subscriber(self): self.subscriber = Subscriber("datasets", self.sub_init, self.sub_mod) self.loop.run_until_complete(self.subscriber.connect( self.args.server, self.args.port))
class ResultsDock(dockarea.Dock): def __init__(self, dialog_parent, dock_area): dockarea.Dock.__init__(self, "Results", size=(1500, 500)) self.dialog_parent = dialog_parent self.dock_area = dock_area grid = LayoutWidget() self.addWidget(grid) self.table = QtGui.QTableView() self.table.setSelectionMode(QtGui.QAbstractItemView.NoSelection) self.table.horizontalHeader().setResizeMode( QtGui.QHeaderView.ResizeToContents) grid.addWidget(self.table, 0, 0) add_display_box = QtGui.QGroupBox("Add display") grid.addWidget(add_display_box, 0, 1) display_grid = QtGui.QGridLayout() add_display_box.setLayout(display_grid) for n, name in enumerate(display_types.keys()): btn = QtGui.QPushButton(name) display_grid.addWidget(btn, n, 0) btn.clicked.connect(partial(self.create_dialog, name)) self.displays = dict() def get_result(self, key): return self.table_model.backing_store[key] @asyncio.coroutine def sub_connect(self, host, port): self.subscriber = Subscriber("rt_results", self.init_results_model, self.on_mod) yield from self.subscriber.connect(host, port) @asyncio.coroutine def sub_close(self): yield from self.subscriber.close() def init_results_model(self, init): self.table_model = ResultsModel(self.table, init) self.table.setModel(self.table_model) return self.table_model def on_mod(self, mod): if mod["action"] == "init": for display in self.displays.values(): display.update_data(self.table_model.backing_store) return if mod["action"] == "setitem": source = mod["key"] elif mod["path"]: source = mod["path"][0] else: return for display in self.displays.values(): if source in display.data_sources(): display.update_data(self.table_model.backing_store) def create_dialog(self, ty): dlg_class = display_types[ty][0] dlg = dlg_class(self.dialog_parent, None, dict(), sorted(self.table_model.backing_store.keys()), partial(self.create_display, ty, None)) dlg.open() def create_display(self, ty, prev_name, name, settings): if prev_name is not None and prev_name in self.displays: raise NotImplementedError dsp_class = display_types[ty][1] dsp = dsp_class(name, settings) self.displays[name] = dsp dsp.update_data(self.table_model.backing_store) def on_close(): del self.displays[name] dsp.sigClosed.connect(on_close) self.dock_area.addDock(dsp) self.dock_area.floatDock(dsp) return dsp def save_state(self): r = dict() for name, display in self.displays.items(): r[name] = { "ty": _get_display_type_name(type(display)), "settings": display.settings, "state": display.save_state() } return r def restore_state(self, state): for name, desc in state.items(): try: dsp = self.create_display(desc["ty"], None, name, desc["settings"]) except: logger.warning("Failed to create display '%s'", name, exc_info=True) try: dsp.restore_state(desc["state"]) except: logger.warning("Failed to restore display state of '%s'", name, exc_info=True)
def __init__(self, notifier_name, model_factory, disconnect_cb=None): ModelManager.__init__(self, model_factory) Subscriber.__init__(self, notifier_name, self._create_model, disconnect_cb=disconnect_cb)
class MonInj(TaskObject): def __init__(self): self.ttl_dock = _MonInjDock("TTL") self.dds_dock = _MonInjDock("DDS") self.subscriber = Subscriber("devices", self.init_devices) self.dm = _DeviceManager(self.send_to_device, dict()) self.transport = None @asyncio.coroutine def start(self, server, port): loop = asyncio.get_event_loop() yield from loop.create_datagram_endpoint(lambda: self, family=socket.AF_INET) try: yield from self.subscriber.connect(server, port) try: TaskObject.start(self) except: yield from self.subscriber.close() raise except: self.transport.close() raise @asyncio.coroutine def stop(self): yield from TaskObject.stop(self) yield from self.subscriber.close() if self.transport is not None: self.transport.close() self.transport = None def connection_made(self, transport): self.transport = transport def datagram_received(self, data, addr): try: ttl_levels, ttl_oes, ttl_overrides = \ struct.unpack(">QQQ", data[:8*3]) for channel, w in self.dm.ttl_widgets.items(): w.set_value(ttl_levels & (1 << channel), ttl_oes & (1 << channel), ttl_overrides & (1 << channel)) dds_data = data[8*3:] ndds = len(dds_data)//4 ftws = struct.unpack(">" + "I"*ndds, dds_data) for channel, w in self.dm.dds_widgets.items(): try: ftw = ftws[channel] except KeyError: pass else: w.set_value(ftw) except: logger.warning("failed to process datagram", exc_info=True) def error_received(self, exc): logger.warning("datagram endpoint error") def connection_lost(self, exc): self.transport = None def send_to_device(self, data): ca = self.dm.get_core_addr() if ca is None: logger.warning("could not find core device address") elif self.transport is None: logger.warning("datagram endpoint not available") else: self.transport.sendto(data, (ca, 3250)) @asyncio.coroutine def _do(self): while True: yield from asyncio.sleep(0.2) # MONINJ_REQ_MONITOR self.send_to_device(b"\x01") def init_devices(self, d): self.dm = _DeviceManager(self.send_to_device, d) self.dm.ttl_cb = lambda: self.ttl_dock.layout_widgets( self.dm.ttl_widgets.items()) self.dm.dds_cb = lambda: self.dds_dock.layout_widgets( self.dm.dds_widgets.items()) self.dm.ttl_cb() self.dm.dds_cb() return self.dm
def sub_connect(self, host, port): self.explist_subscriber = Subscriber("explist", self.init_explist_model) yield from self.explist_subscriber.connect(host, port)
class ExplorerDock(dockarea.Dock): def __init__(self, dialog_parent, status_bar, schedule_ctl): dockarea.Dock.__init__(self, "Explorer", size=(1500, 500)) self.dialog_parent = dialog_parent self.status_bar = status_bar self.schedule_ctl = schedule_ctl self.splitter = QtGui.QSplitter(QtCore.Qt.Horizontal) self.addWidget(self.splitter) grid = LayoutWidget() self.splitter.addWidget(grid) self.el = QtGui.QListView() self.el.selectionChanged = self._selection_changed self.selected_key = None grid.addWidget(self.el, 0, 0, colspan=4) self.datetime = QtGui.QDateTimeEdit() self.datetime.setDisplayFormat("MMM d yyyy hh:mm:ss") self.datetime.setCalendarPopup(True) self.datetime.setDate(QtCore.QDate.currentDate()) self.datetime.dateTimeChanged.connect(self.enable_duedate) self.datetime_en = QtGui.QCheckBox("Due date:") grid.addWidget(self.datetime_en, 1, 0) grid.addWidget(self.datetime, 1, 1) self.priority = QtGui.QSpinBox() self.priority.setRange(-99, 99) grid.addWidget(QtGui.QLabel("Priority:"), 1, 2) grid.addWidget(self.priority, 1, 3) self.pipeline = QtGui.QLineEdit() self.pipeline.setText("main") grid.addWidget(QtGui.QLabel("Pipeline:"), 2, 0) grid.addWidget(self.pipeline, 2, 1) self.flush = QtGui.QCheckBox("Flush") grid.addWidget(self.flush, 2, 2, colspan=2) submit = QtGui.QPushButton("Submit") grid.addWidget(submit, 3, 0, colspan=4) submit.clicked.connect(self.submit_clicked) self.argeditor = _ArgumentEditor(self.dialog_parent) self.splitter.addWidget(self.argeditor) self.splitter.setSizes([grid.minimumSizeHint().width(), 1000]) self.state = dict() def update_selection(self, selected, deselected): if deselected: self.state[deselected] = self.argeditor.save_state() if selected: expinfo = self.explist_model.backing_store[selected] self.argeditor.set_arguments(expinfo["arguments"]) if selected in self.state: self.argeditor.restore_state(self.state[selected]) self.splitter.insertWidget(1, self.argeditor) self.selected_key = selected def _sel_to_key(self, selection): selection = selection.indexes() if selection: row = selection[0].row() return self.explist_model.row_to_key[row] else: return None def _selection_changed(self, selected, deselected): self.update_selection(self._sel_to_key(selected), self._sel_to_key(deselected)) def save_state(self): idx = self.el.selectedIndexes() if idx: row = idx[0].row() key = self.explist_model.row_to_key[row] self.state[key] = self.argeditor.save_state() return self.state def restore_state(self, state): self.state = state def enable_duedate(self): self.datetime_en.setChecked(True) @asyncio.coroutine def sub_connect(self, host, port): self.explist_subscriber = Subscriber("explist", self.init_explist_model) yield from self.explist_subscriber.connect(host, port) @asyncio.coroutine def sub_close(self): yield from self.explist_subscriber.close() def init_explist_model(self, init): self.explist_model = _ExplistModel(self, self.el, init) self.el.setModel(self.explist_model) return self.explist_model @asyncio.coroutine def submit(self, pipeline_name, file, class_name, arguments, priority, due_date, flush): expid = { "repo_rev": None, "file": file, "class_name": class_name, "arguments": arguments, } rid = yield from self.schedule_ctl.submit(pipeline_name, expid, priority, due_date, flush) self.status_bar.showMessage("Submitted RID {}".format(rid)) def submit_clicked(self): if self.selected_key is not None: expinfo = self.explist_model.backing_store[self.selected_key] if self.datetime_en.isChecked(): due_date = self.datetime.dateTime().toMSecsSinceEpoch()/1000 else: due_date = None arguments = self.argeditor.get_argument_values(True) if arguments is None: return asyncio.async(self.submit(self.pipeline.text(), expinfo["file"], expinfo["class_name"], arguments, self.priority.value(), due_date, self.flush.isChecked()))
def __init__(self, notifier_name, model_factory): ModelManager.__init__(self, model_factory) Subscriber.__init__(self, notifier_name, self._create_model)
def sub_connect(self, host, port): self.subscriber = Subscriber("rt_results", self.init_results_model, self.on_mod) yield from self.subscriber.connect(host, port)
def sub_connect(self, host, port): self.subscriber = Subscriber("log", self.init_log_model) yield from self.subscriber.connect(host, port)
def sub_connect(self, host, port): self.subscriber = Subscriber("schedule", self.init_schedule_model) yield from self.subscriber.connect(host, port)
def __init__(self, notifier_name, model_factory): Subscriber.__init__(self, notifier_name, self._create_model) self.model = None self._model_factory = model_factory self._setmodel_callbacks = []
async def sub_connect(self, host, port): self.subscriber = Subscriber("datasets", self.init_datasets_model, self.on_mod) await self.subscriber.connect(host, port)
def sub_connect(self, host, port): self.sets_subscriber = Subscriber("rt_results", self.init_groups, self.on_mod) yield from self.sets_subscriber.connect(host, port)
def _show_log(args): subscriber = Subscriber("log", _LogPrinter) _run_subscriber(args.server, args.port, subscriber)
def sub_connect(self, host, port): self.subscriber = Subscriber("parameters", self.init_parameters_model) yield from self.subscriber.connect(host, port)
class SimpleApplet: def __init__(self, main_widget_class, cmd_description=None, default_update_delay=0.0): self.main_widget_class = main_widget_class self.argparser = argparse.ArgumentParser(description=cmd_description) self.argparser.add_argument( "--update-delay", type=float, default=default_update_delay, help="time to wait after a mod (buffering other mods) " "before updating (default: %(default).2f)") group = self.argparser.add_argument_group("standalone mode (default)") group.add_argument( "--server", default="::1", help="hostname or IP of the master to connect to " "for dataset notifications " "(ignored in embedded mode)") group.add_argument( "--port", default=3250, type=int, help="TCP port to connect to") self.argparser.add_argument( "--embed", default=None, help="embed into GUI", metavar="IPC_ADDRESS") self._arggroup_datasets = self.argparser.add_argument_group("datasets") self.dataset_args = set() def add_dataset(self, name, help=None, required=True): kwargs = dict() if help is not None: kwargs["help"] = help if required: self._arggroup_datasets.add_argument(name, **kwargs) else: self._arggroup_datasets.add_argument("--" + name, **kwargs) self.dataset_args.add(name) def args_init(self): self.args = self.argparser.parse_args() self.datasets = {getattr(self.args, arg.replace("-", "_")) for arg in self.dataset_args} def quamash_init(self): app = QtWidgets.QApplication([]) self.loop = QEventLoop(app) asyncio.set_event_loop(self.loop) def ipc_init(self): if self.args.embed is not None: self.ipc = AppletIPCClient(self.args.embed) self.loop.run_until_complete(self.ipc.connect()) def ipc_close(self): if self.args.embed is not None: self.ipc.close() def create_main_widget(self): self.main_widget = self.main_widget_class(self.args) if self.args.embed is not None: self.ipc.set_close_cb(self.main_widget.close) if os.name == "nt": # HACK: if the window has a frame, there will be garbage # (usually white) displayed at its right and bottom borders # after it is embedded. self.main_widget.setWindowFlags(QtCore.Qt.FramelessWindowHint) self.main_widget.show() win_id = int(self.main_widget.winId()) self.loop.run_until_complete(self.ipc.embed(win_id)) else: # HACK: # Qt window embedding is ridiculously buggy, and empirical # testing has shown that the following procedure must be # followed exactly on Linux: # 1. applet creates widget # 2. applet creates native window without showing it, and # gets its ID # 3. applet sends the ID to host, host embeds the widget # 4. applet shows the widget # 5. parent resizes the widget win_id = int(self.main_widget.winId()) self.loop.run_until_complete(self.ipc.embed(win_id)) self.main_widget.show() self.ipc.fix_initial_size() else: self.main_widget.show() def sub_init(self, data): self.data = data return data def filter_mod(self, mod): if self.args.embed is not None: # the parent already filters for us return True if mod["action"] == "init": return True if mod["path"]: return mod["path"][0] in self.datasets elif mod["action"] in {"setitem", "delitem"}: return mod["key"] in self.datasets else: return False def emit_data_changed(self, data, mod_buffer): self.main_widget.data_changed(data, mod_buffer) def flush_mod_buffer(self): self.emit_data_changed(self.data, self.mod_buffer) del self.mod_buffer def sub_mod(self, mod): if not self.filter_mod(mod): return if self.args.update_delay: if hasattr(self, "mod_buffer"): self.mod_buffer.append(mod) else: self.mod_buffer = [mod] asyncio.get_event_loop().call_later(self.args.update_delay, self.flush_mod_buffer) else: self.emit_data_changed(self.data, [mod]) def subscribe(self): if self.args.embed is None: self.subscriber = Subscriber("datasets", self.sub_init, self.sub_mod) self.loop.run_until_complete(self.subscriber.connect( self.args.server, self.args.port)) else: self.ipc.subscribe(self.datasets, self.sub_init, self.sub_mod) def unsubscribe(self): if self.args.embed is None: self.loop.run_until_complete(self.subscriber.close()) def run(self): self.args_init() self.quamash_init() try: self.ipc_init() try: self.create_main_widget() self.subscribe() try: self.loop.run_forever() finally: self.unsubscribe() finally: self.ipc_close() finally: self.loop.close()