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

CAFE is a C++ library and software platform providing an intuitive and user-friendly interface to EPICS Channel Access (CA).


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

Preliminaries

// Instantiate CAFE
CAFE * cafe = new CAFE();

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

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 cafe->get or cafe->monitor operation. Alternatively, cafe->get can be configured to return cached data for monitored channels by setting the relevant policy accordingly.

// Exception handling
try {
    int status = cafe->open('pvName', handle);  
} catch(CAFEException_open & e) {
    cout << e.what() << endl;
}  
			
// 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. 
// If waveform, only the first element is returned if a scalar variable is given. 
// Any datatype may be specified 
double value = 0;
int status = cafe->get (⟨handlePV⟩, value);
if (status != ICAFE_NORMAL) {
  cout << "status = " << status << " indicates an error "<< endl;
  cafe->getCafeStatus().report(status);
  //or
  cafe->printStatus(handle,status);
			
  //To explicitly check on timeout error
  if (cafe->getCafeStatus().isTimeout(status)) {
    cout << "Timeout error" << endl;	
  }	
} 
else {
  cout << "Value d= " << d << endl; 
}

// With alarm status and alarm severity
short alarmStatus, alarmSeverity; epicsTimeStamp ets;
status = cafe->get (⟨handlePV⟩, value, alarmStatus, alarmSeverity, ets);
Data retrieval methods returning an array
// Array of scalars returned.
// It is important that sufficient memory is allocated to retrieve the waveform (or other) data 
double value[256];
status = cafe->get (⟨handlePV⟩, value);
Retrieving structured data
// PVDataHolder object returned. See "PVHolder.h" and "PVDataHolder.h"
PVDataHolder pvd;
status = cafe->get (⟨handlePV⟩, pvd);
// PVDataHolder has the following access methods 
pvd.getNelem()  // No. elements in val[] field
//Retrieve value with desired data type; first element only
pvd.getAsString() string
pvd.getAsDbr_string_t() dbr_string_t (char[40])
pvd.getAsDouble() double
pvd.getAsFloat() float
pvd.getAsChar() char // (unsigned short) pvdata.getAsChar()
pvd.getAsShort() short
pvd.getAsUShort() unsigned short
pvd.getAsInt() long
pvd.getAsLong() long
pvd.getAsULong() unsigned long
pvd.getAsLongLong() long long
pvd.getAsULongLong() unsigned long long
 
//Retrieve an array of values with desired data type, e.g., as a string
for (unsigned int i=0; i < pvd.getNelem(); ++i) { pvd.getAsString(i); }
//Vectors
pvd.getAsVectorString() vector<string>
pvd.getAsVectorDouble() vector<double>
pvd.getAsVectorFloat() vector<float>
pvd.getAsVectorInt() vector<int>
pvd.getAsVector... vector<...>
//Shared pointers
pvd.getAsVString() boost::shared_ptr<vector<string> >
pvd.getAsVDouble() boost::shared_ptr<vector<double> >
pvd.getAsVFloat() boost::shared_ptr<vector<float> >
pvd.getAsVInt() boost::shared_ptr<vector<int> >
pvd.getAsV... boost::shared_ptr<vector<...> >
 
//Alarm status and severity
pvd.hasAlarm() bool // True if alarm stat/sev returned
pvd.getAlarmStatus() short
pvd.getAlarmSeverity() short
pvd.getAlarmStatusAsString() string
pvd.getAlarmSeverityAsString string
pvd.hasTimeStamp() bool // True if timestamp returned
pvd.getTimeStamp() epicsTimeStamp // Struct fields: secPastEpoch, nsec
 
//Interpret a waveform with elements of data type dbt_char_t as a single string
pvd.getWFAsString() string
 
//Status of method invocation
pvd.getStatus() int
 
//Static data. At PSI, PV names are formulated from a device and attribute component, separated by a colon.
//The default symbol for the separator may be configured in "defines.h"
pvd.getPVName() char *
pvd.getDevice() char *
pvd.getAttribute() char *
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 
cafe->get (⟨handlePV⟩);
...
// Data may be subsequently retrieved from cache in a number of ways. Some examples are:
double dVal; PVDataHolder pvd;
status  = cafe->getCache      (⟨handlePV⟩, dVal);
status  = cafe->getPVCache    (⟨handlePV⟩, pvd); // Status also contained within pvd.getStatus()
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 PVDataHolder object 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 and alarm severity
short alarmStatus   = pvd.getAlarmStatus();
short alarmSeverity = pvd.getAlarmSeverity();

// Retrieves etsDate which is a struct with 7 members:
// year, month, day, hour, min, sec, nsec
etsDate ts = pvd.getEpicsTimeStampAsDate();

// Retrieves epicsTimestamp which is a struct with 2 members:
// secPastEpoch, nsec
epicsTimestamp ets = pvd.getEpicsTimeStamp();
Setting value(s)

The set method is able to interpret many data types and caters for value, string, array and vector types.

// Input data may be of many data types
int status = cafe->set (⟨handlePV⟩, data);
if (status != ICAFE_NORMAL) {
  cout << "status = " << status << " indicates an error "<< endl;
  cafe->getCafeStatus().report(status);
  //or
  cafe->printStatus(handle,status);
}
 

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.

