Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 342ab639fce6
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 3efb84155715
Choose a head ref
  • 4 commits
  • 15 files changed
  • 1 contributor

Commits on Apr 4, 2016

  1. Copy the full SHA
    f860548 View commit details
  2. Copy the full SHA
    aa61c29 View commit details
  3. GUI -> dashboard

    sbourdeauducq committed Apr 4, 2016
    Copy the full SHA
    7453d85 View commit details
  4. Copy the full SHA
    3efb841 View commit details
7 changes: 6 additions & 1 deletion artiq/frontend/artiq_client.py
Original file line number Diff line number Diff line change
@@ -96,6 +96,8 @@ def get_argparser():

parser_scan_repos = subparsers.add_parser(
"scan-repository", help="trigger a repository (re)scan")
parser_scan_repos.add_argument("--async", action="store_true",
help="trigger scan and return immediately")
parser_scan_repos.add_argument("revision", default=None, nargs="?",
help="use a specific repository revision "
"(defaults to head)")
@@ -159,7 +161,10 @@ def _action_scan_devices(remote, args):


def _action_scan_repository(remote, args):
remote.scan_repository(args.revision)
if args.async:
remote.scan_repository_async(args.revision)
else:
remote.scan_repository(args.revision)


def _action_ls(remote, args):
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@


def get_argparser():
parser = argparse.ArgumentParser(description="ARTIQ GUI client")
parser = argparse.ArgumentParser(description="ARTIQ Dashboard")
parser.add_argument(
"-s", "--server", default="::1",
help="hostname or IP of the master to connect to")
@@ -28,7 +28,7 @@ def get_argparser():
"--port-control", default=3251, type=int,
help="TCP port to connect to for control")
parser.add_argument(
"--db-file", default="artiq_gui.pyon",
"--db-file", default="artiq_dashboard.pyon",
help="database file for local GUI settings")
verbosity_args(parser)
return parser
@@ -40,7 +40,7 @@ def __init__(self, server):

icon = QtGui.QIcon(os.path.join(artiq_dir, "gui", "logo.svg"))
self.setWindowIcon(icon)
self.setWindowTitle("ARTIQ - {}".format(server))
self.setWindowTitle("ARTIQ Dashboard - {}".format(server))

qfm = QtGui.QFontMetrics(self.font())
self.resize(140*qfm.averageCharWidth(), 38*qfm.lineSpacing())
8 changes: 5 additions & 3 deletions artiq/frontend/artiq_rpctool.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
import readline # This makes input() nicer
import pprint

from artiq.protocols.pc_rpc import AutoTarget, Client, RemoteError
from artiq.protocols.pc_rpc import AutoTarget, Client


def get_argparser():
@@ -101,9 +101,11 @@ def __getitem__(self, k):
try:
ret = eval(cmd, {}, RemoteDict())
except Exception as e:
if isinstance(e, RemoteError):
if hasattr(e, "parent_traceback"):
print("Remote exception:")
print(str(e))
print(traceback.format_exception_only(type(e), e)[0].rstrip())
for l in e.parent_traceback:
print(l.rstrip())
else:
traceback.print_exc()
else:
13 changes: 6 additions & 7 deletions artiq/master/worker.py
Original file line number Diff line number Diff line change
@@ -3,11 +3,11 @@
import asyncio
import logging
import subprocess
import traceback
import time

from artiq.protocols import pipe_ipc, pyon
from artiq.protocols.logging import LogParser
from artiq.protocols.packed_exceptions import current_exc_packed
from artiq.tools import asyncio_wait_or_cancel


@@ -216,12 +216,11 @@ async def _handle_worker_requests(self):
try:
data = func(*obj["args"], **obj["kwargs"])
reply = {"status": "ok", "data": data}
except Exception as e:
reply = {"status": "failed",
"exception": traceback.format_exception_only(
type(e), e)[0][:-1],
"message": str(e),
"traceback": traceback.format_tb(e.__traceback__)}
except:
reply = {
"status": "failed",
"exception": current_exc_packed()
}
await self.io_lock.acquire()
try:
await self._send(reply)
18 changes: 5 additions & 13 deletions artiq/master/worker_impl.py
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@

import artiq
from artiq.protocols import pipe_ipc, pyon
from artiq.protocols.packed_exceptions import raise_packed_exc
from artiq.tools import multiline_log_config, file_import
from artiq.master.worker_db import DeviceManager, DatasetManager, get_hdf5_output
from artiq.language.environment import is_experiment
@@ -27,11 +28,7 @@ def put_object(obj):
ipc.write((ds + "\n").encode())


class ParentActionError(Exception):
pass


def make_parent_action(action, exception=None):
def make_parent_action(action):
def parent_action(*args, **kwargs):
request = {"action": action, "args": args, "kwargs": kwargs}
put_object(request)
@@ -44,22 +41,17 @@ def parent_action(*args, **kwargs):
if reply["status"] == "ok":
return reply["data"]
else:
if exception is None:
exn = ParentActionError(reply["exception"])
else:
exn = exception(reply["message"])
exn.parent_traceback = reply["traceback"]
raise exn
raise_packed_exc(reply["exception"])
return parent_action


