@@ -158,6 +158,26 @@ returned as well. They are then stored in the class and cann be accessed with
|
||||
unchanged and `FE_ERR_DRIVER` is returned. Additionally, an error message is
|
||||
logged with `cm_msg`.
|
||||
|
||||
### Synchronous and asynchronous mode
|
||||
|
||||
This driver supports asynchronous write operations via `mEpicsCa::put<false>`.
|
||||
When writing asynchronously, multiple write commands can be send in batches:
|
||||
|
||||
```cpp
|
||||
auto val_ca = mEpicsCa<double>("EXAMPLE:AO");
|
||||
auto drvh_ca = mEpicsCa<double>("EXAMPLE:AO.DRVH");
|
||||
auto drvl_ca = mEpicsCa<double>("EXAMPLE:AO.DRVL");
|
||||
|
||||
drvh_ca.put<false>(&10.0);
|
||||
drvl_ca.put<false>(&0.0);
|
||||
val_ca.put<false>(&5.0);
|
||||
|
||||
val_ca.flushIo();
|
||||
```
|
||||
|
||||
All three write commands get send at once with `mEpicsCa::flushIo>`. This helps
|
||||
to minimize network traffic when writing multiple values at once.
|
||||
|
||||
## Testing
|
||||
|
||||
This driver comes with its own test suite which can be run via CMake:
|
||||
|
||||
+43
-9
@@ -80,9 +80,10 @@ template <typename T> class mEpicsCa {
|
||||
* left unchanged.
|
||||
*
|
||||
* When reading a value, the `STAT` and `SEVR` fields of the record of the
|
||||
* channel are also read. If `SEVR` is not `menuAlarmSevrNO_ALARM`, it is
|
||||
* assumed that the read value is invalid. The error is then logged and an
|
||||
* error code is returned.
|
||||
* channel are also read and used to update `mEpicsCa::status` and
|
||||
* `mEpicsCa::severity`. If the severity is larger than
|
||||
* `menuAlarmSevrNO_ALARM`, the error is logged and an error code is
|
||||
* returned.
|
||||
*
|
||||
* If the subscription mechanism is used, this method just reads
|
||||
* `mEpicsCa::cached`.
|
||||
@@ -116,6 +117,8 @@ template <typename T> class mEpicsCa {
|
||||
* @brief Like `mEpicsCa::get(V *value)`, but for a C-style string (char
|
||||
* array).
|
||||
*
|
||||
* @tparam S Whether the method operates in synchronous or asynchronous mode
|
||||
* (see above).
|
||||
* @param buf Pointer to the first char off the array
|
||||
* @param len Length of the array
|
||||
* @return int MIDAS status error code as defined in midas.h.
|
||||
@@ -126,6 +129,21 @@ template <typename T> class mEpicsCa {
|
||||
* @brief Try to write `value` to the channel. If successful,
|
||||
* `CM_SUCCESS` is returned and the channel is updated.
|
||||
*
|
||||
* By default, this method operates in "synchronous" mode: It returns after
|
||||
* the channel / record field has been updated. However, by setting the
|
||||
* template parameter S to `false`, it can be switched to asynchronous mode:
|
||||
* The write request is queued within EPICS and this method returns
|
||||
* immediately. The channel / field will be updated once `mEpicsCa::flushIo`
|
||||
* has been called. This is useful if multiple values should be written at
|
||||
* once, because it minimizes network traffic (i.e. sending one request
|
||||
* updating 10 values instead of 10 request updating one value). Until
|
||||
* `mEpicsCa::flushIo` has been called, the channel / field does not contain
|
||||
* the new value! If multiple instances of `mEpicsCa` are involved, it is
|
||||
* sufficient to call `mEpicsCa::flushIo` on one of them to update all
|
||||
* values at once.
|
||||
*
|
||||
* @tparam S Whether the method operates in synchronous or asynchronous mode
|
||||
* (see above).
|
||||
* @tparam V Type of the `value` pointer, which needs to be a valid "alias"
|
||||
* for `T`, see `mEpicsCa::get`. This is checked during instantiation.
|
||||
*
|
||||
@@ -141,17 +159,35 @@ template <typename T> class mEpicsCa {
|
||||
* @param value Pointer to the new value which gets written to the channel.
|
||||
* @return int MIDAS status error code as defined in midas.h.
|
||||
*/
|
||||
template <typename V> int put(V *value);
|
||||
template <bool S = true, typename V> int put(V *value);
|
||||
|
||||
/**
|
||||
* @brief Like `mEpicsCa::put(V *value)`, but for a C-style string (char
|
||||
* array).
|
||||
*
|
||||
* @tparam S Whether the method operates in synchronous or asynchronous mode
|
||||
* (see above).
|
||||
* @param buf Pointer to the first char off the array
|
||||
* @param len Length of the array
|
||||
* @return int MIDAS status error code as defined in midas.h.
|
||||
*/
|
||||
int put(const char *buf, u_long len);
|
||||
template <bool S = true> int put(const char *buf, u_long len);
|
||||
|
||||
/**
|
||||
* @brief Finishes all asynchronous `put` calls (or times out)
|
||||
*
|
||||
* `mEpicsCa::put` can operate in asynchronous mode, meaning that the write
|
||||
* requests are queued so they can be sent in batch. This method calls
|
||||
* `ca_flush_io` using the provided `timeout` so the queued requests are
|
||||
* processed. Using the asynchronous mode can be useful if a lot of
|
||||
* operations are performed at once, because it minimizes network traffic
|
||||
* (sending 10 requests at once instead of sending 10 individual requests,
|
||||
* each containing handshakes etc.). This method should therefore be called
|
||||
* after first performing asynchronous `mEpicsCa::put` calls.
|
||||
*
|
||||
* @return int Error code received from `ca_flush_io` (converted to MIDAS)
|
||||
*/
|
||||
int flushIo();
|
||||
|
||||
/**
|
||||
* @brief Returns whether the driver is currently connected to an EPICS
|
||||
@@ -226,14 +262,12 @@ template <typename T> class mEpicsCa {
|
||||
static void eventCallback(struct event_handler_args args);
|
||||
|
||||
// The following methods hold the actual implementations of `mEpicsCa::get`
|
||||
// for different types of `T`.
|
||||
int getRaw(char *buf, size_t len);
|
||||
template <typename V> int getRaw(V *value);
|
||||
|
||||
// The following methods hold the actual implementations of `mEpicsCa::put`
|
||||
// for different types of `T`.
|
||||
int putRaw(const char *buf, size_t len);
|
||||
template <typename V> int putRaw(V *value);
|
||||
template <bool S> int putRaw(const char *buf, size_t len);
|
||||
template <bool S, typename V> int putRaw(V *value);
|
||||
|
||||
/**
|
||||
* @brief Adjusts the status argument according to the values of the
|
||||
|
||||
+24
-10
@@ -508,7 +508,9 @@ template <typename T> int mEpicsCa<T>::getRaw(char *buf, u_long len) {
|
||||
// put
|
||||
// =============================================================================
|
||||
|
||||
template <typename T> template <typename V> int mEpicsCa<T>::put(V *value) {
|
||||
template <typename T>
|
||||
template <bool S, typename V>
|
||||
int mEpicsCa<T>::put(V *value) {
|
||||
|
||||
// If "this" has the type mEpicsCa<uint16_t> (enum channel), then V needs to
|
||||
// be a string or an integer
|
||||
@@ -532,13 +534,15 @@ template <typename T> template <typename V> int mEpicsCa<T>::put(V *value) {
|
||||
|
||||
if constexpr (std::is_same_v<V, std::string>) {
|
||||
// Use the c-style string buffer put algorithm
|
||||
return putRaw(value->c_str(), value->size());
|
||||
return putRaw<S>(value->c_str(), value->size());
|
||||
} else {
|
||||
return putRaw(value);
|
||||
return putRaw<S>(value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::put(const char *buf, u_long len) {
|
||||
template <typename T>
|
||||
template <bool S>
|
||||
int mEpicsCa<T>::put(const char *buf, u_long len) {
|
||||
|
||||
// If "this" has the type mEpicsCa<uint16_t> (enum channel), then the type
|
||||
// check is omitted
|
||||
@@ -553,14 +557,16 @@ template <typename T> int mEpicsCa<T>::put(const char *buf, u_long len) {
|
||||
_chanName.c_str());
|
||||
return FE_ERR_DRIVER;
|
||||
}
|
||||
return putRaw(buf, len);
|
||||
return putRaw<S>(buf, len);
|
||||
}
|
||||
|
||||
template <typename T> template <typename V> int mEpicsCa<T>::putRaw(V *value) {
|
||||
template <typename T>
|
||||
template <bool S, typename V>
|
||||
int mEpicsCa<T>::putRaw(V *value) {
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_put(channelType<V>::dbr, this->_pChanID, value),
|
||||
this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
if (status != CM_SUCCESS || !S)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
@@ -569,7 +575,9 @@ template <typename T> template <typename V> int mEpicsCa<T>::putRaw(V *value) {
|
||||
this->_chanName.c_str());
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::putRaw(const char *buf, u_long len) {
|
||||
template <typename T>
|
||||
template <bool S>
|
||||
int mEpicsCa<T>::putRaw(const char *buf, u_long len) {
|
||||
|
||||
if constexpr (dbfFromType<T>() == DBF_ENUM) {
|
||||
// Check if the given string corresponds to one of the enum variants
|
||||
@@ -588,7 +596,7 @@ template <typename T> int mEpicsCa<T>::putRaw(const char *buf, u_long len) {
|
||||
variants += data.strs[i];
|
||||
|
||||
if (strcmp(data.strs[i], buf) == 0)
|
||||
return putRaw(&i);
|
||||
return putRaw<S>(&i);
|
||||
}
|
||||
|
||||
// Given string did not match any of the variants -> return an
|
||||
@@ -613,7 +621,7 @@ template <typename T> int mEpicsCa<T>::putRaw(const char *buf, u_long len) {
|
||||
this->_chanName.c_str());
|
||||
}
|
||||
|
||||
if (status != CM_SUCCESS)
|
||||
if (status != CM_SUCCESS || !S)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
@@ -623,6 +631,12 @@ template <typename T> int mEpicsCa<T>::putRaw(const char *buf, u_long len) {
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
template <typename T> int mEpicsCa<T>::flushIo() {
|
||||
return convertAndHandleEcaCode(ca_flush_io(), this->_chanName.c_str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void mEpicsCa<T>::connStateCallback(struct connection_handler_args args) {
|
||||
mEpicsCa<T> *self = static_cast<mEpicsCa<T> *>(ca_puser(args.chid));
|
||||
|
||||
Reference in New Issue
Block a user