Home C++ MATLAB Python Download
 
Python Objects
for EPICS Channel Access
 

PyCafe, is a Cython-based API that provides EPICS connectivity to Python through the CAFE library.


Preliminaries Basic single channel operations Opening and closing channels Multiple scalar operations Synchronous group operations Control display parameters and channel information Monitors Special methods

Preliminaries

#import PyCafe and instantiate CAFE
import PyCafe
cafe = PyCafe.CyCafe()
cyca = PyCafe.CyCa()

The ⟨datatype⟩ argument (of type 'str'), appearing in data retrieval methods is a placeholder for any one of the following set of supported Python data types: 'float','int','str'. Alternatively 'native' may be entered, as is the default, which specifies the data is to be presented in the Python equivalent native data type.

A number of data retrieval methods are accompanied by an equivalent getCache method that retrieves the last value from cache. For example, the cafe.get method retrieves the current value from the control system while cafe.getCache retrieves the last cached value as returned through either a preceding get or monitor operation.

The ⟨handlePV⟩ is a placeholder for either the Process Variable (PV) name, 'pvName', or handle (of type 'int'), which is the reference to the PV as returned from the channel cafe.open method.

# Exception handling
try:
    handle = cafe.open('pvName')
    val = cafe.get(⟨handlePV⟩, dt=⟨datatype⟩)
    val = cafe.getCache(⟨handlePV⟩, dt=⟨datatype⟩)
except PyCafe.CafeException as cafeEx:   
    cafeEx.show()
    # or
    cafeEx.reveal()
    # or
    print(cafeEx.name,"(handle=", cafeEx.handle,")")
    print(cafeEx.error_code,":", cafeEx.error_text,cafeEx.error_info)   

# Exceptions are disabled by default, allowing the user instead to check on the return
# value, which will return a Python None in the event of a failed operation 
cafe.withExceptions(True) # enables Exceptions
# Tidy up: stop any monitors, close channels, release all CA resources 
cafe.terminate ()
 

Basic Single Channel Operations

Data retrieval methods returning a scalar value
# Scalar returned as Python equivalent of native type.
# If waveform, only first element is returned 
val = cafe.get(⟨handlePV⟩)

# Scalar returned as given Python ⟨datatype⟩, i.e. 'float','int','str';
# dt='native' returns the Python equivalent of the underlying native type
val = cafe.get(⟨handlePV⟩, dt=⟨datatype⟩);
# or equivalently with wrapper methods getFloat, getInt, getStr:
val = cafe.getStr(⟨handlePV⟩)
Data retrieval methods returning a Python list
# List returned with Python equivalent of native type; also applicable to waveforms 
valList = cafe.getList(⟨handlePV⟩)

# List returned with Python ⟨datatype⟩
valList = cafe.getList(⟨handlePV⟩, dt=⟨datatype⟩);
# or equivalently with wrapper methods getFloatList, getIntList, getStrList: 
valList = cafe.getFloatList(⟨handlePV⟩)
Data retrieval methods returning an array, either as a memoryview (default), numpy.ndarray, or array.array
# ⟨arraytype⟩ returned with Python equivalent of native type; applicable to waveforms 
mv = cafe.getArray(⟨handlePV⟩, art=⟨arraytype⟩), where ⟨arraytype⟩ may be one from
                                                            'memoryview, 'numpy', 'array.array'
# memoryview returned with Python ⟨datatype⟩
mv = cafe.getArray(⟨handlePV⟩, dt=⟨datatype⟩)
# or equivalently with wrapper methods getFloatArray, getIntArray, getStrArray: 
mv = cafe.getIntArray(⟨handlePV⟩)

