Skip to content

Writing clients

Matthew Neeley edited this page Jul 8, 2015 · 10 revisions

Way of making requests

In the following examples we assume that a connection object has been constructed via

cxn = labrad.connect()

Direct interface with a server

my_server = cxn.my_server
result = my_server.foo(args)

Use a "packet" to send multiple requests at once

p = cxn.my_server.packet()
p.foo(args)
p.bar(args)
result = p.send() # This is a blocking call
answer_to_foo = result['foo']
answer_to_bar = result['bar']

result is a dictionary keyed by the name of each setting. If you invoke the same setting multiple times in a single packet the result is a list. Alternately, you can set your own keys for each request

p = cxn.my_server.packet()
p.foo(args, key='banana')
p.foo(args, key='orange')
result = p.send()
answer_foo1 = result['banana']
answer_foo2 = result['orange']

Send a request to a server without waiting for the answer

p = cxn.my_server.packet()
p.foo(args)
result_future = p.send(wait=False) # This is not a blocking call
<other code>
# Now we want to wait for the answer to our request.
# Once this call completes, we will be able to do
# result['foo'] to retrieve the result of our request.
result = result_future.wait()
print("result of foo is: ", result['foo'])

Making connections in scripts

Suppose we write a script like this

# myscript.py

cxn = labrad.connect()
result = cxn.myserver.do_something()
...
another_result = cxn.myserver.do_something_else()

If we run the script from a python interactive shell like this

>>> run myscript.py

then we make a new labrad connection every time we run the script. This will pile up lots of connections lead to problems. There are two alternatives, each of which is better.

Pass a connection into a function

Instead of making a runnable script we wrap up the code we want into a function

# mymodule.py

def my_function(cxn)
    cxn.myserver.do_something()
    ...
    result = cxn.myserver.do_something_else()
    return result

and then from the interactive shell we do this

>>> import mymodule
>>> cxn = labrad.connect()
>>> result = mymodule.my_function(cxn)

This way, you only ever make a single connection. For convenience the cxn = labrad.connect() call can be in a python startup script which runs once when you fire up your python shell.

Use context management

# myscript.py

with labrad.connect() as cxn:
    <code>

The with statement ensures that when the with block ends, the connection's close method is automatically run, even if there was an exception inside the block. This way, no matter how many times you run the script the connections always close by the time the script ends.

Contexts

Each labrad request happens within a specific context. Servers use that context to store specific information about the client such as their current working directory (datavault, registry) or the currently selected GPIB device (any GPIB device server). Each client gets its own default context, which is normally all you need. To get a new context (for instance to avoid trampling over the working directory, or for keeping pipelined requests to the qubit sequencer separate):

ctx = cxn.context()  # Returns a tuple like (0, 5)
p = cxn.my_server.packet(context=ctx)
p.foo(args)
p.send()

Signals

Labrad servers can send signals. Signals are asynchronous notifications from servers that do not come as a reply to a particular request. Each client must register to receive a particular signal in order for it to be delivered. Setting up a client to receive a notification is a two step process:

def signal_handler(message_ctx, data):
   print("Got signal in context %s with data: %s" % (message_ctx, data))

def set_listner(cxn, server, ctx=None):
    notification_ID = 4444
    cxn._backend.cxn.addListener(signal_handler, source=server.ID, context=ctx, ID=notification_ID)
    server.signal_name.notify_on_change(notification_ID, True, context=ctx) # True enables notification

The addListner call sets up the local client code so that when it receives a notification from a specific server with a specific ID and context to dispatch it to the registered function. The notify_on_change call sends a message to the server telling it that we want to receive notifications, and that they should be send to use with the ID and context specified. The ID (4444 here) serves much the same purpose as a server's setting ID. It is used to route each notification to the proper handler. It can be anything, but it must be unique: a client can't use the same notification ID for two messages.

Debugging tips

Next

Writing servers