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: 4d0e5db955c8
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: d5a3f3ef51d2
Choose a head ref
  • 3 commits
  • 5 files changed
  • 1 contributor

Commits on Oct 27, 2014

  1. Copy the full SHA
    1e5b05d View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    Mic92 Jörg Thalheim
    Copy the full SHA
    2946fa5 View commit details
  3. Verified

    This commit was signed with the committer’s verified signature.
    Mic92 Jörg Thalheim
    Copy the full SHA
    d5a3f3e View commit details
Showing with 127 additions and 19 deletions.
  1. +2 −2 artiq/devices/pdq2/pdq2-client
  2. +6 −17 artiq/devices/pdq2/pdq2-controller
  3. +21 −0 artiq/management/pc_rpc.py
  4. +1 −0 doc/manual/index.rst
  5. +97 −0 doc/manual/writing_a_driver.rst
4 changes: 2 additions & 2 deletions artiq/devices/pdq2/pdq2-client
Original file line number Diff line number Diff line change
@@ -16,9 +16,9 @@ def _get_args():
Evaluates times and voltages, interpolates and uploads
them to the controller.""")
parser.add_argument("-s", "--server", default="::1",
help="hostname or IP of the master to connect to")
help="hostname or IP of the controller to connect to")
parser.add_argument("--port", default=8889, type=int,
help="TCP port to use to connect to the master")
help="TCP port to use to connect to the controller")
parser.add_argument("-c", "--channel", default=0, type=int,
help="channel: 3*board_num+dac_num [%(default)s]")
parser.add_argument("-f", "--frame", default=0, type=int,
23 changes: 6 additions & 17 deletions artiq/devices/pdq2/pdq2-controller
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ import asyncio
from scipy import interpolate
import numpy as np

from artiq.management.pc_rpc import Server
from artiq.management.pc_rpc import simple_server_loop


logger = logging.getLogger("Pdq2")
@@ -332,12 +332,10 @@ class Pdq2:

def _get_args():
parser = argparse.ArgumentParser(description="PDQ2 controller")
parser.add_argument(
"--bind", default="::1",
help="hostname or IP address to bind to")
parser.add_argument(
"--port", default=8889, type=int,
help="TCP port to listen to")
parser.add_argument("--bind", default="::1",
help="hostname or IP address to bind to")
parser.add_argument("-p", "--port", default=8889, type=int,
help="TCP port to listen to")
parser.add_argument(
"-s", "--serial", default=None,
help="device (FT245R) serial string [first]")
@@ -357,16 +355,7 @@ def main():

dev = Pdq2(serial=args.serial)
try:
loop = asyncio.get_event_loop()
try:
server = Server(dev)
loop.run_until_complete(server.start(args.bind, args.port))
try:
loop.run_forever()
finally:
loop.run_until_complete(server.stop())
finally:
loop.close()
simple_server_loop(dev, args.bind, args.port)
finally:
dev.close()

21 changes: 21 additions & 0 deletions artiq/management/pc_rpc.py
Original file line number Diff line number Diff line change
@@ -158,3 +158,24 @@ def _handle_connection_task(self, reader, writer):
writer.write(line.encode())
finally:
writer.close()


def simple_server_loop(target, host, port):
"""Runs a server until an exception is raised (e.g. the user hits Ctrl-C).
:param target: Object providing the RPC methods to be exposed to the
client.
:param host: Bind address of the server.
:param port: TCP port to bind to.
"""
loop = asyncio.get_event_loop()
try:
server = Server(target)
loop.run_until_complete(server.start(host, port))
try:
loop.run_forever()
finally:
loop.run_until_complete(server.stop())
finally:
loop.close()
1 change: 1 addition & 0 deletions doc/manual/index.rst
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ Contents:

installing
getting_started
writing_a_driver
core_language_reference
core_drivers_reference
management_reference
97 changes: 97 additions & 0 deletions doc/manual/writing_a_driver.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
Writing a driver
================

These instructions cover writing a simple driver for a "slow" device, that uses the controller paradigm.

The controller
--------------

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.management.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):
print("message: " + msg)

To turn it into a server, we use :class:`artiq.management.pc_rpc`. Import the function we will use: ::

from artiq.management.pc_rpc import simple_server_loop

and add a ``main`` function that is run when the program is executed: ::

def main():
simple_server_loop(Hello(), "::1", 7777)

if __name__ == "__main__":
main()

The parameters ``::1`` and 7777 are respectively the address to bind the server to (IPv6 localhost) and the TCP port to use. Then add a line: ::

#!/usr/bin/env python3

at the beginning of the file, save it to ``hello-controller`` and set its execution permissions: ::

$ chmod 755 hello-controller

Run it as: ::

$ ./hello-controller

and verify that you can connect to the TCP port: ::

$ telnet ::1 7777
Trying ::1...
Connected to ::1.
Escape character is '^]'.

:tip: Use the key combination Ctrl-AltGr-9 to get the ``telnet>`` prompt, and enter ``close`` to quit Telnet. Quit the controller with Ctrl-C.

The client
----------

Create a ``hello-client`` file with the following contents: ::

#!/usr/bin/env python3

from artiq.management.pc_rpc import Client


def main():
remote = Client("::1", 7777)
try:
remote.message("Hello World!")
finally:
remote.close_rpc()

if __name__ == "__main__":
main()

Run it as before, while the controller is running. You should see the message appearing on the controller's terminal: ::

$ ./hello-controller
message: Hello World!


Command-line arguments
----------------------

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.

The controller's code would contain something similar to this: ::

def _get_args():
parser = argparse.ArgumentParser(description="Hello world controller")
parser.add_argument("--bind", default="::1",
help="hostname or IP address to bind to")
parser.add_argument("--port", default=7777, type=int,
help="TCP port to listen to")
return parser.parse_args()

def main():
args = _get_args()
simple_server_loop(Hello(), args.bind, args.port)