# numpy.ndarray returned with Python equivalent of native type; applicable to waveforms 
ny = cafe.getArray(⟨handlePV⟩, art='numpy')
# numpy.ndarray returned with Python ⟨datatype⟩
ny = cafe.getArray(⟨handlePV⟩, dt=⟨datatype⟩, art='numpy')
# or equivalently with wrapper methods getFloatArray, getIntArray, getStrArray: 
ny = cafe.getIntArray(⟨handlePV⟩, art='numpy')
Retrieving structured data
# pvdata returned with value list having the Python equivalent of the native data type, 
# unless otherwise specified by dt=⟨datatype⟩
pvdata = cafe.getPV(⟨handlePV⟩, dt=⟨datatype⟩)

# pvdata has the following fields 
pvdata.nelem 'int' #No. of elements in value field
pvdata.value list[⟨datatype⟩]
pvdata.alarmStatus 'int'
pvdata.alarmSeverity 'int'
pvdata.ts list['int'] #EpicsTimeStamp: List of 2 elements
pvdata.tsDate list['int'] #EpicsTimestamp as date: List of 7 elements
pvdata.status 'int'
# and methods
pvdata.show()#print all field values
pvdata.showMax(int n) #print first n value[] elements of waveform
Asynchronous get

The asynchronous get is a non-blocking interaction, i.e. it does not wait to retrieve the data from the control system. The subsequent getCache methods will, if necessary, wait until the underlying callback function is complete or a timeout is reached.

# Non-blocking data retrieval operation 
try:
    cafe.getAsyn(⟨handlePV⟩)
    ...
    # Data may be subsequently retrieved from cache. Some examples are:
    val = cafe.getCache(⟨handlePV⟩, dt=⟨datatype⟩)
    valList = cafe.getListCache(⟨handlePV⟩, dt=⟨datatype⟩)
    npArray = cafe.getArrayCache(⟨handlePV⟩, dt=⟨datatype⟩, art='numpy')
    pvData = cafe.getPVCache(⟨handlePV⟩, dt=⟨datatype⟩)
except PyCafe.CafeException as cafeEx: 
    cafeEx.show()
    # or
    cafeEx.reveal()
    # or
    print(cafeEx.name,"(handle=", cafeEx.handle,")")
    print(cafeEx.error_code,":", cafeEx.error_text,cafeEx.error_info)
Timestamps and alarm status/severity values

CAFE differentiates between data acquisition and data presentation. The default is for timestamps and alarm status/severity values to be retrieved, even if they are not immediately presented to the user. Their data are, however, always retrieved and presented in the pvdata returned from the cafe.getPV methods. The data may also be retrieved from cache through the following methods, assuming that the policy for their retrieval from the control system has not been otherwise modified from the default.

# Retrieves alarm status (alarmList[0]) and alarm severity (alarmList[1])
alarmList = cafe.getAlarm(⟨handlePV⟩)

# Retrieves EpicsTimestamp expressed as a date in list of length 7
# ts[0]=year, ts[1]=month, ts[2]=day, ts[3]=hour, ts[4]=min, ts[5]=sec, ts[6]=nsec
ts = cafe.getTimeStampDate(⟨handlePV⟩)

# Retrieves EpicsTimestamp in list of length 2
# ets[0]=secPastEpoch, ets[1]=nsec
ets = cafe.getTimeStamp(⟨handlePV⟩)

# The following method determines the data to be returned for a given handle
# ⟨DBR_TYPE⟩ may be one of:
# cyca.CY_DBR_PRIMITIVE (value only)
# cyca.CY_DBR_STS       (value, alarm status and alarm severity)
# cyca.CY_DBR_TIME      (value, alarm status, alarm severity, and timestamp)
cafe.setDbrBase(⟨handlePV⟩, ⟨DBR_TYPE⟩)
Setting value(s)

The set method is able to interpret all Python data types and caters for scalars, lists, memoryviews, numpy arrays and array.array types.

try:
    # Input data may be in any Python data type
    # Single channel
    status = cafe.set(⟨handlePV⟩, data)
    # Multiple channels
    status,statusList[] = cafe.set(⟨handlePV[]⟩, data)		
