Device API reference

Device interface

NICOS models every device type as a Python class, and concrete devices as instances of these classes. There are also abstract classes that do not map to real devices, but serve to define the interface for derived classes.

For almost any action on a device, there are two methods:

  • The public method, e.g. device.start() is what the user or a user command will call. It typically is only present on the most abstract class that has that method and delegates most of the work to the implementation method, except for repetitive tasks that always have to be executed for that action.
  • The implementation method, prefixed with do, e.g. device.doStart() executes the action for the specific device. It is overwritten in specific subclasses.

When writing a device class for a specific device, only those implementation methods need to written that the device can support; the others will be skipped when called by the public methods.

Additional public methods can be implemented for specific devices, such as setPosition() for motors; the method/doMethod split should only be applied if the method implementation does something common for all possible implementations of doMethod. Public methods that can be called by the user should be decorated with usermethod.

Device classes

The base classes for all devices are contained in the module nicos.core.device and re-exported in nicos.core. There is a hierarchy of classes that correspond to varying levels of interaction that is possible with the device:

Device

class nicos.core.device.Device

This class is the most basic class to use for NICOS devices. It supports setting parameters from the configuration, and allows the user to get and set parameter values via automatically created properties.

Special attributes

parameters

Device objects must have a parameters class attribute that defines the available additional parameters. It must be a dictionary mapping parameter name to a Param object that describes the parameter’s properties (e.g. whether is it user-settable).

The parameters attribute does not need to contain the parameters of base classes again, they are automatically merged.

As an example, here is the parameter specification of the Device class itself:

parameters = {
    'description': Param('A description of the device', type=str,
                         settable=True),
    'lowlevel':    Param('Whether the device is not interesting to users',
                         type=bool, default=False),
    'loglevel':    Param('The logging level of the device', type=str,
                         default='info', settable=True),
}
parameter_overrides

While a subclass automatically inherits all parameters defined by base classes, it can make changes to parameters’ properties using the override mechanism. This dictionary, if present, must be a mapping of existing parameter names from base classes to Override objects that describe the desired changes to the parameter info.

For example, while usually the unit parameter is mandatory, it can be omitted for devices that can find out their unit automatically. This would be done like this:

parameter_overrides = {
    'unit': Override(mandatory=False),
}
attached_devices

Device classes can also have an attached_devices attribute that defines the class’s “attached devices” it needs to operate, such as a low-level communication device, or motor and encoders for an axis class. It maps the “internal name” (the device will be available as an attribute with that name) to the type of the device (which usually is an abstract type), and a string describing the purpose of the device. For example:

attached_devices = {
    'motor': (nicos.abstract.Motor, 'The motor to move'),
    'coder': (nicos.abstract.Coder, 'The coder for reading position'),
}

The actual attached devices for a specific instance (given in the instance’s configuration) are then type-checked against these types. As a special case, if the type is a list containing one type, such as [Readable], the corresponding entry in the configuration must be a list of zero to many instances of that type.

The attached_devices attribute does not need to contain the entries of base classes again, they are automatically merged.

Public methods

These methods are present on every Device. They do not need to be reimplemented in custom devices. Custom behavior is implemented in the various do...() methods described below.

init()

Initialize the object; this is called by the NICOS system when the device instance has been created.

This method first initializes all attached devices (creating them if necessary), then initializes parameters.

doPreinit()

This method, if present, is called before parameters are initialized (except for parameters that have the preinit property set to true).

This allows to initialize a hardware connection if it is necessary for the various doRead...() methods of other parameters that read the current parameter value from the hardware.

doInit()

This method, if present, is called after all parameters have been initialized. It is the correct place to set up additional attributes, or to perform initial (read-only!) communication with the hardware.

Note

Currently, doPreinit() and doInit() are called regardless of the current execution mode. This means that if one of these methods does hardware access, it needs to be done only if self._mode != 'simulation'.

shutdown()