// Open (i.e. connect to) a single channel; returns status and an 'unsigned int' handle by value
status = cafe->open ('pvname', unsigned int handle);
// Open (i.e. connect to) an array of PV names; returns an array of 'unsigned int's by value
status = cafe->open (pvArray, unsigned int* handleArray);
if (status != ICAFE_NORMAL) {
  cout << "status = " << status << " indicates an error "<< endl;
  cafe->getCafeStatus().report(status);
  //or
  cafe->printStatus(handle,status);
}

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.

cafe->openPrepare ()
// Open (i.e. connect to) a single channel; returns status and an 'unsigned int' handle by value
status = cafe->open ('pvname', unsigned int handle);
// Open (i.e. connect to) an array of PV names; returns an array of 'unsigned int's by value
status = cafe->open (pvArray, unsigned int* handleArray);
// Now flush the message buffer and wait for waitTime (float) seconds
cafe->openNowAndWait (⟨timeout⟩)
if (status != ICAFE_NORMAL) {
  cout << "status = " << status << " indicates an error "<< endl;
  cafe->getCafeStatus().report(status);
  //or
  cafe->printStatus(handle,status);
}

// Connection state of ⟨handlePV⟩
bool isConnected  = cafe->isChannelConnected (⟨handlePV⟩);
// Connection state of all channels
bool allConnected = cafe->allChannelsConnected();
// 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 Operations

Multiple Scalar Operations

Operations to set/get a vector 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 vector in the set operation may contain varied data types. The get operation will likewise, by default, return a vector by value.

status = cafe->get (std::vector⟨unsigned int⟩ handleArray, std::vector⟨datatype valueArray);
status = cafe->set (std::vector⟨unsigned int⟩ handleArray, std::vector⟨datatype valueArray);
 

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).

std::vector⟨std::string⟩ PVNames = {"PV:AI","PV:MBBI","PV:WF"};
status = cafe->groupDefine ('groupName',  PVNames);
status = cafe->groupOpen ('groupName', unsigned int groupHandle);
if (status != ICAFE_NORMAL) {
  cout << "status = " << status << " indicates an error "<< endl;
  cafe->getCafeStatus().report(status);
  //or
  cafe->printStatus(handle,status);
}
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 vector within the vector.
status  = cafe->groupGetScalar (⟨groupHandle⟩, vector⟨datatype valueArray, vector⟨int statusArray);

// Retrieves a pvgroup object
status  = cafe->groupGet (⟨groupHandle⟩, PVGroup pvgroup);

// pvgroup has the following fields 

pvgroup.npv unsigned int// No. of PVs in group
pvgroup.pvdata PVDataHolder // A List of pvdata types
pvgroup.name char[]// Name of group
pvgroup.groupHandleunsigned int// Group handle
pvgroup.statusGroupint// Overall status of group operation
pvgroup.show() // print all field values
pvgroup.showMax(int n) // print the first n group members
 

Retrieving Control Parameters

Control Parameters

The control display parameters are held within the PVCtrlHolder object. The methods that extract the data from the val field are as for the PVDataHolder object (shown previously) and are not repeated here. If it is only the control parameters (e.g., engineering units, display limits, etc.) that are of interest, it is often sufficient to retreive such data directly from cache as they are typically static.

// PVCtrlHolder object returned. See "PVHolder.h" and "PVCtrlHolder.h" 
PVCtrlHolder pvc;
status = cafe.getCtrl (⟨handlePV⟩, pvc)
status = cafe.getCtrlCache (⟨handlePV⟩, pvc)
// PVCtrlHolder has the methods listed below. 
pvc.getNelem() unsigned int // No. elements in val[] field
pvc.getAlarmStatus() short
pvc.getAlarmSeverity short
pvc.getAlarmStatusAsString() string
pvc.getAlarmSeverityAsString() string
pvc.precision int // The precision to which the value(s) is given
pvc.units str // Engineering units
pvc.noEnumStrings int // The number of enumerated types in mbbi/o records
pvc.enumStrings str  // List of enumerated types in mbbi/o records
pvc.upperDisplayLimit  float
pvc.lowerDisplayLimit float
pvc.upperAlarmLimit float
pvc.lowerAlarmLimit float
pvc.upperWarningLimit float
pvc.lowerWarningLimit float
pvc.upperControlLimit float
pvc.lowerControlLimit float
# and methods
pvc.show()  // Print field values
pvc.showMax(int n)  // Print first n value[] elements of waveform
 

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 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 Exception as inst:
    print ("ERROR IN CALLBACK HANDLER")
    print (inst)

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:
cyca.CY_DBE_VALUE on value change
cyca.CY_DBE_ALARM on alarm change
cyca.CY_DBE_LOG on logging to the archiver
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)
 

Special methods

Specialized methods can be provided almost at will. Here are a couple of examples.

Set and match

 

Helper methods

A number of helper methods exist to allow the user to easily access information from within the Boost container which stores all static and dynamic data related to a given channel. Some examples are shown. See "HandleHelper.h" for the full complement of available methods.

PV/handle from handle/PV

 

Policies

A number of policy-type classes exist to allow the user to dynamically configure the behaviour of the method invocations, e.g., whether they be blocking or non-blocking, enable/disenable self-governing timeouts, etc. Some examples are shown. See "policies.h" for the full complement of available methods.

Block or non-blocking