forked from M-Labs/artiq
refactor device/parameter management, immediate parameter updates, start introducing results
This commit is contained in:
parent
c938e3f4f0
commit
891c0d12f2
@ -1,5 +1,5 @@
|
||||
from artiq.language.core import *
|
||||
from artiq.language.context import *
|
||||
from artiq.language.db import *
|
||||
from artiq.language.units import check_unit
|
||||
from artiq.language.units import ps, ns, us, ms, s
|
||||
from artiq.language.units import Hz, kHz, MHz, GHz
|
||||
|
@ -1,6 +1,6 @@
|
||||
from operator import itemgetter
|
||||
|
||||
from artiq.language.context import AutoContext
|
||||
from artiq.language.db import AutoDB
|
||||
from artiq.language.units import ms, ns
|
||||
from artiq.coredevice.runtime import LinkInterface
|
||||
|
||||
@ -14,8 +14,9 @@ class _RuntimeEnvironment(LinkInterface):
|
||||
return str(self.llvm_module)
|
||||
|
||||
|
||||
class Comm(AutoContext):
|
||||
implicit_core = False
|
||||
class Comm(AutoDB):
|
||||
class DBKeys:
|
||||
implicit_core = False
|
||||
|
||||
def get_runtime_env(self):
|
||||
return _RuntimeEnvironment(1*ns)
|
||||
|
@ -7,7 +7,7 @@ import logging
|
||||
|
||||
from artiq.language import core as core_language
|
||||
from artiq.language import units
|
||||
from artiq.language.context import *
|
||||
from artiq.language.db import *
|
||||
from artiq.coredevice.runtime import Environment
|
||||
from artiq.coredevice import runtime_exceptions
|
||||
from artiq.coredevice.rpc_wrapper import RPCWrapper
|
||||
@ -60,10 +60,11 @@ def _read_exactly(f, n):
|
||||
return r
|
||||
|
||||
|
||||
class Comm(AutoContext):
|
||||
serial_dev = Parameter("/dev/ttyUSB1")
|
||||
baud_rate = Parameter(115200)
|
||||
implicit_core = False
|
||||
class Comm(AutoDB):
|
||||
class DBKeys:
|
||||
serial_dev = Parameter("/dev/ttyUSB1")
|
||||
baud_rate = Parameter(115200)
|
||||
implicit_core = False
|
||||
|
||||
def build(self):
|
||||
self.port = serial.Serial(self.serial_dev, baudrate=115200)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import os
|
||||
|
||||
from artiq.language.core import *
|
||||
from artiq.language.context import *
|
||||
from artiq.language.db import *
|
||||
|
||||
from artiq.transforms.inline import inline
|
||||
from artiq.transforms.lower_units import lower_units
|
||||
@ -44,10 +44,11 @@ def _no_debug_unparse(label, node):
|
||||
pass
|
||||
|
||||
|
||||
class Core(AutoContext):
|
||||
comm = Device("comm")
|
||||
external_clock = Parameter(None)
|
||||
implicit_core = False
|
||||
class Core(AutoDB):
|
||||
class DBKeys:
|
||||
comm = Device()
|
||||
external_clock = Parameter(None)
|
||||
implicit_core = False
|
||||
|
||||
def build(self):
|
||||
self.runtime_env = self.comm.get_runtime_env()
|
||||
|
@ -1,5 +1,5 @@
|
||||
from artiq.language.core import *
|
||||
from artiq.language.context import *
|
||||
from artiq.language.db import *
|
||||
from artiq.language.units import *
|
||||
from artiq.coredevice import rtio
|
||||
|
||||
@ -10,7 +10,7 @@ PHASE_MODE_ABSOLUTE = 1
|
||||
PHASE_MODE_TRACKING = 2
|
||||
|
||||
|
||||
class DDS(AutoContext):
|
||||
class DDS(AutoDB):
|
||||
"""Core device Direct Digital Synthesis (DDS) driver.
|
||||
|
||||
Controls DDS devices managed directly by the core device's runtime. It also
|
||||
@ -24,15 +24,16 @@ class DDS(AutoContext):
|
||||
the DDS device.
|
||||
|
||||
"""
|
||||
dds_sysclk = Parameter(1*GHz)
|
||||
reg_channel = Parameter()
|
||||
rtio_switch = Parameter()
|
||||
class DBKeys:
|
||||
dds_sysclk = Parameter(1*GHz)
|
||||
reg_channel = Argument()
|
||||
rtio_switch = Argument()
|
||||
|
||||
def build(self):
|
||||
self.previous_on = False
|
||||
self.previous_frequency = 0*MHz
|
||||
self.set_phase_mode(PHASE_MODE_CONTINUOUS)
|
||||
self.sw = rtio.RTIOOut(self, channel=self.rtio_switch)
|
||||
self.sw = rtio.RTIOOut(core=self.core, channel=self.rtio_switch)
|
||||
|
||||
@portable
|
||||
def frequency_to_ftw(self, frequency):
|
||||
|
@ -1,9 +1,10 @@
|
||||
from artiq.language.core import *
|
||||
from artiq.language.context import *
|
||||
from artiq.language.db import *
|
||||
|
||||
|
||||
class GPIOOut(AutoContext):
|
||||
channel = Parameter()
|
||||
class GPIOOut(AutoDB):
|
||||
class DBKeys:
|
||||
channel = Argument()
|
||||
|
||||
@kernel
|
||||
def on(self):
|
||||
|
@ -1,8 +1,8 @@
|
||||
from artiq.language.core import *
|
||||
from artiq.language.context import *
|
||||
from artiq.language.db import *
|
||||
|
||||
|
||||
class LLRTIOOut(AutoContext):
|
||||
class LLRTIOOut(AutoDB):
|
||||
"""Low-level RTIO output driver.
|
||||
|
||||
Allows setting RTIO outputs at arbitrary times, without time unit
|
||||
@ -12,7 +12,8 @@ class LLRTIOOut(AutoContext):
|
||||
``RTIOOut`` instead.
|
||||
|
||||
"""
|
||||
channel = Parameter()
|
||||
class DBKeys:
|
||||
channel = Argument()
|
||||
|
||||
def build(self):
|
||||
self._set_oe()
|
||||
@ -50,8 +51,9 @@ class LLRTIOOut(AutoContext):
|
||||
self.set_value(t, 0)
|
||||
|
||||
|
||||
class _RTIOBase(AutoContext):
|
||||
channel = Parameter()
|
||||
class _RTIOBase(AutoDB):
|
||||
class DBKeys:
|
||||
channel = Argument()
|
||||
|
||||
def build(self):
|
||||
self.previous_timestamp = int64(0) # in RTIO cycles
|
||||
|
@ -1,5 +1,5 @@
|
||||
from artiq.language.core import *
|
||||
from artiq.language.context import *
|
||||
from artiq.language.db import *
|
||||
from artiq.language.units import *
|
||||
from artiq.coredevice import rtio
|
||||
|
||||
@ -111,16 +111,17 @@ class _Frame:
|
||||
del self.fn
|
||||
|
||||
|
||||
class CompoundPDQ2(AutoContext):
|
||||
ids = Parameter()
|
||||
rtio_trigger = Parameter()
|
||||
rtio_frame = Parameter()
|
||||
class CompoundPDQ2(AutoDB):
|
||||
class DBKeys:
|
||||
ids = Argument()
|
||||
rtio_trigger = Argument()
|
||||
rtio_frame = Argument()
|
||||
|
||||
def build(self):
|
||||
self.trigger = rtio.LLRTIOOut(self, channel=self.rtio_trigger)
|
||||
self.frame0 = rtio.LLRTIOOut(self, channel=self.rtio_frame[0])
|
||||
self.frame1 = rtio.LLRTIOOut(self, channel=self.rtio_frame[1])
|
||||
self.frame2 = rtio.LLRTIOOut(self, channel=self.rtio_frame[2])
|
||||
self.trigger = rtio.LLRTIOOut(core=self.core, channel=self.rtio_trigger)
|
||||
self.frame0 = rtio.LLRTIOOut(core=self.core, channel=self.rtio_frame[0])
|
||||
self.frame1 = rtio.LLRTIOOut(core=self.core, channel=self.rtio_frame[1])
|
||||
self.frame2 = rtio.LLRTIOOut(core=self.core, channel=self.rtio_frame[2])
|
||||
|
||||
self.frames = []
|
||||
self.current_frame = -1
|
||||
|
@ -1,179 +0,0 @@
|
||||
"""
|
||||
Device and parameter attributes.
|
||||
|
||||
"""
|
||||
|
||||
class _AttributeKind:
|
||||
pass
|
||||
|
||||
|
||||
class Device(_AttributeKind):
|
||||
"""Represents a device for ``AutoContext`` to process.
|
||||
|
||||
:param type_hint: An optional string giving a hint about the type of the
|
||||
device.
|
||||
|
||||
"""
|
||||
def __init__(self, type_hint=None):
|
||||
self.type_hint = type_hint
|
||||
|
||||
|
||||
class NoDefault:
|
||||
"""Represents the absence of a default value for ``Parameter``.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Parameter(_AttributeKind):
|
||||
"""Represents a parameter (from the database) for ``AutoContext``
|
||||
to process.
|
||||
|
||||
:param default: Default value of the parameter to be used if not found
|
||||
in the database.
|
||||
:param write_db: Writes any modification of the parameter back to the
|
||||
database.
|
||||
|
||||
"""
|
||||
def __init__(self, default=NoDefault, write_db=False):
|
||||
self.default = default
|
||||
self.write_db = write_db
|
||||
|
||||
|
||||
class Argument(_AttributeKind):
|
||||
"""Represents an argument (specifiable at instance creation) for
|
||||
``AutoContext`` to process.
|
||||
|
||||
:param default: Default value of the argument to be used if not specified
|
||||
at instance creation.
|
||||
|
||||
"""
|
||||
def __init__(self, default=NoDefault):
|
||||
self.default = default
|
||||
|
||||
|
||||
class AutoContext:
|
||||
"""Base class to automate device and parameter discovery.
|
||||
|
||||
Drivers and experiments should in most cases overload this class to
|
||||
obtain the parameters and devices (including the core device) that they
|
||||
need.
|
||||
|
||||
This class sets all its ``__init__`` keyword arguments as attributes. It
|
||||
then iterates over each element in the attribute dictionary of the class,
|
||||
and when they are abtract attributes (e.g. ``Device``, ``Parameter``),
|
||||
requests them from the ``mvs`` (Missing Value Supplier) object.
|
||||
|
||||
A ``AutoContext`` instance can be used as MVS. If the requested parameter
|
||||
is within its attributes, the value of that attribute is returned.
|
||||
Otherwise, the request is forwarded to the parent MVS.
|
||||
|
||||
All keyword arguments are set as object attributes. This enables setting
|
||||
parameters of a lower-level ``AutoContext`` object using keyword arguments
|
||||
without having those explicitly listed in the upper-level ``AutoContext``
|
||||
parameter list.
|
||||
|
||||
At the top-level, it is possible to have a MVS that issues requests to a
|
||||
database and hardware management system.
|
||||
|
||||
:var implicit_core: Automatically adds a ``core`` device to the attributes.
|
||||
Default: True.
|
||||
|
||||
Example:
|
||||
|
||||
>>> class SubExperiment(AutoContext):
|
||||
... foo = Parameter()
|
||||
... bar = Parameter()
|
||||
...
|
||||
... def run(self):
|
||||
... do_something(self.foo, self.bar)
|
||||
...
|
||||
>>> class MainExperiment(AutoContext):
|
||||
... bar1 = Parameter()
|
||||
... bar2 = Parameter()
|
||||
... offset = Parameter()
|
||||
...
|
||||
... def build(self):
|
||||
... self.exp1 = SubExperiment(self, bar=self.bar1)
|
||||
... self.exp2 = SubExperiment(self, bar=self.bar2)
|
||||
... self.exp3 = SubExperiment(self, bar=self.bar2 + self.offset)
|
||||
...
|
||||
... def run(self):
|
||||
... self.exp1.run()
|
||||
... self.exp2.run()
|
||||
... self.exp3.run()
|
||||
...
|
||||
>>> # does not require a database.
|
||||
>>> a = MainExperiment(foo=1, bar1=2, bar2=3, offset=0)
|
||||
>>> # "foo" and "offset" are automatically retrieved from the database.
|
||||
>>> b = MainExperiment(db_mvs, bar1=2, bar2=3)
|
||||
|
||||
"""
|
||||
implicit_core = True
|
||||
|
||||
def __init__(self, mvs=None, **kwargs):
|
||||
if self.implicit_core:
|
||||
if hasattr(self, "core"):
|
||||
raise ValueError(
|
||||
"Set implicit_core to False when"
|
||||
" core is explicitly specified")
|
||||
self.core = Device("core")
|
||||
|
||||
self.mvs = mvs
|
||||
for k, v in kwargs.items():
|
||||
if hasattr(self, k):
|
||||
p = getattr(self, k)
|
||||
if isinstance(p, Parameter) and p.write_db:
|
||||
self.mvs.register_parameter_wb(self, k)
|
||||
if (not hasattr(self, k)
|
||||
or not isinstance(getattr(self, k), _AttributeKind)):
|
||||
raise ValueError(
|
||||
"Got unexpected keyword argument: '{}'".format(k))
|
||||
setattr(self, k, v)
|
||||
|
||||
for k in dir(self):
|
||||
v = getattr(self, k)
|
||||
if isinstance(v, _AttributeKind):
|
||||
if isinstance(v, Argument):
|
||||
# never goes through MVS
|
||||
if v.default is NoDefault:
|
||||
raise AttributeError(
|
||||
"No value specified for argument '{}'".format(k))
|
||||
value = v.default
|
||||
else:
|
||||
if self.mvs is None:
|
||||
if (isinstance(v, Parameter)
|
||||
and v.default is not NoDefault):
|
||||
value = v.default
|
||||
else:
|
||||
raise AttributeError("Attribute '{}' not specified"
|
||||
" and no MVS present".format(k))
|
||||
else:
|
||||
value = self.mvs.get_missing_value(k, v, self)
|
||||
if isinstance(v, Parameter) and v.write_db:
|
||||
self.mvs.register_parameter_wb(self, k)
|
||||
setattr(self, k, value)
|
||||
|
||||
self.build()
|
||||
|
||||
def get_missing_value(self, name, kind, requester):
|
||||
"""Attempts to retrieve ``name`` from the object's attributes.
|
||||
If not present, forwards the request to the parent MVS.
|
||||
|
||||
The presence of this method makes ``AutoContext`` act as a MVS.
|
||||
|
||||
"""
|
||||
try:
|
||||
return getattr(self, name)
|
||||
except AttributeError:
|
||||
return self.mvs.get_missing_value(name, kind, requester)
|
||||
|
||||
def build(self):
|
||||
"""This is called by ``__init__`` after the parameter initialization
|
||||
is done.
|
||||
|
||||
The user may overload this method to complete the object's
|
||||
initialization with all parameters available.
|
||||
|
||||
"""
|
||||
pass
|
145
artiq/language/db.py
Normal file
145
artiq/language/db.py
Normal file
@ -0,0 +1,145 @@
|
||||
"""
|
||||
Connection to device, parameter and result database.
|
||||
|
||||
"""
|
||||
|
||||
class _AttributeKind:
|
||||
pass
|
||||
|
||||
|
||||
class Device(_AttributeKind):
|
||||
"""Represents a device for ``AutoDB`` to process.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class NoDefault:
|
||||
"""Represents the absence of a default value for ``Parameter``
|
||||
and ``Argument``.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Parameter(_AttributeKind):
|
||||
"""Represents a parameter (from the database) for ``AutoDB``
|
||||
to process.
|
||||
|
||||
:param default: Default value of the parameter to be used if not found
|
||||
in the database.
|
||||
|
||||
"""
|
||||
def __init__(self, default=NoDefault):
|
||||
self.default = default
|
||||
|
||||
|
||||
class Argument(_AttributeKind):
|
||||
"""Represents an argument (specifiable at instance creation) for
|
||||
``AutoDB`` to process.
|
||||
|
||||
:param default: Default value of the argument to be used if not specified
|
||||
at instance creation.
|
||||
|
||||
"""
|
||||
def __init__(self, default=NoDefault):
|
||||
self.default = default
|
||||
|
||||
|
||||
class Result(_AttributeKind):
|
||||
"""Represents a result for ``AutoDB`` to process.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class AutoDB:
|
||||
"""Base class to automate device, parameter and result database access.
|
||||
|
||||
Drivers and experiments should in most cases overload this class to
|
||||
obtain the parameters and devices (including the core device) that they
|
||||
need, report results, and modify parameters.
|
||||
|
||||
:param dbh: database hub to use. If ``None``, all devices and parameters
|
||||
must be supplied as keyword arguments, and reporting results and
|
||||
modifying parameters is not supported.
|
||||
|
||||
"""
|
||||
class DBKeys:
|
||||
pass
|
||||
|
||||
def __init__(self, dbh=None, **kwargs):
|
||||
self.dbh = dbh
|
||||
|
||||
dbkeys = self.DBKeys()
|
||||
if getattr(dbkeys, "implicit_core", True):
|
||||
if hasattr(dbkeys, "core"):
|
||||
raise ValueError(
|
||||
"Set implicit_core to False when"
|
||||
" core is explicitly specified")
|
||||
dbkeys.core = Device()
|
||||
|
||||
for k, v in kwargs.items():
|
||||
object.__setattr__(self, k, v)
|
||||
|
||||
for k in dir(dbkeys):
|
||||
if k not in self.__dict__:
|
||||
ak = getattr(dbkeys, k)
|
||||
if isinstance(ak, Argument):
|
||||
if ak.default is NoDefault:
|
||||
raise AttributeError(
|
||||
"No value specified for argument '{}'".format(k))
|
||||
object.__setattr__(self, k, ak.default)
|
||||
elif isinstance(ak, Device):
|
||||
try:
|
||||
dev = self.dbh.get_device(k)
|
||||
except KeyError:
|
||||
raise KeyError("Device '{}' not found".format(k))
|
||||
object.__setattr__(self, k, dev)
|
||||
|
||||
self.build()
|
||||
|
||||
def __getattr__(self, name):
|
||||
ak = getattr(self.DBKeys, name)
|
||||
if isinstance(ak, Parameter):
|
||||
try:
|
||||
if self.dbh is None:
|
||||
raise KeyError
|
||||
return self.dbh.get_parameter(name)
|
||||
except KeyError:
|
||||
if ak.default is not NoDefault:
|
||||
return ak.default
|
||||
else:
|
||||
raise AttributeError("Parameter '{}' not in database"
|
||||
" and without default value"
|
||||
.format(name))
|
||||
elif isinstance(ak, Result):
|
||||
try:
|
||||
return self.dbh.get_result(name)
|
||||
except KeyError:
|
||||
raise AttributeError("Result '{}' not found".format(name))
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
try:
|
||||
ak = getattr(self.DBKeys, name)
|
||||
except AttributeError:
|
||||
object.__setattr__(self, name, value)
|
||||
else:
|
||||
if isinstance(ak, Parameter):
|
||||
self.dbh.set_parameter(name, value)
|
||||
elif isinstance(ak, Result):
|
||||
self.dbh.set_result(name, value)
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
def build(self):
|
||||
"""This is called by ``__init__`` after the parameter initialization
|
||||
is done.
|
||||
|
||||
The user may overload this method to complete the object's
|
||||
initialization with all parameters available.
|
||||
|
||||
"""
|
||||
pass
|
106
artiq/management/db.py
Normal file
106
artiq/management/db.py
Normal file
@ -0,0 +1,106 @@
|
||||
from collections import OrderedDict, defaultdict
|
||||
import importlib
|
||||
from time import time
|
||||
|
||||
from artiq.language.db import *
|
||||
from artiq.management import pyon
|
||||
from artiq.management.sync_struct import Notifier
|
||||
|
||||
|
||||
class FlatFileDB:
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.data = Notifier(pyon.load_file(self.filename))
|
||||
self.hooks = []
|
||||
|
||||
def save(self):
|
||||
pyon.store_file(self.filename, self.data.backing_struct)
|
||||
|
||||
def request(self, name):
|
||||
return self.data.backing_struct[name]
|
||||
|
||||
def set(self, name, value):
|
||||
self.data[name] = value
|
||||
self.save()
|
||||
timestamp = time()
|
||||
for hook in self.hooks:
|
||||
hook.set(timestamp, name, value)
|
||||
|
||||
def delete(self, name):
|
||||
del self.data[name]
|
||||
self.save()
|
||||
timestamp = time()
|
||||
for hook in self.hooks:
|
||||
hook.delete(timestamp, name)
|
||||
|
||||
|
||||
class SimpleHistory:
|
||||
def __init__(self, depth):
|
||||
self.depth = depth
|
||||
self.history = Notifier([])
|
||||
|
||||
def set(self, timestamp, name, value):
|
||||
if len(self.history.backing_struct) >= self.depth:
|
||||
del self.history[0]
|
||||
self.history.append((timestamp, name, value))
|
||||
|
||||
def delete(self, timestamp, name):
|
||||
if len(self.history.backing_struct) >= self.depth:
|
||||
del self.history[0]
|
||||
self.history.append((timestamp, name))
|
||||
|
||||
|
||||
class ResultDB:
|
||||
def __init__(self):
|
||||
self.data = defaultdict(list)
|
||||
|
||||
def request(self, name):
|
||||
return self.data[name]
|
||||
|
||||
def set(self, name, value):
|
||||
self.data[name] = value
|
||||
|
||||
|
||||
def _create_device(desc, dbh):
|
||||
module = importlib.import_module(desc["module"])
|
||||
device_class = getattr(module, desc["class"])
|
||||
return device_class(dbh, **desc["arguments"])
|
||||
|
||||
|
||||
class DBHub:
|
||||
"""Connects device, parameter and result databases to experiment.
|
||||
Handle device driver creation and destruction.
|
||||
|
||||
"""
|
||||
def __init__(self, ddb, pdb, rdb):
|
||||
self.ddb = ddb
|
||||
self.active_devices = OrderedDict()
|
||||
|
||||
self.get_parameter = pdb.request
|
||||
self.set_parameter = pdb.set
|
||||
self.get_result = rdb.request
|
||||
self.set_result = rdb.set
|
||||
|
||||
def get_device(self, name):
|
||||
if name in self.active_devices:
|
||||
return self.active_devices[name]
|
||||
else:
|
||||
desc = self.ddb.request(name)
|
||||
while isinstance(desc, str):
|
||||
# alias
|
||||
desc = self.ddb.request(desc)
|
||||
dev = _create_device(desc, self)
|
||||
self.active_devices[name] = dev
|
||||
return dev
|
||||
|
||||
def close(self):
|
||||
"""Closes all active devices, in the opposite order as they were
|
||||
requested.
|
||||
|
||||
Do not use the same ``DBHub`` again after calling
|
||||
this function.
|
||||
|
||||
"""
|
||||
for dev in reversed(list(self.active_devices.values())):
|
||||
if hasattr(dev, "close"):
|
||||
dev.close()
|
@ -1,133 +0,0 @@
|
||||
from collections import OrderedDict
|
||||
import importlib
|
||||
from time import time
|
||||
|
||||
from artiq.language.context import *
|
||||
from artiq.management import pyon
|
||||
from artiq.management.sync_struct import Notifier
|
||||
|
||||
|
||||
def create_device(desc, mvs):
|
||||
module = importlib.import_module(desc["module"])
|
||||
device_class = getattr(module, desc["class"])
|
||||
return device_class(mvs, **desc["parameters"])
|
||||
|
||||
|
||||
class DeviceParamSupplier:
|
||||
"""Supplies devices and parameters to AutoContext objects.
|
||||
|
||||
"""
|
||||
def __init__(self, req_device, req_parameter):
|
||||
self.req_device = req_device
|
||||
self.req_parameter = req_parameter
|
||||
self.active_devices = OrderedDict()
|
||||
# list of (requester, name)
|
||||
self.parameter_wb = []
|
||||
|
||||
def get_missing_value(self, name, kind, requester):
|
||||
if isinstance(kind, Device):
|
||||
if name in self.active_devices:
|
||||
return self.active_devices[name]
|
||||
else:
|
||||
try:
|
||||
desc = self.req_device(name)
|
||||
except KeyError:
|
||||
raise KeyError(
|
||||
"Unknown device '{}' of type '{}' requested by {}"
|
||||
.format(name, kind.type_hint, requester))
|
||||
try:
|
||||
while isinstance(desc, str):
|
||||
# alias
|
||||
desc = self.req_device(desc)
|
||||
except KeyError:
|
||||
raise KeyError(
|
||||
"Unknown alias '{}' for device '{}' of type '{}'"
|
||||
" requested by {}"
|
||||
.format(desc, name, kind.type_hint, requester))
|
||||
dev = create_device(desc, self)
|
||||
self.active_devices[name] = dev
|
||||
return dev
|
||||
elif isinstance(kind, Parameter):
|
||||
try:
|
||||
return self.req_parameter(name)
|
||||
except KeyError:
|
||||
if kind.default is not NoDefault:
|
||||
return kind.default
|
||||
else:
|
||||
raise KeyError("Unknown parameter: " + name)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def register_parameter_wb(self, requester, name):
|
||||
self.parameter_wb.append((requester, name))
|
||||
|
||||
def close(self):
|
||||
"""Closes all active devices, in the opposite order as they were
|
||||
requested.
|
||||
|
||||
Do not use the same ``DeviceParamSupplier`` again after calling
|
||||
this function.
|
||||
|
||||
"""
|
||||
for dev in reversed(list(self.active_devices.values())):
|
||||
if hasattr(dev, "close"):
|
||||
dev.close()
|
||||
|
||||
|
||||
class DeviceParamDB:
|
||||
def __init__(self, ddb_file, pdb_file):
|
||||
self.ddb_file = ddb_file
|
||||
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)
|
||||
|
||||
def save_pdb(self):
|
||||
pyon.store_file(self.pdb_file, self.pdb.backing_struct)
|
||||
|
||||
def req_device(self, name):
|
||||
return self.ddb.backing_struct[name]
|
||||
|
||||
def set_device(self, name, description):
|
||||
self.ddb[name] = description
|
||||
self.save_ddb()
|
||||
|
||||
def del_device(self, name):
|
||||
del self.ddb[name]
|
||||
self.save_ddb()
|
||||
|
||||
def req_parameter(self, name):
|
||||
return self.pdb.backing_struct[name]
|
||||
|
||||
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))
|
@ -4,24 +4,24 @@ import traceback
|
||||
|
||||
from artiq.management import pyon
|
||||
from artiq.management.file_import import file_import
|
||||
from artiq.language.context import AutoContext
|
||||
from artiq.management.dpdb import DeviceParamSupplier
|
||||
from artiq.language.db import AutoDB
|
||||
from artiq.management.db import DBHub, ResultDB
|
||||
|
||||
|
||||
def run(dps, file, unit, arguments):
|
||||
def run(dbh, file, unit, arguments):
|
||||
module = file_import(file)
|
||||
if unit is None:
|
||||
units = [v for k, v in module.__dict__.items()
|
||||
if k[0] != "_"
|
||||
and isclass(v)
|
||||
and issubclass(v, AutoContext)
|
||||
and v is not AutoContext]
|
||||
and issubclass(v, AutoDB)
|
||||
and v is not AutoDB]
|
||||
if len(units) != 1:
|
||||
raise ValueError("Found {} units in module".format(len(units)))
|
||||
unit = units[0]
|
||||
else:
|
||||
unit = getattr(module, unit)
|
||||
unit_inst = unit(dps, **arguments)
|
||||
unit_inst = unit(dbh, **arguments)
|
||||
unit_inst.run()
|
||||
|
||||
|
||||
@ -56,9 +56,13 @@ def make_parent_action(action, argnames, exception=ParentActionError):
|
||||
return parent_action
|
||||
|
||||
|
||||
req_device = make_parent_action("req_device", "name", KeyError)
|
||||
req_parameter = make_parent_action("req_parameter", "name", KeyError)
|
||||
set_parameter = make_parent_action("set_parameter", "name value")
|
||||
class ParentDDB:
|
||||
request = make_parent_action("req_device", "name", KeyError)
|
||||
|
||||
|
||||
class ParentPDB:
|
||||
request = make_parent_action("req_parameter", "name", KeyError)
|
||||
set = make_parent_action("set_parameter", "name value")
|
||||
|
||||
|
||||
def main():
|
||||
@ -68,12 +72,11 @@ def main():
|
||||
obj = get_object()
|
||||
put_object("ack")
|
||||
|
||||
dps = DeviceParamSupplier(req_device, req_parameter)
|
||||
rdb = ResultDB()
|
||||
dbh = DBHub(ParentDDB, ParentPDB, rdb)
|
||||
try:
|
||||
try:
|
||||
run(dps, **obj)
|
||||
for requester, name in dps.parameter_wb:
|
||||
set_parameter(name, getattr(requester, name))
|
||||
run(dbh, **obj)
|
||||
except Exception:
|
||||
put_object({"action": "report_completed",
|
||||
"status": "failed",
|
||||
@ -82,7 +85,7 @@ def main():
|
||||
put_object({"action": "report_completed",
|
||||
"status": "ok"})
|
||||
finally:
|
||||
dps.close()
|
||||
dbh.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -1,13 +1,14 @@
|
||||
from random import Random
|
||||
|
||||
from artiq.language.core import delay, kernel
|
||||
from artiq.language.context import AutoContext, Parameter
|
||||
from artiq.language.db import AutoDB, Argument
|
||||
from artiq.language import units
|
||||
from artiq.sim import time
|
||||
|
||||
|
||||
class Core(AutoContext):
|
||||
implicit_core = False
|
||||
class Core(AutoDB):
|
||||
class DBKeys:
|
||||
implicit_core = False
|
||||
|
||||
_level = 0
|
||||
|
||||
@ -20,8 +21,9 @@ class Core(AutoContext):
|
||||
return r
|
||||
|
||||
|
||||
class Input(AutoContext):
|
||||
name = Parameter()
|
||||
class Input(AutoDB):
|
||||
class DBKeys:
|
||||
name = Argument()
|
||||
|
||||
def build(self):
|
||||
self.prng = Random()
|
||||
@ -40,8 +42,9 @@ class Input(AutoContext):
|
||||
return result
|
||||
|
||||
|
||||
class WaveOutput(AutoContext):
|
||||
name = Parameter()
|
||||
class WaveOutput(AutoDB):
|
||||
class DBKeys:
|
||||
name = Argument()
|
||||
|
||||
@kernel
|
||||
def pulse(self, frequency, duration):
|
||||
@ -49,8 +52,9 @@ class WaveOutput(AutoContext):
|
||||
delay(duration)
|
||||
|
||||
|
||||
class VoltageOutput(AutoContext):
|
||||
name = Parameter()
|
||||
class VoltageOutput(AutoDB):
|
||||
class DBKeys:
|
||||
name = Argument()
|
||||
|
||||
@kernel
|
||||
def set(self, value):
|
||||
|
@ -28,9 +28,10 @@ def _run_on_host(k_class, **parameters):
|
||||
k_inst.run()
|
||||
|
||||
|
||||
class _Primes(AutoContext):
|
||||
output_list = Parameter()
|
||||
maximum = Parameter()
|
||||
class _Primes(AutoDB):
|
||||
class DBKeys:
|
||||
output_list = Argument()
|
||||
maximum = Argument()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
@ -46,7 +47,7 @@ class _Primes(AutoContext):
|
||||
self.output_list.append(x)
|
||||
|
||||
|
||||
class _Misc(AutoContext):
|
||||
class _Misc(AutoDB):
|
||||
def build(self):
|
||||
self.input = 84
|
||||
self.inhomogeneous_units = []
|
||||
@ -82,9 +83,10 @@ class _Misc(AutoContext):
|
||||
delay(10*Hz)
|
||||
|
||||
|
||||
class _PulseLogger(AutoContext):
|
||||
output_list = Parameter()
|
||||
name = Parameter()
|
||||
class _PulseLogger(AutoDB):
|
||||
class DBKeys:
|
||||
output_list = Argument()
|
||||
name = Argument()
|
||||
|
||||
def _append(self, t, l, f):
|
||||
if not hasattr(self, "first_timestamp"):
|
||||
@ -104,12 +106,15 @@ class _PulseLogger(AutoContext):
|
||||
self.off(int(now().amount*1000000000))
|
||||
|
||||
|
||||
class _Pulses(AutoContext):
|
||||
output_list = Parameter()
|
||||
class _Pulses(AutoDB):
|
||||
class DBKeys:
|
||||
output_list = Argument()
|
||||
|
||||
def build(self):
|
||||
for name in "a", "b", "c", "d":
|
||||
pl = _PulseLogger(self, name=name)
|
||||
pl = _PulseLogger(core=self.core,
|
||||
output_list=self.output_list,
|
||||
name=name)
|
||||
setattr(self, name, pl)
|
||||
|
||||
@kernel
|
||||
@ -128,8 +133,9 @@ class _MyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class _Exceptions(AutoContext):
|
||||
trace = Parameter()
|
||||
class _Exceptions(AutoDB):
|
||||
class DBKeys:
|
||||
trace = Argument()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
@ -170,7 +176,7 @@ class _Exceptions(AutoContext):
|
||||
self.trace.append(104)
|
||||
|
||||
|
||||
class _RPCExceptions(AutoContext):
|
||||
class _RPCExceptions(AutoDB):
|
||||
def build(self):
|
||||
self.success = False
|
||||
|
||||
@ -250,10 +256,11 @@ class ExecutionCase(unittest.TestCase):
|
||||
comm.close()
|
||||
|
||||
|
||||
class _RTIOLoopback(AutoContext):
|
||||
i = Device("ttl_in")
|
||||
o = Device("ttl_out")
|
||||
npulses = Parameter()
|
||||
class _RTIOLoopback(AutoDB):
|
||||
class DBKeys:
|
||||
i = Device()
|
||||
o = Device()
|
||||
npulses = Argument()
|
||||
|
||||
def report(self, n):
|
||||
self.result = n
|
||||
@ -269,8 +276,9 @@ class _RTIOLoopback(AutoContext):
|
||||
self.report(self.i.count())
|
||||
|
||||
|
||||
class _RTIOUnderflow(AutoContext):
|
||||
o = Device("ttl_out")
|
||||
class _RTIOUnderflow(AutoDB):
|
||||
class DBKeys:
|
||||
o = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
@ -279,8 +287,9 @@ class _RTIOUnderflow(AutoContext):
|
||||
self.o.pulse(25*ns)
|
||||
|
||||
|
||||
class _RTIOSequenceError(AutoContext):
|
||||
o = Device("ttl_out")
|
||||
class _RTIOSequenceError(AutoDB):
|
||||
class DBKeys:
|
||||
o = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
|
@ -9,15 +9,16 @@ As a very first step, we will turn on a LED on the core device. Create a file ``
|
||||
from artiq import *
|
||||
|
||||
|
||||
class LED(AutoContext):
|
||||
led = Device("gpio_out")
|
||||
class LED(AutoDB):
|
||||
class DBKeys:
|
||||
led = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.led.on()
|
||||
|
||||
|
||||
The central part of our code is our ``LED`` class, that derives from :class:`artiq.language.core.AutoContext`. ``AutoContext`` is part of the mechanism that attaches device drivers and retrieves parameters according to a database. Abstract attributes such as ``Device("gpio_out")`` list the devices (and parameters) that our class needs in order to operate, and the names of the attributes (e.g. ``led``) are used to search the database. ``AutoContext`` replaces them with the actual device drivers (and parameter values). Finally, the ``@kernel`` decorator tells the system that the ``run`` method must be executed on the core device (instead of the host).
|
||||
The central part of our code is our ``LED`` class, that derives from :class:`artiq.language.db.AutoDB`. ``AutoDB`` is part of the mechanism that attaches device drivers and retrieves parameters according to a database. Our ``DBKeys`` class lists the devices (and parameters) that ``LED`` needs in order to operate, and the names of the attributes (e.g. ``led``) are used to search the database. ``AutoDB`` replaces them with the actual device drivers (and parameter values). Finally, the ``@kernel`` decorator tells the system that the ``run`` method must be executed on the core device (instead of the host).
|
||||
|
||||
Copy the files ``ddb.pyon`` and ``pdb.pyon`` (containing the device and parameter databases) from the ``examples`` folder of ARTIQ into the same directory as ``led.py`` (alternatively, you can use the ``-d`` and ``-p`` options of ``artiq_run.py``). You can open the database files using a text editor - their contents are in a human-readable format.
|
||||
|
||||
@ -37,8 +38,9 @@ Modify the code as follows: ::
|
||||
def input_led_state():
|
||||
return int(input("Enter desired LED state: "))
|
||||
|
||||
class LED(AutoContext):
|
||||
led = Device("gpio_out")
|
||||
class LED(AutoDB):
|
||||
class DBKeys:
|
||||
led = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
@ -79,8 +81,9 @@ Create a new file ``rtio.py`` containing the following: ::
|
||||
|
||||
from artiq import *
|
||||
|
||||
class Tutorial(AutoContext):
|
||||
ttl0 = Device("ttl_out")
|
||||
class Tutorial(AutoDB):
|
||||
class DBKeys:
|
||||
ttl0 = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
@ -100,9 +103,10 @@ Try reducing the period of the generated waveform until the CPU cannot keep up w
|
||||
def print_underflow():
|
||||
print("RTIO underflow occured")
|
||||
|
||||
class Tutorial(AutoContext):
|
||||
led = Device("gpio_out")
|
||||
ttl0 = Device("ttl_out")
|
||||
class Tutorial(AutoDB):
|
||||
class DBKeys:
|
||||
led = Device()
|
||||
ttl0 = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
|
@ -81,7 +81,7 @@ Run it as before, while the controller is running. You should see the message ap
|
||||
$ ./hello_controller.py
|
||||
message: Hello World!
|
||||
|
||||
When using the driver in an experiment, for simple cases the ``Client`` instance can be returned by the :class:`artiq.language.core.AutoContext` mechanism and used normally as a device.
|
||||
When using the driver in an experiment, for simple cases the ``Client`` instance can be returned by the :class:`artiq.language.db.AutoDB` mechanism and used normally as a device.
|
||||
|
||||
:warning: RPC servers operate on copies of objects provided by the client, and modifications to mutable types are not written back. For example, if the client passes a list as a parameter of an RPC method, and that method ``append()s`` an element to the list, the element is not appended to the client's list.
|
||||
|
||||
|
@ -86,7 +86,7 @@ with parallel:
|
||||
\begin{frame}[fragile]
|
||||
\frametitle{\fontseries{l}\selectfont Object orientation and code reuse}
|
||||
\begin{verbatimtab}
|
||||
class Main(AutoContext):
|
||||
class Main(AutoDB):
|
||||
def build(self):
|
||||
self.ion1 = Ion(...)
|
||||
self.ion2 = Ion(...)
|
||||
@ -130,9 +130,9 @@ class Main(AutoContext):
|
||||
\begin{frame}[fragile]
|
||||
\frametitle{\fontseries{l}\selectfont Channels and parameters}
|
||||
\begin{itemize}
|
||||
\item A kernel is a method of a class that derives from the \verb!AutoContext! class
|
||||
\item A kernel is a method of a class
|
||||
\item The entry point for an experiment is called \verb!run! --- may or may not be a kernel
|
||||
\item The \verb!AutoContext! class manages channels and parameters, and sets them as attributes
|
||||
\item The \verb!AutoDB! class manages channels and parameters
|
||||
\item If channels/parameters are passed as constructor arguments, those are used
|
||||
\item Otherwise, they are looked up in the device and parameter databases
|
||||
\end{itemize}
|
||||
|
@ -1,16 +1,17 @@
|
||||
from artiq import *
|
||||
|
||||
|
||||
class AluminumSpectroscopy(AutoContext):
|
||||
mains_sync = Device("ttl_in")
|
||||
laser_cooling = Device("dds")
|
||||
spectroscopy = Device("dds")
|
||||
spectroscopy_b = Device("dac")
|
||||
state_detection = Device("dds")
|
||||
pmt = Device("ttl_in")
|
||||
spectroscopy_freq = Parameter(432*MHz)
|
||||
photon_limit_low = Parameter(10)
|
||||
photon_limit_high = Parameter(15)
|
||||
class AluminumSpectroscopy(AutoDB):
|
||||
class DBKeys:
|
||||
mains_sync = Device()
|
||||
laser_cooling = Device()
|
||||
spectroscopy = Device()
|
||||
spectroscopy_b = Device()
|
||||
state_detection = Device()
|
||||
pmt = Device()
|
||||
spectroscopy_freq = Parameter(432*MHz)
|
||||
photon_limit_low = Argument(10)
|
||||
photon_limit_high = Argument(15)
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
|
@ -2,67 +2,67 @@
|
||||
"comm": {
|
||||
"module": "artiq.coredevice.comm_serial",
|
||||
"class": "Comm",
|
||||
"parameters": {}
|
||||
"arguments": {}
|
||||
},
|
||||
"core": {
|
||||
"module": "artiq.coredevice.core",
|
||||
"class": "Core",
|
||||
"parameters": {}
|
||||
"arguments": {}
|
||||
},
|
||||
|
||||
"led": {
|
||||
"module": "artiq.coredevice.gpio",
|
||||
"class": "GPIOOut",
|
||||
"parameters": {"channel": 0}
|
||||
"arguments": {"channel": 0}
|
||||
},
|
||||
|
||||
"pmt0": {
|
||||
"module": "artiq.coredevice.rtio",
|
||||
"class": "RTIOIn",
|
||||
"parameters": {"channel": 0}
|
||||
"arguments": {"channel": 0}
|
||||
},
|
||||
"pmt1": {
|
||||
"module": "artiq.coredevice.rtio",
|
||||
"class": "RTIOIn",
|
||||
"parameters": {"channel": 1}
|
||||
"arguments": {"channel": 1}
|
||||
},
|
||||
|
||||
"ttl0": {
|
||||
"module": "artiq.coredevice.rtio",
|
||||
"class": "RTIOOut",
|
||||
"parameters": {"channel": 2}
|
||||
"arguments": {"channel": 2}
|
||||
},
|
||||
"ttl1": {
|
||||
"module": "artiq.coredevice.rtio",
|
||||
"class": "RTIOOut",
|
||||
"parameters": {"channel": 3}
|
||||
"arguments": {"channel": 3}
|
||||
},
|
||||
"ttl2": {
|
||||
"module": "artiq.coredevice.rtio",
|
||||
"class": "RTIOOut",
|
||||
"parameters": {"channel": 4}
|
||||
"arguments": {"channel": 4}
|
||||
},
|
||||
|
||||
"dds0": {
|
||||
"module": "artiq.coredevice.dds",
|
||||
"class": "DDS",
|
||||
"parameters": {"reg_channel": 0, "rtio_switch": 5}
|
||||
"arguments": {"reg_channel": 0, "rtio_switch": 5}
|
||||
},
|
||||
"dds1": {
|
||||
"module": "artiq.coredevice.dds",
|
||||
"class": "DDS",
|
||||
"parameters": {"reg_channel": 1, "rtio_switch": 6}
|
||||
"arguments": {"reg_channel": 1, "rtio_switch": 6}
|
||||
},
|
||||
"dds2": {
|
||||
"module": "artiq.coredevice.dds",
|
||||
"class": "DDS",
|
||||
"parameters": {"reg_channel": 2, "rtio_switch": 7}
|
||||
"arguments": {"reg_channel": 2, "rtio_switch": 7}
|
||||
},
|
||||
|
||||
"electrodes": {
|
||||
"module": "artiq.devices.pdq2",
|
||||
"class": "CompoundPDQ2",
|
||||
"parameters": {
|
||||
"arguments": {
|
||||
"ids": ["qc_q1_0", "qc_q1_1", "qc_q1_2", "qc_q1_3"],
|
||||
"rtio_trigger": 7,
|
||||
"rtio_frame": (2, 3, 4)
|
||||
|
@ -2,36 +2,36 @@
|
||||
"core": {
|
||||
"module": "artiq.sim.devices",
|
||||
"class": "Core",
|
||||
"parameters": {}
|
||||
"arguments": {}
|
||||
},
|
||||
"mains_sync": {
|
||||
"module": "artiq.sim.devices",
|
||||
"class": "Input",
|
||||
"parameters": {"name": "mains_sync"}
|
||||
"arguments": {"name": "mains_sync"}
|
||||
},
|
||||
"pmt": {
|
||||
"module": "artiq.sim.devices",
|
||||
"class": "Input",
|
||||
"parameters": {"name": "pmt"}
|
||||
"arguments": {"name": "pmt"}
|
||||
},
|
||||
"laser_cooling": {
|
||||
"module": "artiq.sim.devices",
|
||||
"class": "WaveOutput",
|
||||
"parameters": {"name": "laser_cooling"}
|
||||
"arguments": {"name": "laser_cooling"}
|
||||
},
|
||||
"spectroscopy": {
|
||||
"module": "artiq.sim.devices",
|
||||
"class": "WaveOutput",
|
||||
"parameters": {"name": "spectroscopy"}
|
||||
"arguments": {"name": "spectroscopy"}
|
||||
},
|
||||
"spectroscopy_b": {
|
||||
"module": "artiq.sim.devices",
|
||||
"class": "VoltageOutput",
|
||||
"parameters": {"name": "spectroscopy_b"}
|
||||
"arguments": {"name": "spectroscopy_b"}
|
||||
},
|
||||
"state_detection": {
|
||||
"module": "artiq.sim.devices",
|
||||
"class": "WaveOutput",
|
||||
"parameters": {"name": "state_detection"}
|
||||
"arguments": {"name": "state_detection"}
|
||||
},
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
from artiq import *
|
||||
|
||||
|
||||
class DDSTest(AutoContext):
|
||||
dds0 = Device("dds")
|
||||
dds1 = Device("dds")
|
||||
dds2 = Device("dds")
|
||||
led = Device("gpio_out")
|
||||
class DDSTest(AutoDB):
|
||||
class DBKeys:
|
||||
dds0 = Device()
|
||||
dds1 = Device()
|
||||
dds2 = Device()
|
||||
led = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
|
@ -3,7 +3,7 @@ import sys
|
||||
from artiq import *
|
||||
|
||||
|
||||
class Mandelbrot(AutoContext):
|
||||
class Mandelbrot(AutoDB):
|
||||
def col(self, i):
|
||||
sys.stdout.write(" .,-:;i+hHM$*#@ "[i])
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
from artiq import *
|
||||
|
||||
|
||||
class PhotonHistogram(AutoContext):
|
||||
bd = Device("dds")
|
||||
bdd = Device("dds")
|
||||
pmt = Device("ttl_in")
|
||||
class PhotonHistogram(AutoDB):
|
||||
class DBKeys:
|
||||
bd = Device()
|
||||
bdd = Device()
|
||||
pmt = Device()
|
||||
|
||||
nbins = Argument(100)
|
||||
repeats = Argument(100)
|
||||
nbins = Argument(100)
|
||||
repeats = Argument(100)
|
||||
|
||||
@kernel
|
||||
def cool_detect(self):
|
||||
|
@ -6,8 +6,9 @@ def print_min_period(p):
|
||||
print("Minimum square wave output period: {} ns".format(p))
|
||||
|
||||
|
||||
class PulsePerformance(AutoContext):
|
||||
ttl0 = Device("ttl_out")
|
||||
class PulsePerformance(AutoDB):
|
||||
class DBKeys:
|
||||
ttl0 = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
|
@ -9,9 +9,10 @@ def print_failed():
|
||||
print("Pulse was not received back")
|
||||
|
||||
|
||||
class RTIOSkew(AutoContext):
|
||||
pmt0 = Device("ttl_in")
|
||||
ttl0 = Device("ttl_out")
|
||||
class RTIOSkew(AutoDB):
|
||||
class DBKeys:
|
||||
pmt0 = Device()
|
||||
ttl0 = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
|
@ -1,11 +1,12 @@
|
||||
from artiq import *
|
||||
|
||||
|
||||
class SimpleSimulation(AutoContext):
|
||||
a = Device("dds")
|
||||
b = Device("dds")
|
||||
c = Device("dds")
|
||||
d = Device("dds")
|
||||
class SimpleSimulation(AutoDB):
|
||||
class DBKeys:
|
||||
a = Device()
|
||||
b = Device()
|
||||
c = Device()
|
||||
d = Device()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
@ -20,17 +21,16 @@ class SimpleSimulation(AutoContext):
|
||||
|
||||
def main():
|
||||
from artiq.sim import devices as sd
|
||||
from artiq.sim import time
|
||||
|
||||
core = sd.Core()
|
||||
exp = SimpleSimulation(
|
||||
core=sd.Core(),
|
||||
a=sd.WaveOutput(name="a"),
|
||||
b=sd.WaveOutput(name="b"),
|
||||
c=sd.WaveOutput(name="c"),
|
||||
d=sd.WaveOutput(name="d"),
|
||||
core=core,
|
||||
a=sd.WaveOutput(core=core, name="a"),
|
||||
b=sd.WaveOutput(core=core, name="b"),
|
||||
c=sd.WaveOutput(core=core, name="c"),
|
||||
d=sd.WaveOutput(core=core, name="d"),
|
||||
)
|
||||
exp.run()
|
||||
print(time.manager.format_timeline())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -10,17 +10,18 @@ transport_data = dict(
|
||||
# 4 devices, 3 board each, 3 dacs each
|
||||
)
|
||||
|
||||
class Transport(AutoContext):
|
||||
bd = Device("dds")
|
||||
bdd = Device("dds")
|
||||
pmt = Device("ttl_in")
|
||||
electrodes = Device("pdq")
|
||||
class Transport(AutoDB):
|
||||
class DBKeys:
|
||||
bd = Device()
|
||||
bdd = Device()
|
||||
pmt = Device()
|
||||
electrodes = Device()
|
||||
|
||||
wait_at_stop = Parameter(100*us)
|
||||
speed = Parameter(1.5)
|
||||
wait_at_stop = Parameter(100*us)
|
||||
speed = Parameter(1.5)
|
||||
|
||||
repeats = Argument(100)
|
||||
nbins = Argument(100)
|
||||
repeats = Argument(100)
|
||||
nbins = Argument(100)
|
||||
|
||||
def prepare(self, stop):
|
||||
t = transport_data["t"][:stop]*self.speed
|
||||
|
@ -113,19 +113,19 @@ def _action_cancel(remote, args):
|
||||
|
||||
|
||||
def _action_set_device(remote, args):
|
||||
remote.set_device(args.name, pyon.decode(args.description))
|
||||
remote.set(args.name, pyon.decode(args.description))
|
||||
|
||||
|
||||
def _action_del_device(remote, args):
|
||||
remote.del_device(args.name)
|
||||
remote.delete(args.name)
|
||||
|
||||
|
||||
def _action_set_parameter(remote, args):
|
||||
remote.set_parameter(args.name, pyon.decode(args.value))
|
||||
remote.set(args.name, pyon.decode(args.value))
|
||||
|
||||
|
||||
def _action_del_parameter(remote, args):
|
||||
remote.del_parameter(args.name)
|
||||
remote.delete(args.name)
|
||||
|
||||
|
||||
def _show_queue(queue):
|
||||
@ -233,10 +233,14 @@ def main():
|
||||
sys.exit(1)
|
||||
else:
|
||||
port = 8888 if args.port is None else args.port
|
||||
if action in ("submit", "cancel"):
|
||||
target_name = "master_schedule"
|
||||
else:
|
||||
target_name = "master_dpdb"
|
||||
target_name = {
|
||||
"submit": "master_schedule",
|
||||
"cancel": "master_schedule",
|
||||
"set_device": "master_ddb",
|
||||
"del_device": "master_ddb",
|
||||
"set_parameter": "master_pdb",
|
||||
"del_parameter": "master_pdb",
|
||||
}[action]
|
||||
remote = Client(args.server, port, target_name)
|
||||
try:
|
||||
globals()["_action_" + action](remote, args)
|
||||
|
@ -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, SimpleParameterHistory
|
||||
from artiq.management.db import FlatFileDB, SimpleHistory
|
||||
from artiq.management.scheduler import Scheduler
|
||||
|
||||
|
||||
@ -27,24 +27,26 @@ def _get_args():
|
||||
def main():
|
||||
args = _get_args()
|
||||
|
||||
dpdb = DeviceParamDB("ddb.pyon", "pdb.pyon")
|
||||
simplephist = SimpleParameterHistory(30)
|
||||
dpdb.parameter_hooks.append(simplephist)
|
||||
ddb = FlatFileDB("ddb.pyon")
|
||||
pdb = FlatFileDB("pdb.pyon")
|
||||
simplephist = SimpleHistory(30)
|
||||
pdb.hooks.append(simplephist)
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
atexit.register(lambda: loop.close())
|
||||
|
||||
scheduler = Scheduler({
|
||||
"req_device": dpdb.req_device,
|
||||
"req_parameter": dpdb.req_parameter,
|
||||
"set_parameter": dpdb.set_parameter
|
||||
"req_device": ddb.request,
|
||||
"req_parameter": pdb.request,
|
||||
"set_parameter": pdb.set
|
||||
})
|
||||
loop.run_until_complete(scheduler.start())
|
||||
atexit.register(lambda: loop.run_until_complete(scheduler.stop()))
|
||||
|
||||
server_control = Server({
|
||||
"master_schedule": scheduler,
|
||||
"master_dpdb": dpdb
|
||||
"master_ddb": ddb,
|
||||
"master_pdb": pdb
|
||||
})
|
||||
loop.run_until_complete(server_control.start(
|
||||
args.bind, args.port_control))
|
||||
@ -53,8 +55,8 @@ def main():
|
||||
server_notify = Publisher({
|
||||
"queue": scheduler.queue,
|
||||
"periodic": scheduler.periodic,
|
||||
"devices": dpdb.ddb,
|
||||
"parameters": dpdb.pdb,
|
||||
"devices": ddb.data,
|
||||
"parameters": pdb.data,
|
||||
"parameters_simplehist": simplephist.history
|
||||
})
|
||||
loop.run_until_complete(server_notify.start(
|
||||
|
@ -6,14 +6,15 @@ from inspect import isclass
|
||||
from operator import itemgetter
|
||||
|
||||
from artiq.management.file_import import file_import
|
||||
from artiq.language.context import *
|
||||
from artiq.language.db import *
|
||||
from artiq.management import pyon
|
||||
from artiq.management.dpdb import DeviceParamDB, DeviceParamSupplier
|
||||
from artiq.management.db import *
|
||||
|
||||
|
||||
class ELFRunner(AutoContext):
|
||||
comm = Device("comm")
|
||||
implicit_core = False
|
||||
class ELFRunner(AutoDB):
|
||||
class DBKeys:
|
||||
comm = Device()
|
||||
implicit_core = False
|
||||
|
||||
def run(self, filename):
|
||||
with open(filename, "rb") as f:
|
||||
@ -23,8 +24,14 @@ class ELFRunner(AutoContext):
|
||||
comm.serve(dict(), dict())
|
||||
|
||||
|
||||
class SimpleParamLogger:
|
||||
def set(self, timestamp, name, value):
|
||||
print("Parameter change: {} -> {}".format(name, value))
|
||||
|
||||
|
||||
def _get_args():
|
||||
parser = argparse.ArgumentParser(description="Local running tool")
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Local experiment running tool")
|
||||
|
||||
parser.add_argument("-d", "--ddb", default="ddb.pyon",
|
||||
help="device database file")
|
||||
@ -54,8 +61,11 @@ def _parse_arguments(arguments):
|
||||
def main():
|
||||
args = _get_args()
|
||||
|
||||
dpdb = DeviceParamDB(args.ddb, args.pdb)
|
||||
dps = DeviceParamSupplier(dpdb.req_device, dpdb.req_parameter)
|
||||
ddb = FlatFileDB(args.ddb)
|
||||
pdb = FlatFileDB(args.pdb)
|
||||
pdb.hooks.append(SimpleParamLogger())
|
||||
rdb = ResultDB()
|
||||
dbh = DBHub(ddb, pdb, rdb)
|
||||
try:
|
||||
if args.elf:
|
||||
if args.arguments:
|
||||
@ -69,8 +79,8 @@ def main():
|
||||
units = [(k, v) for k, v in module.__dict__.items()
|
||||
if k[0] != "_"
|
||||
and isclass(v)
|
||||
and issubclass(v, AutoContext)
|
||||
and v is not AutoContext]
|
||||
and issubclass(v, AutoDB)
|
||||
and v is not AutoDB]
|
||||
l = len(units)
|
||||
if l == 0:
|
||||
print("No units found in module")
|
||||
@ -92,15 +102,15 @@ def main():
|
||||
print("Failed to parse run arguments")
|
||||
sys.exit(1)
|
||||
|
||||
unit_inst = unit(dps, **arguments)
|
||||
unit_inst = unit(dbh, **arguments)
|
||||
unit_inst.run()
|
||||
|
||||
if dps.parameter_wb:
|
||||
print("Modified parameters:")
|
||||
for requester, name in dps.parameter_wb:
|
||||
print("{}: {}".format(name, getattr(requester, name)))
|
||||
if rdb.data:
|
||||
print("Results:")
|
||||
for k, v in rdb.data.items():
|
||||
print("{}: {}".format(k, v))
|
||||
finally:
|
||||
dps.close()
|
||||
dbh.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
Loading…
Reference in New Issue
Block a user