Shut down the device. This method is called by the NICOS system when the device is destroyed, manually or because the current setup is unloaded.

doShutdown()

This method is called, if present, but not in simulation mode. It should perform cleanup, for example closing connections to hardware.

info()

Return “device information” as an iterable of tuples (category, name, value).

This “device information” is put into data files and should therefore include any parameters that will be essential to record the current status of the instrument.

The default implementation already collects all parameters whose category property is set.

doInfo()

This method can add more device information by returning it as a sequence of tuples.

version()

Return a list of versions for this device.

These are tuples (component, version) where a “component” can be the name of a Python module, or an external dependency (like a TACO server).

The base implementation already collects VCS revision information available from all Python modules involved in the class inheritance chain of the device class.

doVersion()

This method is called if present, and should return a list of (component, version) tuples that are added to the version info.

getPar(name)
setPar(name, value)

These are compatibility methods from the old NICOS system. Parameter access is now done via a property for every parameter.

Parameter access

For every parameter of a device class, a Python property is created on the object. This means that every parameter can be read as dev.param and written as dev.param = value. Setting the parameter at runtime is disallowed if the settable parameter property is false.

For every parameter, a read-related method can be defined (where “foo” is the parameter name):

doReadFoo()

For every parameter “foo”, a doReadFoo() method can be implemented. It will be called when the current parameter value is unknown, and cannot be determined from the cache. It should read the parameter value from an independent source, such as the hardware or the filesystem. If no such method exists, the parameter value will be the default value from the default parameter property, or if that is missing as well, a default value based on the type of the parameter (for number-like parameters, this is 0, for string-like parameters the empty string, etc).

For every parameter, two write-related methods can be defined (where “foo” is the parameter name):

doWriteFoo(value)

The doWriteFoo(value) method is called when the parameter is set by the user (or the program) in the current session, using dev.foo = value. This should write the new parameter value to the hardware, or write new parameter values of any dependent devices. It is only called when the current session is in master mode.

value is already type-checked against the parameter type.

This method can raise ConfigurationError if the new parameter value is invalid.

If this method returns something other than None, it is used as the new parameter value instead of the value given by the user.

doUpdateFoo(value)

The doUpdateFoo(value) method, in contrast, is called in every session when the parameter is changed by the master session, and the parameter update is communicated to all other sessions via the cache. This method should update internal state of the object that depends on the values of certain parameters. It may not access the hardware, set other parameters or do write operations on the filesystem.

doUpdateFoo is also called when an instance is created and its parameters are initialized.

This method can raise ConfigurationError if the new parameter value is invalid.

NB: The method names need to contain the parameter name with the first letter capitalized.

Parameters

name (string, optional)

The device name. This parameter should not be set in the configuration, it is set to the chosen device name automatically.

description (string, optional)

A more verbose device description. If not given, this parameter is set to be the same as the name parameter.

lowlevel (bool, optional)

Indicates whether the device is “low-level” and should neither be presented to users, nor created automatically. Default is false.

loglevel (string, optional)

The loglevel for output from the device. This must be set to one of the loglevel constants. Default is info.

Protected members

These protected members are of interest when implementing device classes:

_mode

The current execution mode. One of 'master', 'slave', 'maintenance' and 'simulation'.

_cache

The cache client to use for the device (see CacheClient), or None if no cache is available.

_adevs

A dictionary mapping attached device names (as given by the attached_devices dictionary) to the actual device instances.

_params

Cached dictionary of parameter values. Do not use this, rather access the parameters via their properties (self.parametername).

_setROParam(param, value)

Set an otherwise read-only parameter.

This is useful for parameters that change at runtime, but indirectly, such as “last filenumber”.

_cachelock_acquire(timeout=3)

Acquire an exclusive lock for using this device from the cache. This can be used if read access to the device needs to be locked (write access is locked anyway, since only one NICOS session can be the master session at a time).

_cachelock_release()

Release the exclusive cache lock for this device.

Always use like this:

self._cachelock_acquire()
try:
    ...  # do locked operations
finally:
    self._cachelock_release()

Readable

class nicos.core.device.Readable

This class inherits from Device and additionally supports this public interface and implementation methods:

read(maxage=None)

Read the (possibly cached) main value of the device.

doRead()

This method must be implemented to read the actual device value from the device. It is only called if the last cached value is out of date, or no cache is available.

status(maxage=None)

Return the (possibly cached) status of the device.

The status is a tuple of one of the integer constants defined in the nicos.core.status module, and textual extended info.

doStatus()

This method can be implemented to get actual device status from the device. It is only called if the last cached value is out of date, or no cache is available.

If no doStatus() is implemented, status() returns status.UNKNOWN.

reset()

Reset the device hardware. Returns the new status afterwards.

This operation is forbidden in slave mode, and a no-op for hardware devices in simulation mode.

doReset()

This method is called if implemented. Otherwise, reset() is a no-op.

poll(n=0)

Get status and value directly from the device and put both values into the cache. For continuous polling, n should increase by one with every call to poll.

doPoll(n)

If present, this method is called to perform additional polling, e.g. on parameters that can be changed from outside the NICOS system. The n parameter can be used to perform the polling less frequently than the polling of value and status.

_pollParam(name, with_ttl=0)

Read a parameter value from the hardware and put its value into the cache. This is intendend to be used from doPoll() methods, so that they don’t have to implement parameter polling themselves. If with_ttl is > 0, the cached value gets the TTL of the device value, determined by maxage, multiplied by with_ttl.

format(value)

Format a value from read() into a human-readable string.

The device unit is not included.

This is done using Python string formatting (the % operator) with the fmtstr parameter value as the format string.

valueInfo()

Describe the values read by this device.

Return a tuple of Value instances describing the values that read() returns.

This must be overridden by every Readable that returns more than one value in a list. For example, a slit that returns a width and height would define

def valueInfo(self):
    return (Value(self.name + '.width', unit=self.unit),
            Value(self.name + '.height', unit=self.unit))

By default, this returns a Value that indicates one return value with the proper unit and format string of the device.

info()

The default implementation of Device.info() for Readables adds the device main value and status.

history(name='value', fromtime=None, totime=None)

Return a history of the parameter name (can also be 'value' or 'status').

fromtime and totime can be UNIX timestamps to specify a limiting time window.

Default is to query the values of the last hour.

Parameters

fmtstr (string, optional)

A string format template that determines how format() formats the device value. The default is '%s'.

unit (string, mandatory)

The unit of the device value.

maxage (float, optional)

The maximum age of cached values from this device, in seconds. Default is 5 seconds.

pollinterval (float, optional)

The interval for polling this device from the NICOS poller. Default is 6 seconds.

Moveable

class nicos.core.device.Moveable

This class inherits from Readable and is the base class for all devices that can be moved to different positions (continuously or discretely).

start(pos)

Start movement of the device to a new position.

This method does not generally wait for completion of the movement, although individual devices can implement it that way if it is convenient. In that case, no doWait() should be implemented.

The validity of the given pos is checked by calling isAllowed() before doStart() is called.

This operation is forbidden in slave mode. In simulation mode, it sets an internal variable to the given position for hardware devices instead of calling doStart().

doStart(pos)

This method must be implemented and actually move the device to the new position.

valuetype

This attribute gives the type of the device value, as in the type parameter property. It is used for checking values in start().

isAllowed(pos)

Check if the given position can be moved to.

The return value is a tuple (valid, why). The first item is a boolean indicating if the position is valid, the second item is a string with the reason if it is invalid.

doIsAllowed(pos)

This method must be implemented to check the validity. If it does not exist, all positions are valid.

Note: to implement ordinary (min, max) limits, do not use this method but inherit your device from HasLimits. This takes care of all limit processing.

stop()

Stop any movement of the device.

This operation is forbidden in slave mode, and a no-op for hardware devices in simulation mode.

