Python Module cmwrda

Module implements an Interface for RDA device access, this means provides access to FESA-devices.

state: preliminary (however, seams to be usable)

Required Set-Up

Usage of the Python RDA device access requires the set-up for accelerator controls usage and selection of the controls domain to be used.

Extension of the library search path LD_LIBRARY_PATH is no longer needed (from October 2019) for usage of the module cmwrda. The path to resolve library dependencies is now included in the C++ implementation of the module.

CSCO-Setup

To be able to use the control system tools, the control system setup should be executed:

source /common/usr/cscofe/scripts/accdefs

Don't forget the leading 'source' to establish the definitions in the running shell.

Further the location of the module has to be added into the Python-path:

export PYTHONPATH=/common/usr/cscofe/python

An easy way to ensure the setup is to insert the above given commands into the shell configuration file .bashrc, located in the home directory.

RDA Domain-Selection

RDA remote access uses address information which is hold in so called 'directory servers'. At GSI three domains are established, each using a dedicated directory server:
  • Production: Devices and software in GSIs accelerator facility
  • Integration: Devices (generally mock-devices) and software which is used for integration tests
  • Development: Devices and software under development

Before RDA can be used, the respective directory server has to be selected. Indication is by the environment variable CMW_DIRECTORY_CLIENT_SERVERLIST (export CMW_DIRECTORY_CLIENT_SERVERLIST=cmwdev00a.acc.gsi.de:5021 etc).

For setting of the environment variable CMW_DIRECTORY_CLIENT_SERVERLIST before using Python with the module cmwrda two special comand can be used:
rdapython
or
rdaipython

Both commands will select the required domain by defining the mentioned environment variable, and then call the standard interpreter, python or ipython. Selection of the domain is by comand-line options:
  • --dev: RDA access in the development domain
  • --pro or --prod: RDA access in the production domain
  • --int: RDA access in the integration domain
  • no option: Default is the development domain
$ rdapython --dev # will operate in the development domain

$ rdapython --pro # will operate in the production domain

Environment variable RDAPY_SYSTEM_ENVIRONMENT may be used to set the default environment for rdapython/rdaipython:
  • export RDAPY_SYSTEM_ENVIRONMENT=dev will set the development environment as default
  • export RDAPY_SYSTEM_ENVIRONMENT=pro will set the production environment as default
Explicit usage of options --pro / --prod, --dev, or --int will ovveride the default setting.

Usage

>>> import cmwrda

Basic help is provided:
>>> help(cmwrda)

Concepts

Communication is with properties of devices. A device/property combination establishes a communication endpoint. For each combination of device and property a separate endpoint has to be created to provide access to the properties.

For each such comunication endpoint, data can be read (get), write (set), or subscribed..

In the read and write actions, including subscriptions, data is transported in data-containers. The data sets contain data-elements, called value. Each data-container may contain multiple value data-elements.

Each value is labeled by a name, called the tag (on the FESA-Level: value-item-name-ref), and contains one element of the data types which are supported by RDA. Such elements can be single data (scalars) or multiple data. Presently the Python binding supports
  • Scalar: single values of basic data types (BOOL, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, STRING)
  • Array: arrays of the basic data types (BOOL_ARRAY, SHORT_ARRAY, ..., DOUBLE_ARRAY, STRING_ARRAY)
  • Array2D: two-dimensional arrays of the basic data types (BOOL_ARRAY_2D, SHORT_ARRAY_2D, ..., STRING_ARRAY_2D)
An identifier for the RDA data type is also contained in the value-elements.

Properties may require filters, which then specify the access in detail. Handling of filters is explained below.

Classes and Types

Python-Classes
  • Rda: Endpoint for communication, providing read/write access
  • Data: Data-container
  • ValueScalar: Value-element for single data
  • ValueArray: Value-element for arrays
  • ValueArray2D: Value-element for two dimensional arrays
  • RdaSubscriptionHandle: Set up subscription to a property
  • RdaListener: Callback base class for subscriptions
Variables:
  • RdaType: RDA data types, defined as map
    • RdaType ['DOUBLE'] will provide the enumeration value for the type double (6)
    • RdaType [6] will provide the mnemonic string ('DOUBLE')

Handling

Establish Communication

Define communication endpoint to property of FESA-device:
>>> conn = cmwrda.Rda('XYRT1LT43', 'Setting')

or, with explicit defining a device server (does not depend on database-entries for name resolution),
>>> conn = cmwrda.Rda('XYRT1LT43', 'Setting', 'ElectroStatQuadDU.vml200d')

From such an communication endpoint data can be read, or data can be written to.

Context-Information

Selector for Beam-Process, Sequence, ...

Access to multiplexed data (reading and writing) requires to provide a data selector, which in general means the beam-process number.

The selector has to be provided as parameter in read or write operations. The selector parameter can be omitted for non-multiplexed data.

FESA devices expect the selector as a string in the form 'FAIR.SELECTOR.P=<nn>'. In the cmwrda-module the selector can be given
  • in the full form: 'FAIR.SELECTOR.P=17', 'FAIR.SELECTOR.S=3', ...
  • abbreviated: 'P=17', 'S=3'
  • or, for beam-processes, as integer number only: 17
Context-Information in Read-Data

Data-Containers, read from devices, provide in addition some context-information:
  • acquisition stamp
  • cycle stamp
  • cycle name
Such context-Information is provided as objects of class Context. Context elements can be extracted from a data-container data by:
ctx = data.getContext()

The elements of the context can be extracted by particular methods, e.g. getCycleName(), getAcqStamp() (in nano seconds) or getAcqStampSec() (full seconds) with getAcqStampNanoSec() (nano sec fraction from full second).

Reading Data:

Read-Access

To access a device, define a connection device / property combination, as in above section "Establish Communication".

