master, dashboard: support applet requests from experiments

This commit is contained in:
Sebastien Bourdeauducq 2016-09-05 00:53:44 +08:00
parent 549e09e06b
commit e45c089428
6 changed files with 130 additions and 11 deletions

View File

@ -0,0 +1,89 @@
import logging
from PyQt5 import QtCore, QtWidgets
from artiq.gui import applets
logger = logging.getLogger(__name__)
class AppletsCCBDock(applets.AppletsDock):
def __init__(self, *args, **kwargs):
applets.AppletsDock.__init__(self, *args, **kwargs)
sep = QtWidgets.QAction(self.table)
sep.setSeparator(True)
self.table.addAction(sep)
self.listen_action = QtWidgets.QAction(
"Listen to client control broadcasts", self.table)
self.listen_action.setCheckable(True)
self.table.addAction(self.listen_action)
def locate_applet(self, name, group, create_groups):
if group is None:
group = []
elif isinstance(group, str):
group = [group]
parent = self.table.invisibleRootItem()
for g in group:
new_parent = None
for i in range(parent.childCount()):
child = parent.child(i)
if child.ty == "group" and child.text(1) == g:
new_parent = child
break
if new_parent is None:
if create_groups:
new_parent = self.new_group(g, parent)
else:
return None, None
parent = new_parent
applet = None
for i in range(parent.childCount()):
child = parent.child(i)
if child.ty == "applet" and child.text(1) == name:
applet = child
break
return parent, applet
def ccb_create_applet(self, name, command_or_code, group=None, is_code=False):
if not self.listen_action.isChecked():
return
parent, applet = self.locate_applet(name, group, True)
if applet is None:
applet = self.new(name=name, command=command_or_code, parent=parent)
else:
applet.setText(2, command_or_code)
applet.setCheckState(0, QtCore.Qt.Checked)
def ccb_disable_applet(self, name, group=None):
if not self.listen_action.isChecked():
return
parent, applet = self.locate_applet(name, group, False)
if applet is not None:
applet.setCheckState(0, QtCore.Qt.Unchecked)
def ccb_notify(self, message):
try:
service = message["service"]
args = message["args"]
kwargs = message["kwargs"]
if service == "create_applet":
self.ccb_create_applet(*args, **kwargs)
elif service == "disable_applet":
self.ccb_disable_applet(*args, **kwargs)
except:
logger.error("failed to process CCB", exc_info=True)
def save_state(self):
return {
"applets": applets.AppletsDock.save_state(self),
"listen": self.listen_action.isChecked()
}
def restore_state(self, state):
applets.AppletsDock.restore_state(self, state["applets"])
self.listen_action.setChecked(state["listen"])

View File

@ -29,6 +29,7 @@ class FloppingF(EnvExperiment):
0.1, min=0, max=100, step=0.01))
self.setattr_device("scheduler")
self.setattr_device("ccb")
def run(self):
l = len(self.frequency_scan)
@ -41,6 +42,11 @@ class FloppingF(EnvExperiment):
self.set_dataset("flopping_f_fit", np.full(l, np.nan),
broadcast=True, save=False)
self.ccb.issue("create_applet", "flopping_f",
"${artiq_applet}plot_xy "
"flopping_f_brightness --x flopping_f_frequency "
"--fit flopping_f_fit")
for i, f in enumerate(self.frequency_scan):
m_brightness = model(f, self.F0) + self.noise_amplitude*random.random()
self.mutate_dataset("flopping_f_frequency", i, f)

View File

