Using Devices
The station exposes devices, each of which is a remote software object that manages part of the station. Each device has the following properties:
It has a state,
Many devices manage and represent hardware in the station,
It exposes read-only attributes, that expose values from within the device or from the hardware it represents,
It exposes read-write attributes, that allow controlling the functionality of the device, or the hardware it represents,
It exposes properties, which are fixed configuration parameters (such as port numbers and timeouts),
It exposes commands, that request the execution of a procedure in the device or in the hardware it manages.
The devices are accessed remotely using DeviceProxy
objects. See Monitoring & Control on how to do this.
States
The state of a device is then queried with device.state()
. Each device can be in one of the following states:
DevState.OFF
: The device is not operating,DevState.INIT
: The device is being initialised,DevState.STANDBY
: The device is initialised and ready to be configured further,DevState.ON
: The device is operational,DevState.ALARM
: The device is operational, but one or more attributes are in alarm,DevState.FAULT
: The device is malfunctioning. Functionality cannot be counted on,DevState.DISABLE
: The device is not operating because its hardware has been shut down.The
device.state()
function can throw an error, if the device cannot be reached at all. For example, because it’s docker container is not running. See the Docker device on how to start it.
Each device provides the following commands to change the state:
- boot():
Turn on the device, and initialise the hardware. Moves from
OFF
toON
.- warm_boot():
Turn on the device, but do not change the hardware. Moves from
OFF
toON
.- disable_hardware():
Shut down the hardware related to the device. Moves from
STANDBY
,ON
orALARM
toDISABLE
- off():
Turn the device
OFF
from any state.
The following procedure is a good way to bring a device to ON
from any state:
def force_start(device):
if device.state() == DevState.FAULT:
device.off()
if device.state() == DevState.OFF:
device.boot()
return device.state()
Hint
If a command gives you a timeout, the command will still be running until it finishes. You just won’t know when it does or its result. In order to increase the timeout, use device.set_timeout_millis(timeout * 1000)
.
FAULT
If a device enters the FAULT
state, it means an error occurred that is fundamental to the operation of the software device. For example, the connection
to the hardware was lost. To see the error reason, use
- status():
The verbose status of the device, f.e. the reason why the device went to
FAULT
.
Interaction with the device in the FAULT
state is undefined, and attributes cannot be read or written. The device needs to be reinitialised, which
typically involves the following sequence of commands:
# turn the device off completely first.
device.off()
# turn on the device and fully reinitialise it
# alternatively, device.warm_boot() can be used,
# in which case no hardware is reinitialised.
device.boot()
Of course, the device could go into FAULT
again, even during the boot()
command, for example because the hardware it manages is unreachable. To debug the fault condition, check the Logs of the device in question.
Initialise hardware
Most devices provide the following commands, in order to configure the hardware with base settings. Note that these are automatically called during boot()
, in this order:
- initialise():
Initialise the device (connect to the hardware). Moves from
OFF
toSTANDBY
.- power_hardware_on():
For devices that control hardware, this command turns on power to it.
- power_hardware_off():
For devices that control hardware, this command turns off power to it.
- set_defaults():
Upload default attribute settings from the TangoDB to the hardware.
- on():
Mark the device as operational. Moves from
STANDBY
toON
.
Attributes
The device can be operated in ON
state, where it exposes attributes and commands. The attributes can be accessed as python properties, for example:
recvh = DeviceProxy("STAT/RECVH/1")
# turn on all LED0s
recvh.RCU_LED0_RW = [True] * 32
# retrieve the status of all LED0s
print(recvh.RCU_LED0_R)
The attributes with an:
_R
suffix are monitoring points, reflecting the state of the hardware, and are thus read-only._RW
suffix are control points, reflecting the desired state of the hardware. They are read-write, where writing requests the hardware to set the specified value. Reading them returns the last requested value.
Meta data
A description of the attribute can be retrieved using:
print(recvh.get_attribute_config("RCU_LED0_R").description)
Attribute masks
Several devices employ attribute masks in order to toggle which elements in their hardware array are actually to be controlled. This construct is necessary as most control points consist of arrays of values that cover all hardware elements. These array control points are always fully sent: it is not possible to update only a single element without uploading the rest. Without a mask, it is impossible to control a subset of the hardware.
The masks only affect writing to attributes. Reading attributes (monitoring points) always result in data for all elements in the array.
For example, the RCU_mask_RW
array is the RCU mask in the recvh
device. It behaves as follows, when we interact with the RCU_LED0_R(W)
attributes:
recvh = DeviceProxy("STAT/RECVH/1")
# set mask to control all RCUs
recvh.RCU_mask_RW = [True] * 32
# request to turn off LED0 for all RCUs
recvh.RCU_LED0_RW = [False] * 32
# <--- all LED0s are now off
# recvh.RCU_LED0_R should show this,
# if you have the RCU hardware installed.
# set mask to only control RCU 3
mask = [False] * 32
mask[3] = True
recvh.RCU_mask_RW = mask
# request to turn on LED0, for all RCUs
# due to the mask, only LED0 on RCU 3
# will be set.
recvh.RCU_LED0_RW = [True] * 32
# <--- only LED0 on RCU3 is now on
# recvh.RCU_LED0_R should show this,
# if you have the RCU hardware installed.