CAFE is a C++ library and software platform providing an intuitive and user-friendly interface to EPICS Channel Access (CA).
|
|
CAFE is a C++ library and software platform providing an intuitive and user-friendly interface to EPICS Channel Access (CA).
// 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();
// 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);
// 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);
// 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 * |
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()
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();
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); }
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();
// 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 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);
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); }
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.groupHandle | unsigned int | // Group handle |
pvgroup.statusGroup | int | // Overall status of group operation |
pvgroup.show() | // print all field values | |
pvgroup.showMax(int n) | // print the first n group members |
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 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 |
# Stop all monitors belonging to this handle status = cafe.monitorStop(⟨handlePV⟩) # Stop a specific monitor belonging to this handle status = cafe.monitorStop(⟨handlePV⟩, monitorID)
Specialized methods can be provided almost at will. Here are a couple of examples.
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.
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.