From such a connection, data can be read. For multipexed properties, a selector must be given, which can be omitted for non-multiplexed properties.
>>> data = conn.read() # non-multiplexed: no data selector needed
>>> data = conn.read('P=17') # multiplexed: selector needed, here: beam process 17 :

return an element of class Data , containing returned property data as objects of subclasses of class Value (objects of classes !ValueScalar , !ValueArray , !ValueArray2D ).

Access to properties with filters are supported, see below.
Extracting Data

Such Data-object can be printed directly:
>>> print data # will print contents of data

Contents of Data-object can be extracted at once:
>>> allValues = data.getValues()

will give a dictionary, containing all Value-objects, labelled by their tag-name. To obtain the tag names, iterate over the dictionary:
>>> for tag in allValues: print tag

Single Value-elements can be extracted from the dictionary, indexed by the tag-name (e.g. 'current')
>>> value = allValues['current']

Value elements can be extracted from the Data-object directly, giving the tag-name (e.g. value-item 'current'):
>>> value = data.getValue('current')

From the Value-elements the data, as well as the tag-name and the RDA-type can be extracted:
>>> v = value.data() # get the data from the value-element
>>> tag = value.tag() # get the tag-name
>>> rdaType = value.type() # get the RDA data type (BOOL, ..., INT, ..., DOUBLE_ARRAY, ...)

Value-element data always are returend as list, even for scalars and two-dimensional arrays.

To reconstruct two-dimensinal arrays, number of rows and columns is provided:
>>> r = value.rowCnt() # number of rows
>>> c = value.columnCnt() # number of columns

Writing Data

Provide Data Container

Data is transported in data containers. To start from scratch, a new (empty) data container has to be created:
>>> data = cmwrda.Data()

Then value-elements have to be inserted into the container:
Create Value-elements, either as single value (ValueScalar), Array (ValueArray) or as two-dimensional Array (Array2D), by providing name of tag/value-item, data-type, and data:
>>> v1 = cmwrda.ValueScalar('curents', cmwrda.RdaType['DOUBLE'], 17.04)
then add Value-element to the data container:
>>> data.add(v1)
repeat for other Value-elements
v2=cmwrda.ValueArray ( 'voltageValuesToDemonstrate', cmwrda.RdaType['DOUBLE_ARRAY'], [17.4, 47.11, 0.815] )
v3=cmwrda.ValueScalar ( 'voltageValuesValidation', cmwrda.RdaType['LONG'], 17 )
. . .
until Data Container is filled with desired value-elements

It's also possible to use data containers, as returned from a read operqtion, for write operations.

Write Access

To access a device, define a connection device / property combination, as in above section "Establish Communication".

To such a connection, data can be send. For multipexed properties, a selector must be given, which can be omitted for non-multiplexed properties.
>>> conn.write (data) # no data selector needed
>>> conn.write(data, 'P=17') # selector needed for multiplexed data, here: beam process 17

Access to properties with filters are supported, see below.

Subscriptions

Subscriptions are set up by the class RdaSubscriptionHandle :

>>> subscr = cmwrda.RdaSubscriptionHandle('XYRT1LT43', 'Setting')

or, with explicit defining a device server (does not depend on database-entries for name resolution),

>>> subscr = cmwrda.RdaSubscriptionHandle('XYRT1LT43', 'Setting', 'ElectroStatQuadDU.vml200d')

The method subscribe will switch on the subscription. To receive data from a subscription, a listener (a callback) has to be provided and, for multiplexed properties, a selector must be given:

>>> subscr.subscribe(listener) # no data selector needed
>>> subscr.subscribe(listener, 'P=17') # selector for multiplexed data, here: beam process 17

An empty selector (an empty string, "" , or a None ), which is the default, specifies subscription to all executions of the property (not specific to a beam process or sequence).

Subscriptiopns to properties with filters are supported, see below.

The method unsubscribe will switch a subscription off.

>>> subscr.unsubscribe()

The listener (the callback) must be an object of a class which inherits from cmwrda.RdaListener . It has to implement a method receive and should implement a method !onError ,

>>> class MyListener(cmwrda.RdaListener):
>>> def __init__(self):
>>> cmwrda.RdaListener.__init__(self)
>>> def receive(self, data):
>>> ...

The receive method will be called on notifications and its onError when rda reports an error. The data that is passed to the receive method is a cmwrda.Data container, just as the data you get from the read call (see above). For an example on how to inherit from RdaListener see https://www-acc.gsi.de/viewvc/view/fesa/device/driver/feSupport/trunk/tools/rdapy/test/rdapy_test.py. The most simple python script for a subscription is (non multiplexed property, default callback):
   nomen = "GTK1MU1"
   prop = "Status"
   subs = cmwrda.RdaSubscriptionHandle(nomen, prop)
   listener = cmwrda.RdaListener()
   subs.subscribe(listener)
   while True:
       time.sleep(5)
       print("waiting for updates")

Note that the RdaListener is only meant as base class which should be subclassed by the user. It prints a message on the screen when a notification arrives, and displays the data in generic form. To do anything useful you need to inherit and override its receive.

Filters

FESA properties may require filters. Filter information is transported in container of type Data , filled the same way as a Data container in a write access (see provide data contqainer). The filter container is passed as additional parameter in the access method. In case of filters, a selector must be provided (which, of cause, then can be empty).

Filters can be provided in read , write od subscribe methods:

>>> filter = cmwrda.Data()
. . . # fill filter object

>>> data = conn.read('P=17', filter)
>>> conn.write(data, 'P=17', filter)
>>> subs.subscribe(listener, "", filter)

Online Help

Try Python-help:
>>> help(cmwrda)
Topic revision: r25 - 19 Nov 2019, UdoKrause
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback