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: dee844510c52
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: 324cafae3d75
Choose a head ref
  • 3 commits
  • 6 files changed
  • 1 contributor

Commits on Aug 17, 2015

  1. add InfluxDB bridge

    sbourdeauducq committed Aug 17, 2015
    3

    Verified

    This commit was signed with the committer’s verified signature.
    makenowjust Hiroya Fujinami
    Copy the full SHA
    fd3fefe View commit details
  2. gui: minor cleanup

    sbourdeauducq committed Aug 17, 2015
    Copy the full SHA
    abbf5eb View commit details
  3. rpctool: use pprint

    sbourdeauducq committed Aug 17, 2015
    Copy the full SHA
    324cafa View commit details
Showing with 224 additions and 3 deletions.
  1. +0 −1 artiq/frontend/artiq_gui.py
  2. +206 −0 artiq/frontend/artiq_influxdb.py
  3. +2 −1 artiq/frontend/artiq_rpctool.py
  4. +2 −0 doc/manual/default_network_ports.rst
  5. +7 −0 doc/manual/utilities.rst
  6. +7 −1 setup.py
1 change: 0 additions & 1 deletion artiq/frontend/artiq_gui.py
Original file line number Diff line number Diff line change
@@ -48,7 +48,6 @@ class MainWindow(QtGui.QMainWindow):
def __init__(self, app):
QtGui.QMainWindow.__init__(self)
self.setWindowIcon(QtGui.QIcon(os.path.join(data_dir, "icon.png")))
#self.resize(1400, 800)
self.setWindowTitle("ARTIQ")
self.exit_request = asyncio.Event()

206 changes: 206 additions & 0 deletions artiq/frontend/artiq_influxdb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#!/usr/bin/env python3

import argparse
import logging
import asyncio
import atexit
import fnmatch
from functools import partial

import aiohttp

from artiq.tools import verbosity_args, init_logger
from artiq.tools import TaskObject
from artiq.protocols.sync_struct import Subscriber
from artiq.protocols.pc_rpc import Server
from artiq.protocols import pyon


logger = logging.getLogger(__name__)


def get_argparser():
parser = argparse.ArgumentParser(
description="ARTIQ data to InfluxDB bridge")
group = parser.add_argument_group("master")
group.add_argument(
"--server-master", default="::1",
help="hostname or IP of the master to connect to")
group.add_argument(
"--port-master", default=3250, type=int,
help="TCP port to use to connect to the master")
group.add_argument(
"--retry-master", default=5.0, type=float,
help="retry timer for reconnecting to master")
group = parser.add_argument_group("database")
group.add_argument(
"--baseurl-db", default="http://localhost:8086",
help="base URL to access InfluxDB (default: %(default)s)")
group.add_argument(
"--user-db", default="", help="InfluxDB username")
group.add_argument(
"--password-db", default="", help="InfluxDB password")
group.add_argument(
"--database", default="db", help="database name to use")
group.add_argument(
"--table", default="lab", help="table name to use")
group = parser.add_argument_group("filter")
group.add_argument(
"--bind", default="::1",
help="hostname or IP address to bind to")
group.add_argument(
"--bind-port", default=3248, type=int,
help="TCP port to listen to for control (default: %(default)d)")
group.add_argument(
"--filter-file", default="influxdb_filter.pyon",
help="file to save the filter in (default: %(default)s)")
verbosity_args(parser)
return parser


class DBWriter(TaskObject):
def __init__(self, base_url, user, password, database, table):
self.base_url = base_url
self.user = user
self.password = password
self.database = database
self.table = table

self._queue = asyncio.Queue(100)

def update(self, k, v):
try:
self._queue.put_nowait((k, v))
except asyncio.QueueFull:
logger.warning("failed to update parameter '%s': "
"too many pending updates", k)

@asyncio.coroutine
def _do(self):
while True:
k, v = yield from self._queue.get()
url = self.base_url + "/write"
params = {"u": self.user, "p": self.password, "db": self.database,
"consistency": "any", "precision": "n"}
data = "{} {}={}".format(self.table, k, v)
try:
response = yield from aiohttp.request(
"POST", url, params=params, data=data)
except:
logger.warning("got exception trying to update '%s'",
k, exc_info=True)
else:
if response.status not in (200, 204):
logger.warning("got HTTP status %d trying to update '%s'",
response.status, k)
response.close()


class Parameters:
def __init__(self, filter_function, writer, init):
self.filter_function = filter_function
self.writer = writer

def __setitem__(self, k, v):
try:
v = float(v)
except:
pass
else:
if self.filter_function(k):
self.writer.update(k, v)


