mirror of https://github.com/m-labs/artiq.git
add example_artiq_device controller and doc
This commit is contained in:
parent
c259c4f46f
commit
c699f5e704
|
@ -0,0 +1 @@
|
|||
from .driver import ExampleARTIQDevice
|
|
@ -0,0 +1,215 @@
|
|||
import logging
|
||||
import ctypes
|
||||
import struct
|
||||
import numpy.random
|
||||
from artiq.language.units import dB, check_unit, Quantity
|
||||
|
||||
class ExampleARTIQDevice:
|
||||
"""Example code demonstrating how to write an device controller for ARTIQ.
|
||||
|
||||
What the class ought demonstrate was discussed
|
||||
on the ARTIQ mailing list Dec 2014. References to this discussion are in double parenthesis eg ((1a))
|
||||
https://ssl.serverraum.org/lists-archive/artiq/2014-December/000253.html
|
||||
"""
|
||||
self._timeout_config = 1.0 # timeout in seconds for device to respond upon configuration
|
||||
self._timeout_interaction = 0.1 # timeout in seconds for device to respond post configuration
|
||||
|
||||
def __init__(self, simulate_hw, serial_port):
|
||||
"""Initialization steps require to create an instance of the device interface.
|
||||
|
||||
Examples:
|
||||
* initiate communication with device hardware over serial port
|
||||
* locate on-disk configuration files
|
||||
* how should system respond if device can't be found? What's default timeout?
|
||||
|
||||
"""
|
||||
# TODO handle problem that /dev/ttyUSBx is not unique ((2g))
|
||||
|
||||
# default logging level is 30, controller or client can change this subsequently
|
||||
self.__setup_logging(logging_level=30)
|
||||
|
||||
# setup serial interface to device
|
||||
self.__setup_serial(serial_port)
|
||||
|
||||
def __del__(self):
|
||||
"""De-initialization steps required to safely shutdown an instance of the
|
||||
device interface go here.
|
||||
|
||||
Examples:
|
||||
* close serial port
|
||||
* close any open files
|
||||
"""
|
||||
def __setup_logging(self, logging_level):
|
||||
"""Do whatever is needed to configure ARTIQ logging.
|
||||
|
||||
For more on the python logging tool see https://docs.python.org/2/library/logging.html
|
||||
|
||||
:param int logging_level: to what degree are messages generated by this driver reported to the ARTIQ ecosystem?
|
||||
50 is CRITICAL, 40 is ERROR, 30 is WARNING (default), 20 is INFO, 10 is DEBUG
|
||||
:return None:
|
||||
"""
|
||||
logging_format_string = "%(asctime)-15s %(message)s"
|
||||
logging.basicConfig(format=logging_format_string)
|
||||
self.logger = logging.getLogger(self.__class__.__name__)
|
||||
self.logger.setLevel(logging_level)
|
||||
|
||||
# TODO: Should there be a default class from which devices inherit? Such a class could include
|
||||
# proper setup of things like the logger. This would also make it easier to add additional functionality
|
||||
# across the driver ecosystem without modifying the code of each driver individually.
|
||||
|
||||
# TODO: how is the logger connected to the client and/or GUI? Either or both might need to be notified
|
||||
# of errors. Nobody commented on this...
|
||||
|
||||
def set_logging_level(self, logging_level):
|
||||
"""Change logging level.
|
||||
|
||||
:param int logging_level: to what degree are messages generated by this driver reported to the ARTIQ ecosystem?
|
||||
50 is CRITICAL, 40 is ERROR, 30 is WARNING (default), 20 is INFO, 10 is DEBUG
|
||||
:return None:
|
||||
"""
|
||||
self.logger.setLevel(logging_level)
|
||||
|
||||
def __setup_connection_to_parameter_database(self):
|
||||
"""Do whatever is needed to communicate with the parameter database.
|
||||
|
||||
:return None:
|
||||
"""
|
||||
|
||||
def __setup_serial(self, serial_port):
|
||||
"""Do whatever is needed to communicate with a serial device.
|
||||
|
||||
TODO: For the serial ports adduser user dialout and serial.serial_for_url() is much simpler,
|
||||
more powerful, and also works under windows. RJ
|
||||
|
||||
:param str serial port: on Windows an integer in a string (e.g. "1"); on Linux a device path (e.g. "/dev/ttyUSB0")
|
||||
:return None:
|
||||
"""
|
||||
|
||||
def echo(self, s):
|
||||
"""Demonstration of a simple device subroutine that echoes back whatever string is passed to it.
|
||||
|
||||
:param str s: a string that will be echo'd back
|
||||
:return str: return s
|
||||
"""
|
||||
replys = "echo: {}".format(s)
|
||||
self.logger.info(replys)
|
||||
return s
|
||||
|
||||
def sphynx_documentation_example(self, arg1, arg2, arg3=True):
|
||||
"""Example of how to properly label arguments for automatic parsing by the ARTIQ documentation system.
|
||||
|
||||
ARTIQ uses the Sphinx documentation system to automatically generate help files for devices. The first line of
|
||||
the comment is a short synopsis of what the function does: "Example of how to...". Separated from the first
|
||||
line by an empty carriage return is a more verbose description of what the function does. Finally, the
|
||||
documentation section is concluded by a description of the function arguments and return value in a
|
||||
particular format. See "Info field lists" http://sphinx-doc.org/domains.html#info-field-lists
|
||||
|
||||
:param str arg1: arg1 is a string argument (no default value)
|
||||
:param int arg2: arg2 is an integer argument (no default value)
|
||||
:param bool arg3: arg3 is a boolean argument (true default value)
|
||||
:return int: the return value is an integer
|
||||
"""
|
||||
return 0
|
||||
|
||||
def example_using_logger(self):
|
||||
"""Shows how to use the logger.
|
||||
|
||||
The details on the python logger are here : https://docs.python.org/2/library/logging.html
|
||||
In the context of ARTIQ the logger is the mechanism by which a driver can communicate status information
|
||||
to the front-end Graphical User Interface or put critical errors into the experiment log.
|
||||
|
||||
:return None:
|
||||
"""
|
||||
self.logger.info("logs a message with level INFO")
|
||||
self.logger.warning("logs a message with level WARNING")
|
||||
self.logger.error("logs a message with level ERROR")
|
||||
self.logger.log("logs a message with level CRITICAL")
|
||||
|
||||
def example_using_quantity_class(self, qvar1, qvar2):
|
||||
"""Example of how to properly use write a function that takes arguments of the Quantity class.
|
||||
|
||||
ARTIQ includes a special class for passing around Quantities that have specific types or ranges. For example,
|
||||
a device number might only be an integer, a phase (in rounds) should lie between 0 and 1, and a
|
||||
frequency can't be negative. Other advanced Quantities might be arrays or python classes.
|
||||
|
||||
:param Quantity qvar1: is a ....
|
||||
:param Quantity qvar2: is a ....
|
||||
:return None:
|
||||
"""
|
||||
# TODO: please include some code showing how to use this
|
||||
# show how to raise an exception if the wrong Quantity is passed or a variable that is not of type
|
||||
# Quantity
|
||||
|
||||
def demo_exception_handling(self, myvar):
|
||||
"""Example code that tells the device to do something specific. And throw an exception if it goes bad.
|
||||
|
||||
:param int myvar: variable that modifies device behavior
|
||||
:return None:
|
||||
"""
|
||||
try:
|
||||
my_random_num = numpy.random.rand(1)[0]
|
||||
if myvar < 0:
|
||||
self.logger.error("argument must be greater than zero")
|
||||
elif my_random_num > myvar:
|
||||
# alert GUI that this has happened
|
||||
# raise an ARTIQ specific exception here
|
||||
self.logger.error("you're unlucky")
|
||||
else:
|
||||
r = myvar/0
|
||||
except ZeroDivisionError:
|
||||
# caught a divide by zero error; if it can be handled locally do that
|
||||
# if it can't be handeled locally throw it for another
|
||||
self.logger.error("divide by zero")
|
||||
raise
|
||||
except:
|
||||
self.logger.error("unhandled exception")
|
||||
raise
|
||||
# TODO: Is this right? I don't know how artiq handles exceptions.
|
||||
|
||||
def example_interface_with_c(self):
|
||||
"""(1) example of how to interface with some generic C code
|
||||
|
||||
Generic code in C with functions passing a representative sample of types
|
||||
e.g. (char[10], int[10], double[10], my_struct[10]). In a subfolder of example_artiq_device
|
||||
is the example C code along with a suitable makefile.
|
||||
|
||||
:return None:
|
||||
"""
|
||||
# TODO: implement this
|
||||
|
||||
def program_device_with_vector_argument(self, my_vec):
|
||||
"""get a vector to modify device behavior
|
||||
|
||||
This could be a vector describing the waveform to be generated by an ADC
|
||||
|
||||
:return None:
|
||||
"""
|
||||
# TODO: implement this
|
||||
|
||||
def get_from_parameter_database(self):
|
||||
"""get some parameters from the parameter database
|
||||
|
||||
Cases to consider:
|
||||
1) what if the requested parameter is not in the database
|
||||
2) parameter is a vector or class object
|
||||
|
||||
:return None:
|
||||
"""
|
||||
# TODO: implement this
|
||||
|
||||
def set_variable_to_parameter_database(self):
|
||||
"""update some parameters in the parameter database
|
||||
|
||||
Cases to consider:
|
||||
1) requested parameter is not in the database
|
||||
2) parameter is a vector or class object
|
||||
|
||||
:return None:
|
||||
"""
|
||||
# TODO: implement this
|
||||
def gpib_communication_example(self):
|
||||
"""per ((3a))
|
||||
|
||||
:return None:
|
||||
"""
|
||||
# TODO: implement this
|
|
@ -0,0 +1,2 @@
|
|||
# put code framework and example here for simulating a piece of hardware in the right way.
|
||||
# I don't know how to do this.
|
|
@ -0,0 +1,95 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from functools import partial
|
||||
from artiq.protocols.pc_rpc import Client
|
||||
|
||||
# Question: shouldn't this restricted_float() be part of the Quantity class?
|
||||
# Question: Shouldn't all the parameters passed to drivers be of the Quantity class?
|
||||
def restricted_float(val_min, val_max, x):
|
||||
"""do range checking on a variable
|
||||
"""
|
||||
x = float(x)
|
||||
if x < val_min or x > val_max:
|
||||
raise argparse.ArgumentTypeError(
|
||||
"{:f} not in range [{:f}, {:f}]".format(x, val_min, val_max))
|
||||
return x
|
||||
|
||||
def define_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="example_artiq_device_client",
|
||||
epilog="This is a m-labs.com ARTIQ "
|
||||
"client that serves as a template for interaction with ARTIQ devices. == "
|
||||
"The hardware interface is a serial port.")
|
||||
|
||||
# following are default arguments that should be common to any ARTIQ device client.
|
||||
parser.add_argument("--bind", default="::1",
|
||||
help="hostname or IP address to bind to (::1 is localhost)")
|
||||
parser.add_argument("--port", default=3254, type=int,
|
||||
help="TCP port to listen to 3254")
|
||||
parser.add_argument("--verbose", action="store_true",
|
||||
help="increase output verbosity")
|
||||
|
||||
# Following are command line options for interacting with a specific device.
|
||||
# Roughly, each member function of the driver Class, here Example_ARTIQ_Device
|
||||
# has its own entry below.
|
||||
subparsers = parser.add_subparsers(dest="subparser_name")
|
||||
|
||||
# Here, a python feature called a partial is used to check the parameter range
|
||||
# of some passed arguments.
|
||||
# https://docs.python.org/2/library/argparse.html#partial-parsing
|
||||
restricted_myvar = partial(restricted_float, 0.0, 1.0)
|
||||
parser_demo_exception_handling = subparsers.add_parser("demo_exception_handling",
|
||||
help="demonstration of exception handling")
|
||||
parser_demo_exception_handling.add_argument("myvar", type=restricted_myvar,
|
||||
help="a number in the range"
|
||||
"[0.0,1.0]")
|
||||
parser_demo_exception_handling.add_argument("--optional_argument", default=-1, type=int,
|
||||
choices=range(0, 4),
|
||||
help="an optional argument to pass to demo_exception_handling")
|
||||
|
||||
# All the other member functions in Example_ARTIQ_Device would be parameterized
|
||||
# in a similar fashion.
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def _get_args():
|
||||
p = define_parser()
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
args = _get_args()
|
||||
remote = Client(args.bind, args.port, "novatech409b")
|
||||
try:
|
||||
if args.verbose:
|
||||
print(args)
|
||||
if args.echo:
|
||||
r = remote.echo(args.echo[0])
|
||||
print(r)
|
||||
elif args.subparser_name:
|
||||
if args.subparser_name == "phase":
|
||||
remote.set_phase_all(args.p)
|
||||
elif args.subparser_name == "freq":
|
||||
if args.channel == -1:
|
||||
remote.set_freq_all_phase_continuous(args.f)
|
||||
else:
|
||||
remote.set_freq(args.channel, args.f)
|
||||
elif args.subparser_name == "sweep-freq":
|
||||
remote.freq_sweep_all_phase_continuous(
|
||||
args.f0, args.f1, args.t)
|
||||
elif args.subparser_name == "gain":
|
||||
if args.channel == -1:
|
||||
remote.output_scale_all(args.g)
|
||||
else:
|
||||
remote.output_scale(args.channel, args.g)
|
||||
elif args.subparser_name == "reset":
|
||||
remote.reset()
|
||||
elif args.args.subparser_eeprom == "save-to-eeprom":
|
||||
remote.save_state_to_eeprom()
|
||||
finally:
|
||||
remote.close_rpc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/python3
|
||||
# Copyright (c) 2014 Joe Britton, Sebastien Bourdeauducq
|
||||
|
||||
import argparse
|
||||
from artiq.protocols.pc_rpc import simple_server_loop
|
||||
import importlib
|
||||
import logging
|
||||
import artiq.devices.ExampleARTIQDevice
|
||||
importlib.reload(artiq.devices.ExampleARTIQDevice)
|
||||
|
||||
|
||||
def get_argparser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="example_artiq_device_controller",
|
||||
epilog="This is a sample m-labs.hk ARTIQ "
|
||||
"device controller.")
|
||||
parser.add_argument("--bind", default="::1",
|
||||
help="hostname or IP address to bind to;"
|
||||
"::1 is localhost")
|
||||
parser.add_argument("--port", default=3254, type=int,
|
||||
help="TCP port to listen to")
|
||||
parser.add_argument("--simulate_hw", action="store_true",
|
||||
help="simulate hardware so ARTIQ can be used"
|
||||
"outside the lab")
|
||||
parser.add_argument(
|
||||
"--serial_port",
|
||||
default="/dev/ttyUSB0", type=str,
|
||||
help="serial port: on Windows an integer (e.g. 1),"
|
||||
"on Linux a device path (e.g. \"/dev/ttyUSB0\")")
|
||||
parser.add_argument("--verbosity", type=int, default=1)
|
||||
parser.add_argument("--log", type=int, default=30,
|
||||
help="set log level by verbosity: 50 is CRITICAL, 40 is ERROR, 30 is WARNING, 20 is INFO, 10 is DEBUG")
|
||||
|
||||
# add additional commandline arguments here that might be needed to configure the device
|
||||
parser.add_argument("--myvar", type=int, default=0,
|
||||
help="example user-defined parameter")
|
||||
|
||||
return parser
|
||||
|
||||
def main():
|
||||
"""
|
||||
primary steps:
|
||||
1) Create an instance of the device driver class Example_ARTIQ_Device.
|
||||
2) Start driver event loop using simple_server_loop()
|
||||
"""
|
||||
|
||||
# get command line arguments using the standard python argparser library
|
||||
args = get_argparser().parse_args()
|
||||
|
||||
# start event loop
|
||||
simple_server_loop(
|
||||
{"example_artiq_device":
|
||||
artiq.devices.example_artiq_device.ExampleARTIQDevice(
|
||||
logging_level=args.verbosity,
|
||||
simulate_hw=args.simulate_hw,
|
||||
serial_port=args.port)},
|
||||
host=args.bind,
|
||||
port=args.port )
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,16 +1,52 @@
|
|||
Writing a driver
|
||||
================
|
||||
Writing an ARTIQ Device Driver
|
||||
==============================
|
||||
|
||||
These instructions cover writing a simple driver for a "slow" device, that uses the controller mechanism.
|
||||
These instructions cover writing a simple driver for a "slow" peripheral device.
|
||||
A device driver consists primarily of three parts.
|
||||
|
||||
The controller
|
||||
--------------
|
||||
1. driver.py :: This file is where the low-level implementation details of the driver go. Things like
|
||||
opening a serial connection to a device and passing it arguments. Choose a name for
|
||||
your driver with alphanumeric characters and underscore. For example,
|
||||
widget_233A.
|
||||
|
||||
A controller is a piece of software that receives commands from a client over the network (or the ``localhost`` interface), drives a device, and returns information about the device to the client. The mechanism used is remote procedure calls (RPCs) using :class:`artiq.protocols.pc_rpc`, which makes the network layers transparent for the driver's user.
|
||||
2. widget_233A_client.py :: This file provides a command line interface to the device. Its
|
||||
an easy way to interact with the device without using the ARTIQ GUI.
|
||||
|
||||
The controller we will develop is for a "device" that is very easy to work with: the console from which the controller is run. The operation that the driver will implement is writing a message to that console.
|
||||
3. widget_233A_controller.py :: The controller is a piece of software that receives
|
||||
commands destined for driver.py from a client. The client could be my_driver_client.py
|
||||
or it could be the ARTIQ GUI on another machine. The controller runs continuously in the
|
||||
background waiting for requests from clients.
|
||||
|
||||
For using RPC, the functions that a driver provides must be the methods of a single object. We will thus define a class that provides our message-printing method: ::
|
||||
driver.py
|
||||
---------
|
||||
|
||||
The low-level implementation of the driver goes here. To create a driver.py for your device
|
||||
perform the following steps. By convention the name of this file is driver.py (and nothing
|
||||
else).
|
||||
|
||||
1. Copy everything in artiq/devices/example_artiq_device to a new directory:
|
||||
artiq/devices/widget_233A/.
|
||||
|
||||
3. Following the template in artiq/devices/widget_233A/driver.py fill out the details of your driver. Note that the
|
||||
driver functionality is encapsulated in a class, e.g. Widget233A.
|
||||
|
||||
4. Edit artiq/devices/widget_233A/__init__.py to reflect the path to your class.
|
||||
|
||||
Controller Overview
|
||||
-------------------
|
||||
|
||||
A controller is a piece of software that receives commands from a client over the network
|
||||
(or the ``localhost`` interface), drives a device, and returns information about the device
|
||||
to the client. The mechanism used is remote procedure calls (RPCs)
|
||||
using :class:`artiq.protocols.pc_rpc`, which makes the network layers transparent
|
||||
for the driver's user.
|
||||
|
||||
The controller we will develop is for a "device" that is very easy to work with: the
|
||||
console from which the controller is run. The operation that the driver will implement
|
||||
is writing a message to that console.
|
||||
|
||||
For using RPC, the functions that a driver provides must be the methods of a single
|
||||
object. We will thus define a class that provides our message-printing method: ::
|
||||
|
||||
class Hello:
|
||||
def message(self, msg):
|
||||
|
@ -34,13 +70,13 @@ The parameters ``::1`` and ``3249`` are respectively the address to bind the ser
|
|||
|
||||
#!/usr/bin/env python3
|
||||
|
||||
at the beginning of the file, save it to ``hello_controller.py`` and set its execution permissions: ::
|
||||
at the beginning of the file, save it to ``controller.py`` and set its execution permissions: ::
|
||||
|
||||
$ chmod 755 hello_controller.py
|
||||
$ chmod 755 controller.py
|
||||
|
||||
Run it as: ::
|
||||
|
||||
$ ./hello_controller.py
|
||||
$ ./controller.py
|
||||
|
||||
and verify that you can connect to the TCP port: ::
|
||||
|
||||
|
@ -56,12 +92,23 @@ Also verify that a target (service) named "hello" (as passed in the first argume
|
|||
$ artiq_ctlid.py ::1 3249
|
||||
Target(s): hello
|
||||
|
||||
The client
|
||||
----------
|
||||
|
||||
Controller clients are small command-line utilities that expose certain functionalities of the drivers. They are optional, and not used very often - typically for debugging and testing.
|
||||
widget_233A_controller.py
|
||||
-------------------------
|
||||
Use the following steps to create a controller that works with the ARTIQ ecosystem.
|
||||
1. Copy artiq/frontend/example_artiq_device_controller.py to
|
||||
artiq/frontend/widget_233A_controller.py.
|
||||
2. With the Controller Overview in mind follow the instructions in the example code
|
||||
to flesh out the behavior of a controller for your device.
|
||||
|
||||
Create a ``hello_client.py`` file with the following contents: ::
|
||||
Client Overview
|
||||
---------------
|
||||
|
||||
Controller clients are small command-line utilities that expose certain
|
||||
functionalities of the drivers. It's an easy way to interact with the device
|
||||
without using the ARTIQ GUI.
|
||||
|
||||
Create a ``client.py`` file with the following contents: ::
|
||||
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
@ -78,21 +125,30 @@ Create a ``hello_client.py`` file with the following contents: ::
|
|||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Run it as before, while the controller is running. You should see the message appearing on the controller's terminal: ::
|
||||
Run it as before, while the controller is running. You should see the message appearing
|
||||
on the controller's terminal: ::
|
||||
|
||||
$ ./hello_controller.py
|
||||
$ ./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.db.AutoDB` 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.
|
||||
: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.
|
||||
|
||||
Command-line arguments
|
||||
----------------------
|
||||
The driver's controller should be saved in
|
||||
~/artiq-dev/artiq/artiq/devices/driver_name/client.py.
|
||||
|
||||
Use the Python ``argparse`` module to make the bind address and port configurable on the controller, and the server address, port and message configurable on the client.
|
||||
Use the Python ``argparse`` module to make the bind address and port configurable on the controller, and the server
|
||||
address, port and message configurable on the client.
|
||||
|
||||
We suggest naming the controller parameters ``--bind`` and ``--port`` so that those parameters stay consistent across controller, and use ``-s/--server`` and ``--port`` on the client.
|
||||
We suggest naming the controller parameters ``--bind`` and ``--port`` so that those parameters stay consistent across
|
||||
controller, and use ``-s/--server`` and ``--port`` on the client.
|
||||
|
||||
The controller's code would contain something similar to this: ::
|
||||
|
||||
|
@ -146,6 +202,45 @@ The program below exemplifies how to use logging: ::
|
|||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
The driver's server should be saved in ~/artiq-dev/artiq/artiq/devices/driver_name/server.py.
|
||||
|
||||
widget_233A_client.py
|
||||
---------------------
|
||||
Use the following steps to create a client that works with the ARTIQ ecosystem.
|
||||
1. Copy artiq/frontend/example_artiq_device_client.py to
|
||||
artiq/frontend/widget_233A_client.py.
|
||||
2. With the Client Overview in mind follow the instructions in the example code
|
||||
to flesh out the behavior of a client for your device.
|
||||
|
||||
setup.py
|
||||
--------
|
||||
Edit the entry_points section of setup.py to point to your client and controller following
|
||||
the model set by example_artiq_device_client and example_artiq_device_controller.
|
||||
|
||||
Device Manager Udev Rules
|
||||
-------------------------
|
||||
On Linux systems udev is the device manager. It manages device nodes in /dev. ARTIQ drivers
|
||||
often interface with instruments that have USB-Serial interfaces. Normally these devices
|
||||
are assigned device nodes on an ad hock basis by the kernel (e.g. /dev/ttyUSB3); the
|
||||
assignment is not deterministic. It is convenient
|
||||
for device node names to be consistent for each device (e.g. /dev/artiq_ppro). This can be accomplished
|
||||
by creating a udev rule (https://wiki.archlinux.org/index.php/udev).
|
||||
|
||||
* Get a list of device attributes. ::
|
||||
|
||||
$udevadm info -a /dev/ttyUSB2
|
||||
|
||||
*force reloading of udev
|
||||
$ sudo udevadm control --reload; udevadm trigger
|
||||
|
||||
$ sudo vim /etc/udev/rules.d/30-usb-papilio-pro.rules
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6010", ATTRS{manufacturer}=="FTDI",
|
||||
ATTRS{manufacturer}=="FTDI", GROUP="plugdev", SYMLINK+="artiq_ppro"
|
||||
|
||||
* To confirm that everything is wroking, force reloading of udev. Then disconnect and reconnect
|
||||
the serial device. ::
|
||||
|
||||
$ sudo udevadm control --reload; udevadm trigger
|
||||
|
||||
General guidelines
|
||||
------------------
|
||||
|
|
2
setup.py
2
setup.py
|
@ -37,6 +37,8 @@ setup(
|
|||
"lda_controller=artiq.frontend.lda_controller:main",
|
||||
"pdq2_client=artiq.frontend.pdq2_client:main",
|
||||
"pdq2_controller=artiq.frontend.pdq2_controller:main",
|
||||
"example_artiq_device_client=artiq.frontend.example_artiq_device_client:main",
|
||||
"example_artiq_device_controller=example_artiq_device_controller:main"
|
||||
],
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue