Scan Client

This example shows how to connect to the scan server, submit a scan, and monitor its progress using the basic API, without using site-specific settings or abstractions like the table-based scan.

Example

import time
from scan import *
  
client = ScanClient('localhost')
print(client)

print(client.serverInfo())

# Assemble commands for a scan
# Much more on that later...
cmds = [ Comment('Hello'), Set('motor_x', 10) ]

# Optionally, request a simulation that shows
# how 'Include' and 'Loop' commands get expanded.
info = client.simulate(cmds)
print(info['simulation'])

# Submit scan for execution
id = client.submit(cmds, 'My First Scan')
print(id)

# Fetch information about scan
info = client.scanInfo(id)
print(info)

# Could poll scanInfo until info.isDone().
# Shortcut:
info = client.waitUntilDone(id)
print(info)

# A submitted scan can be paused..
id = client.submit([ Delay(5) ], 'Not sure about this one')

client.pause(id)
print(client.scanInfo(id))

client.resume(id)
print(client.scanInfo(id))

# .. or even aborted  & deleted
client.abort(id)
print(client.scanInfo(id))

print("Before deleting scan %d:" % id)
for info in client.scanInfos():
    print(info)
client.delete(id)
print("After deleting scan %d:" % id)
for info in client.scanInfos():
    print(info)

# To a limited extend, it is possible to change a running scan:
# Parameters of commands that have not been executed can be adjusted
id = client.submit([ Delay(5), Set('motor_x', 10) ], 'Changing...')
client.pause(id)
# Want to set 'motor_x' to 5 instead of 10
client.patch(id, 1, 'value', 5)
client.resume(id)
client.abort(id)

try:
    client.waitUntilDone(id)
except Exception as e:
    print("Waiting for an aborted scan will result in an exception: ", e)

try:
    # This scan will time out
    id = client.submit( [ Wait("motor_x", 60, timeout=1)], "Timeout Test")
    client.waitUntilDone(id)
except Exception as e:
    print("Waiting for a failed scan will result in an exception: ", e)

# Log data during scan
cmds = [ Loop('motor_x', 1, 10, 1,
              [ 
                  Set('neutrons', 0),
                  Loop('motor_y', 1, 3, 1,
                       [
                           Delay(1),
                           Log('motor_y')
                       ]),
                  Log('motor_x', 'neutrons')
              ])
       ]
id = client.submit(cmds, 'Data Demo')
info = client.waitUntilDone(id)
print("Number of log calls: %d" % client.lastSerial(id))

# Fetch data
data = client.getData(id)
 
# Create table for motor_x and neutrons
table = createTable(data, 'motor_x', 'neutrons')
print("Positions: ", table[0])
print("Counts   : ", table[1])
for (pos, count) in zip(*table):
    print("Counts at motor position %g: %g" % (pos, count))
    
# Could plot this with numpy/scipy:  plot(table[0], table[1]) etc.

# Remove information for all completed scans
client.clear()

API

class scan.client.scanclient.ScanClient(host='localhost', port=4810)

Client interface to the scan server

Parameters
  • host – The IP address or name of scan server host.

  • port – The TCP port of the scan server.

Example:

>>> client = ScanClient('localhost')
abort(scanID=- 1)

Abort a running or paused scan

Parameters

scanID – ID of scan or -1 to abort current scan

Using PUT {BaseURL}/scan/{id}/abort

Example:

>>> id = client.submit(commands)
>>> client.abort(id)
clear()

Remove all completed scans.

Using DELETE {BaseURL}/scans/completed

Usage:

>>> client.clear()
delete(scanID)

Remove a completed scan.

Using DELETE {BaseURL}/scan/{id}

Parameters

scanID – The id of scan you want to delete.

Example:

>>> id = client.submit(commands)
>>> client.abort(id)
>>> client.delete(id)
getData(scanID)

Fetch logged data of a scan

Parameters

scanID – ID of scan

Returns

Data dictionary

Example:
>>> data = client.getData(id)
>>> print data

Format of the data:

{ 'device1': {'id': [0, 1, 2, 3, 4 ],
              'value': [2.0, 3.0, 4.0, 2.0, 4.0],
              'time': [1427913270352, 1427913270470, 1427913270528, 1427913270596, 1427913270695]
             },
  'device2': {'id': [0, 3, 6, 9, 12],
              'value': [1.0, 2.0, 3.0, 4.0, 5.0],
              'time': [1427913270351, 1427913270595, 1427913270795, 1427913271076, 1427913271393]
             }
}

The data dictionary has one entry per logged device. Its value is again a dictionary with entries

id:

Sample IDs, starting from 0

value:

Sample values

time:

Times in Posix milliseconds

lastSerial(scanID)

Get the last log data serial.

Obtains the serial ID of the last logged sample of a scan. Allows clients which monitor the progress of a scan to poll for changes in the logged data without always having to pull the complete data log.

Parameters

scanID – The ID of scan for which to fetch information.

Returns

Id of last logged sample of the scan.

Example:

