diff --git a/artiq/gui/parameters.py b/artiq/gui/parameters.py index a57350146..16a086a0b 100644 --- a/artiq/gui/parameters.py +++ b/artiq/gui/parameters.py @@ -1,9 +1,10 @@ import asyncio from operator import itemgetter +import time from gi.repository import Gtk -from artiq.gui.tools import Window +from artiq.gui.tools import Window, ListSyncer from artiq.management.sync_struct import Subscriber @@ -41,11 +42,25 @@ class _ParameterStoreSyncer: del self.parameters_store[self._find_index(key)] +class _LastChangesStoreSyncer(ListSyncer): + def convert(self, x): + if len(x) == 3: + timestamp, name, value = x + else: + timestamp, name = x + value = "" + return [time.strftime("%m/%d %H:%M:%S", time.localtime(timestamp)), + name, str(value)] + + class ParametersWindow(Window): def __init__(self): Window.__init__(self, title="Parameters") self.set_default_size(500, 500) + 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"]): @@ -54,17 +69,38 @@ class ParametersWindow(Window): tree.append_column(column) scroll = Gtk.ScrolledWindow() scroll.add(tree) - self.add(scroll) + 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) diff --git a/artiq/gui/scheduler.py b/artiq/gui/scheduler.py index a3c7b041e..559dcaaa2 100644 --- a/artiq/gui/scheduler.py +++ b/artiq/gui/scheduler.py @@ -3,33 +3,18 @@ import asyncio from gi.repository import Gtk -from artiq.gui.tools import Window +from artiq.gui.tools import Window, ListSyncer from artiq.management.sync_struct import Subscriber -class _QueueStoreSyncer: - def __init__(self, queue_store, init): - self.queue_store = queue_store - self.queue_store.clear() - for x in init: - self.append(x) - - def _convert(self, x): +class _QueueStoreSyncer(ListSyncer): + def convert(self, x): rid, run_params, timeout = x row = [rid, run_params["file"]] for e in run_params["unit"], run_params["function"], timeout: row.append("-" if e is None else str(e)) return row - def append(self, x): - self.queue_store.append(self._convert(x)) - - def insert(self, i, x): - self.queue_store.insert(i, self._convert(x)) - - def __delitem__(self, key): - del self.queue_store[key] - class _PeriodicStoreSyncer: def __init__(self, periodic_store, init): diff --git a/artiq/gui/tools.py b/artiq/gui/tools.py index 8e39f726e..05cfe7c3e 100644 --- a/artiq/gui/tools.py +++ b/artiq/gui/tools.py @@ -5,9 +5,27 @@ from gi.repository import Gtk data_dir = os.path.abspath(os.path.dirname(__file__)) + class Window(Gtk.Window): def __init__(self, *args, **kwargs): Gtk.Window.__init__(self, *args, **kwargs) self.set_wmclass("ARTIQ", "ARTIQ") self.set_icon_from_file(os.path.join(data_dir, "icon.png")) self.set_border_width(6) + + +class ListSyncer: + def __init__(self, store, init): + self.store = store + self.store.clear() + for x in init: + self.append(x) + + def append(self, x): + self.store.append(self.convert(x)) + + def insert(self, i, x): + self.store.insert(i, self.convert(x)) + + def __delitem__(self, key): + del self.store[key] diff --git a/artiq/management/dpdb.py b/artiq/management/dpdb.py index 8afdfe196..8d670b1c7 100644 --- a/artiq/management/dpdb.py +++ b/artiq/management/dpdb.py @@ -1,5 +1,6 @@ from collections import OrderedDict import importlib +from time import time from artiq.language.context import * from artiq.management import pyon @@ -68,6 +69,7 @@ class DeviceParamDB: self.pdb_file = pdb_file self.ddb = Notifier(pyon.load_file(self.ddb_file)) self.pdb = Notifier(pyon.load_file(self.pdb_file)) + self.parameter_hooks = [] def save_ddb(self): pyon.store_file(self.ddb_file, self.ddb.backing_struct) @@ -92,7 +94,29 @@ class DeviceParamDB: def set_parameter(self, name, value): self.pdb[name] = value self.save_pdb() + timestamp = time() + for hook in self.parameter_hooks: + hook.set_parameter(timestamp, name, value) def del_parameter(self, name): del self.pdb[name] self.save_pdb() + timestamp = time() + for hook in self.parameter_hooks: + hook.del_parameter(timestamp, name) + + +class SimpleParameterHistory: + def __init__(self, depth): + self.depth = depth + self.history = Notifier([]) + + def set_parameter(self, timestamp, name, value): + if len(self.history.backing_struct) >= self.depth: + del self.history[0] + self.history.append((timestamp, name, value)) + + def del_parameter(self, timestamp, name): + if len(self.history.backing_struct) >= self.depth: + del self.history[0] + self.history.append((timestamp, name)) diff --git a/frontend/artiq_master.py b/frontend/artiq_master.py index 8b676a999..e9abe3d20 100755 --- a/frontend/artiq_master.py +++ b/frontend/artiq_master.py @@ -6,7 +6,7 @@ import atexit from artiq.management.pc_rpc import Server from artiq.management.sync_struct import Publisher -from artiq.management.dpdb import DeviceParamDB +from artiq.management.dpdb import DeviceParamDB, SimpleParameterHistory from artiq.management.scheduler import Scheduler @@ -26,7 +26,10 @@ def _get_args(): def main(): args = _get_args() + dpdb = DeviceParamDB("ddb.pyon", "pdb.pyon") + simplephist = SimpleParameterHistory(30) + dpdb.parameter_hooks.append(simplephist) loop = asyncio.get_event_loop() atexit.register(lambda: loop.close()) @@ -50,7 +53,8 @@ def main(): "queue": scheduler.queue, "periodic": scheduler.periodic, "devices": dpdb.ddb, - "parameters": dpdb.pdb + "parameters": dpdb.pdb, + "parameters_simplehist": simplephist.history }) loop.run_until_complete(server_notify.start( args.bind, args.port_notify))