Monitoring & Control

The main API to control the station is through the Tango Controls API we expose on port 10000, which is most easily accessed using a PyTango client. The Jupyter Lab installation we provide is such a client.

Jupyter Lab

The station offers Jupyter Lab On http://localhost:8888, which allow one to interact with the station, for example to set control points, access monitoring points, or to graph their values.

The notebooks provide some predefined variables, so you don’t have to look them up:

# Create shortcuts for our devices, if they exist


def OptionalDeviceProxy(device_name: str):
    """Return a DeviceProxy for the given device, or None."""
    try:
        return DeviceProxy(device_name)
    except DevFailed:
        # device is not in database, or otherwise not reachable
        return None


apsct_l0 = OptionalDeviceProxy("STAT/APSCT/L0")
apsct_l1 = OptionalDeviceProxy("STAT/APSCT/L1")
apsct_h0 = OptionalDeviceProxy("STAT/APSCT/H0")
apscts = [apsct_l0, apsct_l1, apsct_h0]

apspu_l0 = OptionalDeviceProxy("STAT/APSPU/L0")
apspu_l1 = OptionalDeviceProxy("STAT/APSPU/L1")
apspu_h0 = OptionalDeviceProxy("STAT/APSPU/H0")
apspus = [apspu_l0, apspu_l1, apspu_h0]

recvl_l0 = OptionalDeviceProxy("STAT/RECVL/L0")
recvl_l1 = OptionalDeviceProxy("STAT/RECVL/L1")
recvh_h0 = OptionalDeviceProxy("STAT/RECVH/H0")
recvs = [recvl_l0, recvl_l1, recvh_h0]

unb2_l0 = OptionalDeviceProxy("STAT/UNB2/L0")
unb2_l1 = OptionalDeviceProxy("STAT/UNB2/L1")
unb2_h0 = OptionalDeviceProxy("STAT/UNB2/H0")
unb2s = [unb2_l0, unb2_l1, unb2_h0]

sdpfirmware_l = OptionalDeviceProxy("STAT/SDPFirmware/LBA")
sdp_l = OptionalDeviceProxy("STAT/SDP/LBA")
bst_l = OptionalDeviceProxy("STAT/BST/LBA")
sst_l = OptionalDeviceProxy("STAT/SST/LBA")
xst_l = OptionalDeviceProxy("STAT/XST/LBA")
beamlet_l = OptionalDeviceProxy("STAT/Beamlet/LBA")
digitalbeam_l = OptionalDeviceProxy("STAT/DigitalBeam/LBA")
antennafield_l = af_l = OptionalDeviceProxy("STAT/AFL/LBA")

sdpfirmware_h = OptionalDeviceProxy("STAT/SDPFirmware/HBA")
sdp_h = OptionalDeviceProxy("STAT/SDP/HBA")
bst_h = OptionalDeviceProxy("STAT/BST/HBA")
sst_h = OptionalDeviceProxy("STAT/SST/HBA")
xst_h = OptionalDeviceProxy("STAT/XST/HBA")
beamlet_h = OptionalDeviceProxy("STAT/Beamlet/HBA")
digitalbeam_h = OptionalDeviceProxy("STAT/DigitalBeam/HBA")
tilebeam_h = OptionalDeviceProxy("STAT/TileBeam/HBA")
antennafield_h = af_h = OptionalDeviceProxy("STAT/AFH/HBA")

sdpfirmware_h0 = OptionalDeviceProxy("STAT/SDPFirmware/HBA0")
sdp_h0 = OptionalDeviceProxy("STAT/SDP/HBA0")
bst_h0 = OptionalDeviceProxy("STAT/BST/HBA0")
sst_h0 = OptionalDeviceProxy("STAT/SST/HBA0")
xst_h0 = OptionalDeviceProxy("STAT/XST/HBA0")
beamlet_h0 = OptionalDeviceProxy("STAT/Beamlet/HBA0")
digitalbeam_h0 = OptionalDeviceProxy("STAT/DigitalBeam/HBA0")
tilebeam_h0 = OptionalDeviceProxy("STAT/TileBeam/HBA0")
antennafield_h0 = af_h0 = OptionalDeviceProxy("STAT/AFH/HBA0")

sdpfirmware_h1 = OptionalDeviceProxy("STAT/SDPFirmware/HBA1")
sdp_h1 = OptionalDeviceProxy("STAT/SDP/HBA1")
bst_h1 = OptionalDeviceProxy("STAT/BST/HBA1")
sst_h1 = OptionalDeviceProxy("STAT/SST/HBA1")
xst_h1 = OptionalDeviceProxy("STAT/XST/HBA1")
beamlet_h1 = OptionalDeviceProxy("STAT/Beamlet/HBA1")
digitalbeam_h1 = OptionalDeviceProxy("STAT/DigitalBeam/HBA1")
tilebeam_h1 = OptionalDeviceProxy("STAT/TileBeam/HBA1")
antennafield_h1 = af_h1 = OptionalDeviceProxy("STAT/AFH/HBA1")