doStop()

This is called to actually stop the device. If not present, stop() will be a no-op.

The stop method will return the device status after stopping.

wait()

Wait until movement of device is completed.

Return current device value after waiting. This is a no-op for hardware devices in simulation mode.

doWait()

If present, this method is called to actually do the waiting. Otherwise, the device is assumed to change position instantly.

maw(target)

Move to target and wait for completion.

Equivalent to dev.start(target); return dev.wait().

fix(reason='')

Fix the device: don’t allow movement anymore.

This blocks start() or stop() when called on the device.

release()

Release the device, i.e. undo the effect of fix().

Parameters

target (any, read-only)

The last target position of a start() operation on the device.

fixed (str, not shown to the user)

This parameter is set by fix() and release() to indicate if the device has been fixed.

Measurable

class nicos.core.device.Measurable

This class inherits from Readable and is the base for all devices used for data acquisition (usually detectors).

Public methods

start(**preset)

Start measurement, with either the given preset or the standard preset.

This operation is forbidden in slave mode.

doStart(**preset)

This method must be present and is called to start the measurement.

stop()

Stop measurement now.

This operation is forbidden in slave mode.

doStop()

This method must be present and is called to actually stop the measurement.

The stop method will return the device status after stopping.

pause()

Pause the measurement, if possible.

Return True if paused successfully. This operation is forbidden in slave mode.

doPause()

If present, this is called to pause the measurement. Otherwise, False is returned to indicate that pausing is not possible.

resume()

Resume paused measurement.

Return True if resumed successfully. This operation is forbidden in slave mode.

doResume()

If present, this is called to resume the measurement. Otherwise, False is returned to indicate that resuming is not possible.

duringMeasureHook(cycle)

Hook called during measurement.

This can be overridden in subclasses to perform some periodic action while measuring. The hook is called by count for every detector in a loop with a delay of 0.025 seconds. The cycle argument is a number incremented with each call to the hook.

isCompleted()

Return true if measurement is complete.

doIsCompleted()

This method must be present and is called to determine if the measurement is completed.

wait()

Wait for completion of the measurement.

This is implemented by calling isCompleted() in a loop.

save()

Save the current measurement, if necessary.

Called by count for all detectors at the end of a counting.

doSave()

This method can be implemented if the detector needs to save data.

valueInfo()

Describe the values measured by this device.

Return a tuple of Value instances describing the values that read() returns.

This must be overridden by every Measurable that returns more than one value in a list. The default indicates a single return value with no additional info about the value type.

All Measurable.doRead() implementations must return tuples with values according to valueInfo().

Mixin classes

HasLimits

class nicos.core.device.HasLimits

This mixin can be inherited from device classes that are continuously moveable. It automatically adds two parameters, absolute and user limits, and overrides isAllowed() to check if the given position is within the limits before moving.

Note

In a base class list, HasLimits must come before Moveable, e.g.:

class MyDevice(HasLimits, Moveable): ...

Parameters

abslimits (number tuple, mandatory)

Absolute minimum and maximum values for the device to move to, as a tuple. This parameter cannot be set after creation of the device and must be given in the setup configuration.

userlimits (number tuple, optional)

Minimum and maximum value for the device to move to. This parameter can be set after creation, but not outside the abslimits.

HasOffset

class nicos.core.device.HasOffset

Mixin class for Readable or Moveable devices that want to provide an ‘offset’ parameter and that can be adjusted via the adjust() user command.

A class that provides an offset must inherit this mixin, and subtract or add self.offset in doRead() or doStart(), respectively.

Parameters

offset (number, optional)

The current offset of the device zero to the hardware zero.

The device position is hardware_position - offset.

HasPrecision

class nicos.core.device.HasPrecision

Mixin class for Readable or Moveable devices that want to provide a ‘precision’ parameter.

Parameters

precision (number, optional)

The precision of the device.

Table Of Contents

Previous topic

Session API reference

Next topic

Further NICOS API