except PyCafe.CafeException as cafeEx: 
    cafeEx.show()
    # or
    cafeEx.reveal()
    # or
    print(cafeEx.name,"(handle=", cafeEx.handle,")")
    print(cafeEx.error_code,":", cafeEx.error_text,cafeEx.error_info)
Writing/reading strings to/from waveforms

A waveform with datatype DBR_CHAR can act as a container for Python Strings

# Write string to waveform
status = cafe.set(⟨handlePV⟩, 'This is a test')
# Retrieve data from waveform, using the 'str' datatype
value = cafe.get(⟨handlePV[]⟩, 'str')
# or		
value = cafe.getWFAsString(⟨handlePV[]⟩)
 

Opening and Closing Channels

Opening channels

The cafe.open method creates the Channel Access (CA) virtual circuit and returns a handle (or handles) to the given PV(s). The following methods will wait for a default time period to establish a connection. Note that an exception is not invoked if the channel is disconnected. In such cases the handle will be automatically activated on the channel's eventual connection. Note that it is not essential to explicitly invoke these cafe.open methods. They will otherwise be called internally by cafe at the time of the first data access operation.

try:
    # Open (i.e. connect to) a single channel; returns an 'int'
    handle = cafe.open('pvname')
    # Open (i.e., connect to) a Python List of PV names; returns a Python List of 'int's
    handle[] = cafe.open(pvnames[])
except PyCafe.CafeException as cafeEx:  
    cafeEx.show()
    # or
    cafeEx.reveal()
    # or
    print(cafeEx.name,"(handle=", cafeEx.handle,")")
    print(cafeEx.error_code,":", cafeEx.error_text,cafeEx.error_info)

If there are multiple invocations of cafe.open, it would be more efficient and less of a time sink, to first prepare the messages before flushing the message buffer once at the end.

try:
    cafe.openPrepare ()
    # Open (i.e., connect to) a single channel; returns an 'int'
    handle = cafe.open('pvname')
    # Open (i.e., connect to) a Python List of PV names; returns a Python List of 'int's
    handle[] = cafe.open(pvnames[])
    # Now flush the message buffer and wait for waitTime (float) seconds
    cafe.openNowAndWait (⟨timeout⟩)
except PyCafe.CafeException as cafeEx:
    cafeEx.show()
    # or
    cafeEx.reveal()
    # or
    print(cafeEx.name,"(handle=", cafeEx.handle,")")
    print(cafeEx.error_code,":", cafeEx.error_text,cafeEx.error_info)
  
# Connection state of ⟨handlePV⟩
isConnected  = cafe.isConnected(⟨handlePV⟩)
# Connection state of all channels
allConnected = cafe.allConnected()
# Display information on handle(s)
cafe.printHandle(⟨handlePV⟩)
cafe.printHandles()
cafe.printDisconnectedHandles()
cafe.printConnectedHandles()
Closing channels
# Close connection to a single channel, referenced by either handle or PV name
status = cafe.close(⟨handlePV⟩)
# Close connection to all channels
status = cafe.closeChannels()
 

Multiple Scalar and Compound Operations

Multiple scalar operations

Operations to set/get a Python List of n scalar values to/from n PVs. Should one of the PVs be a waveform (or other multi-element record), then the get operation retrieves the first element only. The input Python List in the set operation may contain varied data types. The get operation will likewise, by default, return a Python List with each element of the List being presented in its native data type, unless otherwise specified by the dt=⟨datatype⟩, in which case the List is populated homogeneously with the specified data type. Setting the 'cacheFlag' argument to True allows the user to retrieve data from cache when preceded by any asynchronous (or synchronous) data retrieval operation.

valuesList, status, statusList  = cafe.getScalarList (⟨handlePVList⟩, dt=⟨datatype⟩, cacheFlag=False|True) 
status, statusList = cafe.setScalarList (⟨handlePVList⟩, values)
Multiple compound operations