class ParentDeviceDB:
get_device_db = make_parent_action("get_device_db")
get = make_parent_action("get_device", KeyError)
get = make_parent_action("get_device")


class ParentDatasetDB:
get = make_parent_action("get_dataset", KeyError)
get = make_parent_action("get_dataset")
update = make_parent_action("update_dataset")


42 changes: 42 additions & 0 deletions artiq/protocols/packed_exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import inspect
import builtins
import traceback
import sys


__all__ = ["GenericRemoteException", "current_exc_packed", "raise_packed_exc"]


class GenericRemoteException(Exception):
pass


builtin_exceptions = {v: k for k, v in builtins.__dict__.items()
if inspect.isclass(v) and issubclass(v, BaseException)}


def current_exc_packed():
exc_class, exc, exc_tb = sys.exc_info()
if exc_class in builtin_exceptions:
return {
"class": builtin_exceptions[exc_class],
"message": str(exc),
"traceback": traceback.format_tb(exc_tb)
}
else:
message = traceback.format_exception_only(exc_class, exc)[0].rstrip()
return {
"class": "GenericRemoteException",
"message": message,
"traceback": traceback.format_tb(exc_tb)
}


def raise_packed_exc(pack):
if pack["class"] == "GenericRemoteException":
cls = GenericRemoteException
else:
cls = getattr(builtins, pack["class"])
exc = cls(pack["message"])
exc.parent_traceback = pack["traceback"]
raise exc
26 changes: 9 additions & 17 deletions artiq/protocols/pc_rpc.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@

import socket
import asyncio
import traceback
import threading
import time
import logging
@@ -22,6 +21,7 @@

from artiq.protocols import pyon
from artiq.protocols.asyncio_server import AsyncioServer as _AsyncioServer
from artiq.protocols.packed_exceptions import *


logger = logging.getLogger(__name__)
@@ -33,12 +33,6 @@ class AutoTarget:
pass


class RemoteError(Exception):
"""Raised when a RPC failed or raised an exception on the remote (server)
side."""
pass


class IncompatibleServer(Exception):
"""Raised by the client when attempting to connect to a server that does
not have the expected target."""
@@ -163,7 +157,7 @@ def __do_action(self, action):
if obj["status"] == "ok":
return obj["ret"]
elif obj["status"] == "failed":
raise RemoteError(obj["message"])
raise_packed_exc(obj["exception"])
else:
raise ValueError

@@ -267,7 +261,7 @@ async def __do_rpc(self, name, args, kwargs):
if obj["status"] == "ok":
return obj["ret"]
elif obj["status"] == "failed":
raise RemoteError(obj["message"])
raise_packed_exc(obj["exception"])
else:
raise ValueError
finally:
@@ -395,7 +389,7 @@ def __do_rpc(self, name, args, kwargs):
if obj["status"] == "ok":
return obj["ret"]
elif obj["status"] == "failed":
raise RemoteError(obj["message"])
raise_packed_exc(obj["exception"])
else:
raise ValueError