@ -15,9 +15,9 @@ from artiq.tools import (atexit_register_coroutine, verbosity_args,
from artiq.protocols.pc_rpc import AsyncioClient
from artiq.protocols.broadcast import Receiver
from artiq.gui.models import ModelSubscriber
from artiq.gui import state, applets, log
from artiq.gui import state, log
from artiq.dashboard import (experiments, shortcuts, explorer,
moninj, datasets, schedule)
moninj, datasets, schedule, applets_ccb)
def get_argparser():
@ -119,10 +119,13 @@ def main():
atexit_register_coroutine(subscriber.close)
sub_clients[notifier_name] = subscriber
log_receiver = Receiver("log", [])
loop.run_until_complete(log_receiver.connect(
args.server, args.port_broadcast))
atexit_register_coroutine(log_receiver.close)
broadcast_clients = dict()
for target in "log", "ccb":
client = Receiver(target, [])
loop.run_until_complete(client.connect(
args.server, args.port_broadcast))
atexit_register_coroutine(client.close)
broadcast_clients[target] = client
# initialize main window
main_window = MainWindow(args.server)
@ -152,9 +155,10 @@ def main():
rpc_clients["dataset_db"])
smgr.register(d_datasets)
d_applets = applets.AppletsDock(main_window, sub_clients["datasets"])
d_applets = applets_ccb.AppletsCCBDock(main_window, sub_clients["datasets"])
atexit_register_coroutine(d_applets.stop)
smgr.register(d_applets)
broadcast_clients["ccb"].notify_cbs.append(d_applets.ccb_notify)
d_ttl_dds = moninj.MonInj()
loop.run_until_complete(d_ttl_dds.start(args.server, args.port_notify))
@ -166,7 +170,7 @@ def main():
logmgr = log.LogDockManager(main_window)
smgr.register(logmgr)
log_receiver.notify_cbs.append(logmgr.append_message)
broadcast_clients["log"].notify_cbs.append(logmgr.append_message)
widget_log_handler.callback = logmgr.append_message
# lay out docks

View File

@ -69,6 +69,13 @@ def main():
log_forwarder.callback = (lambda msg:
server_broadcast.broadcast("log", msg))
def ccb_issue(service, *args, **kwargs):
msg = {
"service": service,
"args": args,
"kwargs": kwargs
}
server_broadcast.broadcast("ccb", msg)
device_db = DeviceDB(args.device_db)
dataset_db = DatasetDB(args.dataset_db)
@ -96,7 +103,8 @@ def main():
"scheduler_delete": scheduler.delete,
"scheduler_request_termination": scheduler.request_termination,
"scheduler_get_status": scheduler.get_status,
"scheduler_check_pause": scheduler.check_pause
"scheduler_check_pause": scheduler.check_pause,
"ccb_issue": ccb_issue,
})
experiment_db.scan_repository_async()

View File

@ -118,6 +118,12 @@ class DummyScheduler:
pass
class DummyCCB:
def issue(self, service, *args, **kwargs):
logger.info("CCB for service '%s' (args %s, kwargs %s)",
service, args, kwargs)
def get_argparser(with_file=True):
parser = argparse.ArgumentParser(
description="Local experiment running tool")
@ -183,7 +189,8 @@ def run(with_file=False):
init_logger(args)
device_mgr = DeviceManager(DeviceDB(args.device_db),
virtual_devices={"scheduler": DummyScheduler()})
virtual_devices={"scheduler": DummyScheduler(),
"ccb": DummyCCB()})
dataset_db = DatasetDB(args.dataset_db)
dataset_mgr = DatasetManager(dataset_db)

View File

@ -106,6 +106,10 @@ class Scheduler:
return self._check_pause(rid)
class CCB:
issue = staticmethod(make_parent_action("ccb_issue"))
def get_exp(file, class_name):
module = file_import(file, prefix="artiq_worker_")
if class_name is None:
@ -189,7 +193,8 @@ def main():
repository_path = None
device_mgr = DeviceManager(ParentDeviceDB,
virtual_devices={"scheduler": Scheduler()})
virtual_devices={"scheduler": Scheduler(),
"ccb": CCB()})
dataset_mgr = DatasetManager(ParentDatasetDB)
import_cache.install_hook()