Added asynchronous put
CI / build-and-test (push) Successful in 5s

This commit is contained in:
2026-04-29 14:59:50 +02:00
parent abc57815eb
commit 00fa3aa595
3 changed files with 87 additions and 19 deletions
+20
View File
@@ -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
View File
@@ -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
View File
@@ -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));