Operations to set/get a Python List of n compound values to/from n PVs. A waveform is represented as a List within the List. The get operation will, by default, presentthe data in its native data type, unless otherwise specified by the dt=⟨datatype⟩, in which case the List is populated homogeneously with the specified data type. Setting the 'cacheFlag' argument to True allows the user to retrieve data from cache when preceded by any asynchronous (or synchronous) data retrieval operation.

valuesList, status, statusList  = cafe.getCompoundList(⟨handlePVList⟩, dt=⟨datatype⟩, cacheFlag=False|True)
status, statusList = cafe.setCompoundList(⟨handlePVList⟩, values)

# Example for PVs with various record tpes
PVNames = ['PV:AI','PV:MBBI','PV:WF']
Values  = [1.23,'On',[1,2,3,4,5,6,7]]
status, statusList = cafe.setCompoundList(PVNames,  Values)

valuesList, status, statusList = cafe.getCompoundList(PVNames, dt='native')
# Above returns valuesList  = [1.23,'On',[1,2,3,4,5,6,7]] 

valuesList, status, statusList = cafe.getCompoundList(PVNames, dt='int')
# Above returns valuesList  = [1,     1, [1,2,3,4,5,6,7]] 

valuesList, status, statusList = cafe.getCompoundList(PVNames, dt='float')
# Above returns valuesList  = [1.23,  1.,[1.,2.,3.,4.,5.,6.,7.]] 

valuesList, status, statusList = cafe.getCompoundList(PVNames, dt='str')
# Above returns valuesList  = ['1.23','On',['1','2','3','4','5','6','7']] 
 

Synchronous Group Operations

Defining and opening groups

Groups may be defined on the fly or read from an XML configuration file. The members of the group do not necessarily have to be related (i.e. be all of the same record and data type).

PVNames = ['PV:AI','PV:MBBI','PV:WF']
status  = defineGroup('groupName',  PVNames)
try:
    groupHandle = openGroup('groupName')
except PyCafe.CafeException as cafeEx:
    cafeEx.show()
    # or
    cafeEx.reveal()
    # or
    print(cafeEx.name,"(handle=", cafeEx.handle,")")
    print(cafeEx.error_code,":", cafeEx.error_text,cafeEx.error_info)
Operations on groups

Groups may be identified in ⟨groupHandleName⟩ either by their 'groupName' or groupHandle.

# Retrieving List of values; a waveform is represented as a List within the List.
valuesList, status, statusList  = getGroup (⟨groupHandleName⟩, dt='native')

# Retrieves a pvgroup object
pvgroup = getPVGroup(⟨groupHandleName⟩, dt='native')

# pvgroup has the following fields 
pvgroup.npv 'int'# No. of PVs in group
pvgroup.pvdata[] ⟨datatype⟩  # A List of pvdata types
pvgroup.name 'str'# Name of group
pvgroup.groupHandle'int'# Group handle
pvgroup.groupStatus'int'# Overall status of group operation
pvgroup.show() # print all field values
pvgroup.showMax(int n) # print the first n group members
 

Control Display Parameters and Channel Information

Retrieving control system display data
# pvctrl returned with value list having the Python equivalent of the native data type, 
# unless otherwise specified by dt=⟨datatype⟩
pvctrl = cafe.getCtrl(⟨handlePV⟩, dt=⟨datatype⟩)
pvctrl = cafe.getCtrlCache(⟨handlePV⟩, dt=⟨datatype⟩)
# pvctrl has the following fields 