stationmanager = OptionalDeviceProxy("STAT/StationManager/1")
ccd = OptionalDeviceProxy("STAT/CCD/1")
ec = OptionalDeviceProxy("STAT/EC/1")
pcon = OptionalDeviceProxy("STAT/PCON/1")
psoc = OptionalDeviceProxy("STAT/PSOC/1")
docker = OptionalDeviceProxy("STAT/Docker/1")
temperaturemanager = OptionalDeviceProxy("STAT/TemperatureManager/1")
configuration = OptionalDeviceProxy("STAT/Configuration/1")

# Put them in a list in case one wants to iterate
devices = (
    [
        stationmanager,
        ccd,
        ec,
        pcon,
        psoc,
        docker,
        temperaturemanager,
        configuration,
        sdpfirmware_l,
        sdp_l,
        bst_l,
        sst_l,
        xst_l,
        beamlet_l,
        digitalbeam_l,
        af_l,
        sdpfirmware_h,
        sdp_h,
        bst_h,
        sst_h,
        xst_h,
        beamlet_h,
        digitalbeam_h,
        tilebeam_h,
        af_h,
        sdpfirmware_h0,
        sdp_h0,
        bst_h0,
        sst_h0,
        xst_h0,
        beamlet_h0,
        digitalbeam_h0,
        tilebeam_h0,
        af_h0,
        sdpfirmware_h1,
        sdp_h1,
        bst_h1,
        sst_h1,
        xst_h1,
        beamlet_h1,
        digitalbeam_h1,
        tilebeam_h1,
        af_h1,
    ]
    + apscts
    + apspus
    + recvs
    + unb2s
)

Note: the Jupyter notebooks use enhancements from the itango suite, which provide tab completions, but also the Device alias for DeviceProxy as was used in the Python examples in the next section.

For example, you can start a new Station Control notebook (File->New->Notebook->StationControl), and access these devices:

../_images/jupyter_basic_example.png

You can also use Jupyter Labs integrated console to run your commands (File->New->Console->StationControl) and exploit the itango suite enhancements:

../_images/jupyter_console_basic_example.png

Jupyter Lab and Git

We provide the ability to interact with git repositories by including the jupyter-git plugin. See their webpage for how to use this plugin.

In our installation, all git commits will be made as a fictive JupyterLab on $HOSTNAME user. This is because JupyterLab does not know who the user is, and it’s preferred to explicitly state at least where the commit comes from, rather than under the name of the last person who told git who they are.

Any problems encountered in git that cannot be solved through the plugin, can be solved by spawning a Terminal and using the git command-line interface.

PyTango

To access a station from scratch using Python, we need to install some dependencies:

pip3 install tango

Then, if we know what devices are available on the station, we can access them directly:

import tango
import os

# Tango needs to know where our Tango API is running.
os.environ["TANGO_HOST"] = "localhost:10000"

# Construct a remote reference to a specific device.
# One can also use "tango://localhost:10000/STAT/Boot/1" if TANGO_HOST is not set
boot_device = tango.DeviceProxy("STAT/Boot/1")

# Print the device's state.
print(boot_device.state())

To obtain a list of all devices, we need to access the database:

import tango

# Tango needs to know where our Tango API is running.
import os
os.environ["TANGO_HOST"] = "localhost:10000"

# Connect to the database.
db = tango.Database()

# Retrieve the available devices, excluding any Tango-internal ones.
# This returns for example: ['STAT/Boot/1', 'STAT/Docker/1', ...]
devices = list(db.get_device_exported("STAT/*"))

# Connect to any of them.
any_device = tango.DeviceProxy(devices[0])

# Print the device's state.
print(any_device.state())

ReST API

We also provide a ReST API to allow the station to be controlled without needing to use the Tango API. The root access point is http://localhost:8080/tango/rest/v10/hosts/databaseds;port=10000/ (credentials: tango-cs/tango). This API allows for:

  • getting and setting attribute values,

  • calling commands,

  • retrieving the device state,

  • and more.

For example, retrieving http://localhost:8080/tango/rest/v10/hosts/databaseds;port=10000/devices/STAT/SDP/1/state returns the following JSON document:

{"state":"ON","status":"The device is in ON state."}

For a full description of this API, see https://tango-rest-api.readthedocs.io/en/latest/.