>>> last_log_fetched = -1
>>> while not client.scanInfo(id).isDone:
>>>     last_logged = client.lastSerial(id)
>>>     if last_log_fetched != last_logged:
>>>        # .. fetch logged data, because it has changed..
>>>        last_log_fetched = last_logged
>>>     time.sleep(10
patch(scanID, address, property, value)

Update scan on server.

This can be used to update parameters of an existing command in an existing scan on the server.

In case the command had already been executed, the change has no effect.

Using PUT {BaseURL}/scan/{id}/patch

Parameters
  • scanID – The id of scan you want to update.

  • address – Address of the command to update. Counted within the scan starting at 0.

  • property – The property of the command to update.

  • value – The new value for that property.

Example:

>>> id = client.submit([ Delay(5), Set('motor_x', 10) ], 'Changing...')
>>> client.pause(id)
>>> # Want to set 'motor_x' to 5 instead of 10
>>> client.patch(id, 1, 'value', 5)
>>> client.resume(id)
pause(scanID=- 1)

Pause a running scan

Parameters

scanID – ID of scan or -1 to pause current scan

Using PUT {BaseURL}/scan/{id}/pause

Example:

>>> id = client.submit(commands)
>>> client.pause(id)
resume(scanID=- 1)

Resume a paused scan

Parameters

scanID – ID of scan or -1 to resume current scan

Using PUT {BaseURL}/scan/{id}/resume

Example:

>>> id = client.submit(commands)
>>> client.pause(id)
>>> client.resume(id)
scanCmds(scanID)

Get the commands of scan.

Reads scan commands from scan server. Only possible for scans that are still available on the server, not for older scans that only have logged data but no command detail.

Parameters

scanID: – The ID of scan for which to fetch information.

Returns

Scan commands in XML format of a scan.

Example:

>>> client = ScanClient()
>>> scanid = client.submit(...someCMDs...)
>>> # Submit it again:
>>> client.submit(client.scanCmds(scanid))
scanDevices(scanID=- 1)

Get list of devices used by scan.

Parameters

scanID – The ID of scan that is still held in scan server, -1 to fetch default devices.

Provides a list of devices used by a scan. For a running scan, this includes devices accessed in the pre- and post-scan. Also includes devices configured with alias names, but not necessarily used by the scan.

Returns

XML with info about devices.

scanInfo(scanID, timeout=10)

Get information for a scan

Using GET {BaseURL}/scan/{id}

Parameters
  • scanID – The ID of scan for which to fetch information.

  • timeout – Throws exception when no reply within that time in seconds

Returns

ScanInfo

Example:

>>> client = ScanClient()
>>> print client.scanInfo(42)
scanInfos(timeout=20)

Get information of all scans

Using GET {BaseURL}/scans

Parameters

timeout – Throws exception when no reply within that time in seconds

Returns

List of ScanInfo

Example:

>>> infos = client.scanInfos()
>>> print [ str(info) for info in infos ]
serverInfo()

Get scan server information

Provides version number, configuration, … of current server

Using GET {BaseURL}/server/info

Returns

XML with server info

Usage:

>>> client = ScanClient()
>>> print client.serverInfo()
simulate(cmds)

Submit scan to scan server for simulation

Parameters

cmds – List of commands, CommandSequence or text with raw XML format.

Returns

Simulation result as dictionary { ‘simulation’: “Printable text”, ‘seconds’: 193.0 }

Example:

>>> result = client.simulate([ Set('x', 5), Delay(2) ])
>>> print result['simulation']
submit(cmds, name='UnNamed', queue=True, timeout=0, deadline=None, pre_post=True)

Submit scan to scan server for execution

Parameters
  • cmds – List of commands, CommandSequence or text with raw XML format.

  • name – Name of scan

  • queue – Submit to scan server queue, or execute as soon as possible?

  • timeout – Timeout in seconds after which scan will self-abort

  • deadline – Execution deadline in “yyyy-MM-dd HH:mm:ss” format when scan will self-abort

  • pre_post – Execute pre- and post-scan commands (default: True)

Returns

ID of submitted scan

By default, a submitted scan will be queued. One scan is then executed at a time, in order of submission. Each scan is allowed to run until all its commands complete.

The ‘queue’ parameter allows submitting scans for immediate execution, i.e. in parallel to the queued commands.

Either the ‘timeout’ or the ‘deadline’ might be used to limit the execution time.

Examples:

>>> cmds = [ Comment('Hello'), Set('x', 10) ]
>>> id = client.submit(cmds, "My First Scan")
>>> cmds = CommandSequence(Comment('Hello'))
>>> cmds.append(Set('x', 10))
>>> id = client.submit(cmds, "My Second Scan")
>>> cmds = CommandSequence(Delay(600))
>>> id = client.submit(cmds, "Timeout", timeout=10)
waitUntilDone(scanID)

Wait until scan finishes.

On return, the scan has finished successfully. Can also be used for an older scan that has logged data, whereupon this call will return immediately.

In case the scan failed or was aborted, an exception is raised.

If scan information is not available (timeout while requesting it), keep checking.

Parameters

scanID – ID of scan on which to wait

Returns

Scan info

Raises

Exception – If scan was aborted or failed.

class scan.client.scaninfo.ScanInfo(xml)

Information about a scan

Parameters

xml – XML element for a scan info

createdDatetime()
Returns

datetime when scan was created

isDone()
Returns

True if scan has completed, successful or not

percentage()
Returns

Percent of work done, 0…100