pvctrl.nelem 'int' #No. of elements in value field
pvctrl.value list[⟨datatype⟩]
pvctrl.alarmStatus 'int'
pvctrl.alarmSeverity 'int'
pvctrl.precision 'int' #The precision to which the value(s) is given
pvctrl.units 'str' #Engineering units
pvctrl.noEnumStrings 'int' #Number of enumerated types in mbbi/o records
pvctrl.enumStrings list['str']#List of enumerated types in mbbi/o records
pvctrl.upperDisplayLimit  'float'
pvctrl.lowerDisplayLimit 'float'
pvctrl.upperAlarmLimit 'float'
pvctrl.lowerAlarmLimit 'float'
pvctrl.upperWarningLimit 'float'
pvctrl.lowerWarningLimit 'float'
pvctrl.upperControlLimit 'float'
pvctrl.lowerControlLimit 'float'
# and methods
pvctrl.show()  #print field values
pvctrl.showMax(int n)  #print first n value elements of waveform
Retrieving channel information

Information pertaining to the state of the channel and it's access rights, etc., can be obtained through the following method invocation.

pvinfo = cafe.getChannelInfo(⟨handlePV⟩);

The struct (pvinfo) has members:

pvinfo.channelID 'char'
pvinfo.connectFlag   'bool'
pvinfo.hostName 'str'
pvinfo.nelem 'int'
pvinfo.dataType 'int'
pvinfo.className 'str'
pvinfo.accessRead   'bool'
pvinfo.accessWrite 'bool'
pvinfo.connectionState   'int'
 

Monitors

Initiating a monitor

Monitors may be started with a number of optional input parameters, including a user-supplied callback function.

monitorID = cafe.monitorStart(⟨handlePV⟩, cb=py_cb, dbr=⟨CY_DBR_TYPE⟩, mask=⟨CY_DBE_TYPE⟩)

An example of a callback function that receives data of the triggered event.

def py_cb(handle, pvname, pvdata):
   print(handle, pvname)
   pvdata.show()

An example of a generic callback function that receives the handle of the triggered event. The handle can then be used as an input argument to any cafe method that retrives information from cache.

def py_cb(handle):
    try:
        # Invoke any Cache operation to obtain updated value
        print(cafe.getPVNameFromHandle(handle))
        if cafe.getCafeDbrTypeInCallback(handle) >= cyca.CY_DBR_CTRL:
            p1=cafe.getCtrlCache(handle)
        else:
            p1=cafe.getPVCache(handle)
        p1.show()
        # Any user action is permitted, including 'set' operations on other handles;
        only 'get' operations within the callback are disallowed by CA
        ...
    except PyCafe.CafeException as cafeEx:
        print ("ERROR IN CALLBACK HANDLER")
        cafeEx.show()

dbr=⟨CY_DBR_TYPE⟩ determines what parameter values are returned in addition to the value. Monitors are always started with the native data type, though the updated value may be selected in any meaningful type.

# dbr=⟨CY_DBR_TYPE⟩
dbr=cyca.CY_DBR_PLAIN # monitor returns value only
dbr=cyca.CY_DBR_STS   # monitor returns value, alarm status and alarm severity 
dbr=cyca.CY_DBR_TIME  # (default) monitor returns value, alarm status/sev, timestamp 

# mask=⟨CY_DBE_TYPE⟩ determines the trigger for the monitor callback:
mask=cyca.CY_DBE_VALUE # on value change
mask=cyca.CY_DBE_ALARM # on alarm change
mask=cyca.CY_DBE_LOG # on logging to the archiver
mask=cyca.CY_DBE_PROPERTY # on change in enum property values of mbbi records as from EPICS v.3.14.11
# mask=⟨CY_DBE_TYPE⟩ is a logical OR of the cyca.CY_DBE_xxx types mask=cyca.CY_DBE_VALUE|cyca.CY_DBE_LOG|cyca.CY_DBE_ALARM # (default)
Stopping a monitor
# Stop all monitors belonging to this handle
status = cafe.monitorStop(⟨handlePV⟩)
# Stop a specific monitor belonging to this handle
status = cafe.monitorStop(⟨handlePV⟩,  monitorID)
# Stop all monitors
status = cafe.monitorStopAll()