PyCafe, is a Cython-based API that provides EPICS connectivity to Python through the CAFE library.
|
|
PyCafe, is a Cython-based API that provides EPICS connectivity to Python through the CAFE library.
#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 ()
# 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⟩)
# 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⟩)
# ⟨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')
# 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 |
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)
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⟩)
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)
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[]⟩)
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()
# 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()
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)
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']]
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)
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 |
# 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 |
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 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 |
# 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()