@@ -524,13 +518,11 @@ async def _process_action(self, target, obj):
.format(obj["action"]))
except asyncio.CancelledError:
raise
except Exception as exc:
short_exc_info = type(exc).__name__
exc_str = str(exc)
if exc_str:
short_exc_info += ": " + exc_str.splitlines()[0]
return {"status": "failed",
"message": short_exc_info + "\n" + traceback.format_exc()}
except:
return {
"status": "failed",
"exception": current_exc_packed()
}
finally:
if self._noparallel is not None:
self._noparallel.release()
4 changes: 3 additions & 1 deletion artiq/protocols/pyon.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
The main rationale for this new custom serializer (instead of using JSON) is
that JSON does not support Numpy and more generally cannot be extended with
other data types while keeping a concise syntax. Here we can use the Python
function call syntax to mark special data types.
function call syntax to express special data types.
"""


@@ -24,8 +24,10 @@
import tempfile

import numpy

from ..language.core import int as wrapping_int


_encode_map = {
type(None): "none",
bool: "bool",
4 changes: 2 additions & 2 deletions artiq/test/test_pc_rpc.py
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ def _blocking_echo(self, target):
self.assertEqual(test_object, test_object_back)
test_object_back = remote.async_echo(test_object)
self.assertEqual(test_object, test_object_back)
with self.assertRaises(pc_rpc.RemoteError):
with self.assertRaises(AttributeError):
remote.non_existing_method()
remote.terminate()
finally:
@@ -72,7 +72,7 @@ async def _asyncio_echo(self, target):
self.assertEqual(test_object, test_object_back)
test_object_back = await remote.async_echo(test_object)
self.assertEqual(test_object, test_object_back)
with self.assertRaises(pc_rpc.RemoteError):
with self.assertRaises(AttributeError):
await remote.non_existing_method()
await remote.terminate()
finally:
2 changes: 1 addition & 1 deletion conda/artiq/meta.yaml
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ build:
- artiq_coreconfig = artiq.frontend.artiq_coreconfig:main
- artiq_corelog = artiq.frontend.artiq_corelog:main
- artiq_ctlmgr = artiq.frontend.artiq_ctlmgr:main
- artiq_gui = artiq.frontend.artiq_gui:main
- artiq_dashboard = artiq.frontend.artiq_dashboard:main
- artiq_influxdb = artiq.frontend.artiq_influxdb:main
- artiq_master = artiq.frontend.artiq_master:main
- artiq_mkfs = artiq.frontend.artiq_mkfs:main
18 changes: 9 additions & 9 deletions doc/manual/getting_started_mgmt.rst
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ The manipulations described in this tutorial can be carried out using a single c
Starting your first experiment with the master
----------------------------------------------

In the previous tutorial, we used the ``artiq_run`` utility to execute our experiments, which is a simple stand-alone tool that bypasses the ARTIQ management system. We will now see how to run an experiment using the master (the central program in the management system that schedules and executes experiments) and the GUI client (that connects to the master and controls it).
In the previous tutorial, we used the ``artiq_run`` utility to execute our experiments, which is a simple stand-alone tool that bypasses the ARTIQ management system. We will now see how to run an experiment using the master (the central program in the management system that schedules and executes experiments) and the dashboard (that connects to the master and controls it).

First, create a folder ``~/artiq-master`` and copy the file ``device_db.pyon`` (containing the device database) found in the ``examples/master`` directory from the ARTIQ sources. The master uses those files in the same way as ``artiq_run``.

@@ -35,21 +35,21 @@ Start the master with: ::

This last command should not return, as the master keeps running.

Now, start the GUI client with the following commands in another terminal: ::
Now, start the dashboard with the following commands in another terminal: ::

$ cd ~
$ artiq_gui
$ artiq_dashboard

.. note:: The ``artiq_gui`` program uses a file called ``artiq_gui.pyon`` in the current directory to save and restore the GUI state (window/dock positions, last values entered by the user, etc.).
.. note:: The ``artiq_dashboard`` program uses a file called ``artiq_dashboard.pyon`` in the current directory to save and restore the GUI state (window/dock positions, last values entered by the user, etc.).

The GUI should display the list of experiments from the repository folder in a dock called "Explorer". There should be only the experiment we created. Select it and click "Submit", then look at the "Log" dock for the output from this simple experiment.
The dashboard should display the list of experiments from the repository folder in a dock called "Explorer". There should be only the experiment we created. Select it and click "Submit", then look at the "Log" dock for the output from this simple experiment.

.. note:: Multiple clients may be connected at the same time, possibly on different machines, and will be synchronized. See the ``-s`` option of ``artiq_gui`` and the ``--bind`` option of ``artiq_master`` to use the network. Both IPv4 and IPv6 are supported.
.. note:: Multiple clients may be connected at the same time, possibly on different machines, and will be synchronized. See the ``-s`` option of ``artiq_dashboard`` and the ``--bind`` option of ``artiq_master`` to use the network. Both IPv4 and IPv6 are supported.

Adding an argument
------------------

Experiments may have arguments whose values can be set in the GUI and used in the experiment's code. Modify the experiment as follows: ::
Experiments may have arguments whose values can be set in the dashboard and used in the experiment's code. Modify the experiment as follows: ::


def build(self):
@@ -66,7 +66,7 @@ Use the command-line client to trigger a repository rescan: ::

artiq_client scan-repository

The GUI should now display a spin box that allows you to set the value of the ``count`` argument. Try submitting the experiment as before.
The dashboard should now display a spin box that allows you to set the value of the ``count`` argument. Try submitting the experiment as before.

Setting up Git integration
--------------------------
@@ -93,7 +93,7 @@ There should be no errors displayed, and if you start the GUI again you should n
First, another small configuration step is needed. We must tell Git to make the master rescan the repository when new data is added to it. Create a file ``~/artiq-master/repository/hooks/post-receive`` with the following contents: ::

#!/bin/sh
artiq_client scan-repository
artiq_client scan-repository --async

Then set the execution permission on it: ::

2 changes: 1 addition & 1 deletion doc/manual/installing.rst
Original file line number Diff line number Diff line change
@@ -91,7 +91,7 @@ the existing environments using::
.. note::
The ``qt5`` package requires (on Linux only) libraries not packaged under the ``m-labs`` conda labels.
Those need to be installed through the Linux distribution's mechanism.
If ``artiq_gui`` does not start because ``it could not find or load the Qt platform plugin "xcb"``, install the various ``libxcb-*`` packages through your distribution's mechanism.
If GUI programs do not start because they ``could not find or load the Qt platform plugin "xcb"``, install the various ``libxcb-*`` packages through your distribution's mechanism.
The names of the libraries missing can be obtained from the output of a command like ``ldd [path-to-conda-installation]/envs/artiq-[date]/lib/qt5/plugins/platform/libqxcb.so``.

Preparing the core device FPGA board
Loading