class MasterReader(TaskObject):
def __init__(self, server, port, retry, filter_function, writer):
self.server = server
self.port = port
self.retry = retry

self.filter_function = filter_function
self.writer = writer

@asyncio.coroutine
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)


class Filter:
def __init__(self, filter_file):
self.filter_file = filter_file
self.filter = []
try:
self.filter = pyon.load_file(self.filter_file)
except FileNotFoundError:
logger.info("no filter file found, using empty filter")

def _save(self):
pyon.store_file(self.filter_file, self.filter)

def _filter(self, k):
for pattern in self.filter:
if fnmatch.fnmatchcase(k, pattern):
return False
return True

def add_pattern(self, pattern):
"""Add a name pattern to ignore."""
if pattern not in self.filter:
self.filter.append(pattern)
self._save()

def remove_pattern(self, pattern):
"""Remove a pattern name to ignore."""
self.pattern.remove(pattern)
self._save()

def get_patterns(self):
"""Show ignore patterns."""
return self.filter


def main():
args = get_argparser().parse_args()
init_logger(args)

loop = asyncio.get_event_loop()
atexit.register(lambda: loop.close())

writer = DBWriter(args.baseurl_db,
args.user_db, args.password_db,
args.database, args.table)
writer.start()
atexit.register(lambda: loop.run_until_complete(writer.stop()))

filter = Filter(args.filter_file)
rpc_server = Server({"influxdb_filter": filter}, builtin_terminate=True)
loop.run_until_complete(rpc_server.start(args.bind, args.bind_port))
atexit.register(lambda: loop.run_until_complete(rpc_server.stop()))

reader = MasterReader(args.server_master, args.port_master,
args.retry_master, filter._filter, writer)
reader.start()
atexit.register(lambda: loop.run_until_complete(reader.stop()))

loop.run_until_complete(rpc_server.wait_terminate())


if __name__ == "__main__":
main()
3 changes: 2 additions & 1 deletion artiq/frontend/artiq_rpctool.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import textwrap
import sys
import numpy as np # Needed to use numpy in RPC call arguments on cmd line
import pprint

from artiq.protocols.pc_rpc import Client

@@ -77,7 +78,7 @@ def call_method(remote, method_name, args):
method = getattr(remote, method_name)
ret = method(*[eval(arg) for arg in args])
if ret is not None:
print("{}".format(ret))
pprint.pprint(ret)


def main():
2 changes: 2 additions & 0 deletions doc/manual/default_network_ports.rst
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@ Default network ports
+--------------------------+--------------+
| Core device (mon/inj) | 3250 (UDP) |
+--------------------------+--------------+
| InfluxDB bridge | 3248 |
+--------------------------+--------------+
| Controller manager | 3249 |
+--------------------------+--------------+
| Master (notifications) | 3250 |
7 changes: 7 additions & 0 deletions doc/manual/utilities.rst
Original file line number Diff line number Diff line change
@@ -152,3 +152,10 @@ it::
.. argparse::
:ref: artiq.frontend.artiq_coretool.get_argparser
:prog: artiq_coretool

Data to InfluxDB bridge
-----------------------

.. argparse::
:ref: artiq.frontend.artiq_influxdb.get_argparser
:prog: artiq_influxdb
8 changes: 7 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -8,20 +8,25 @@
if sys.version_info[:3] < (3, 4, 3):
raise Exception("You need at least Python 3.4.3 to run ARTIQ")


class PushDocCommand(Command):
description = "uploads the documentation to m-labs.hk"
user_options = []

def initialize_options(self):
pass

def finalize_options(self):
pass

def run(self):
os.system("rsync -avz doc/manual/_build/html/ shell.serverraum.org:~/web/m-labs.hk/artiq/manual")


requirements = [
"sphinx", "sphinx-argparse", "pyserial", "numpy", "scipy",
"python-dateutil", "prettytable", "h5py", "pydaqmx", "pyelftools",
"quamash", "pyqtgraph", "llvmlite_artiq", "pygit2"
"quamash", "pyqtgraph", "llvmlite_artiq", "pygit2", "aiohttp"
]

scripts = [
@@ -30,6 +35,7 @@ def run(self):
"artiq_coretool=artiq.frontend.artiq_coretool:main",
"artiq_ctlmgr=artiq.frontend.artiq_ctlmgr:main",
"artiq_gui=artiq.frontend.artiq_gui:main",
"artiq_influxdb=artiq.frontend.artiq_influxdb:main",
"artiq_master=artiq.frontend.artiq_master:main",
"artiq_mkfs=artiq.frontend.artiq_mkfs:main",
"artiq_rpctool=artiq.frontend.artiq_rpctool:main",