@@ -7,6 +7,7 @@
|
||||
#include <menuAlarmStat.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief Holds information about the CA channel: Value type and element count.
|
||||
@@ -305,6 +306,10 @@ template <typename T> class mEpicsCa {
|
||||
*/
|
||||
std::optional<T> _cached;
|
||||
|
||||
// Vector holding the possible enum variants, if the record is an enum
|
||||
// record like e.g. bo. For all other record types, this is empty.
|
||||
std::vector<std::string> _enumVariants;
|
||||
|
||||
// Status and severity of the last read (or subscription callback)
|
||||
menuAlarmStat _status;
|
||||
menuAlarmSevr _severity;
|
||||
|
||||
+29
-14
@@ -171,7 +171,8 @@ template <typename T>
|
||||
mEpicsCa<T>::mEpicsCa(std::string_view chanName, bool subscribe, double timeout)
|
||||
: _timeout(timeout), _pChanID(nullptr), _chanName(chanName),
|
||||
_subscribe(subscribe), _eventId(nullptr), _cached(std::nullopt),
|
||||
_status(menuAlarmStatUDF), _severity(menuAlarmSevrNO_ALARM) {
|
||||
_enumVariants(std::vector<std::string>()), _status(menuAlarmStatUDF),
|
||||
_severity(menuAlarmSevrNO_ALARM) {
|
||||
|
||||
// Compile time check of T
|
||||
dbfFromType<T>();
|
||||
@@ -580,23 +581,36 @@ 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
|
||||
// and fetch the corresponding index, if it does. Otherwise, create
|
||||
// a nice error message.
|
||||
struct dbr_ctrl_enum data;
|
||||
ca_get(DBR_CTRL_ENUM, this->_pChanID, &data);
|
||||
ca_pend_io(_timeout);
|
||||
|
||||
// Prefetch all enum variants if the vector is currently empty. We
|
||||
// cannot do this at connection time, because this results in a segfault
|
||||
// (likely because we are trying to use ca_get while being inside a
|
||||
// callback).
|
||||
if (_enumVariants.empty()) {
|
||||
struct dbr_ctrl_enum data;
|
||||
ca_get(DBR_CTRL_ENUM, _pChanID, &data);
|
||||
ca_pend_io(_timeout);
|
||||
|
||||
for (int i = 0; i < data.no_str; ++i) {
|
||||
_enumVariants.push_back(data.strs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the given string matches one of the enum variants
|
||||
for (size_t i = 0; i < _enumVariants.size(); i++) {
|
||||
if (_enumVariants[i] == buf) {
|
||||
uint16_t idx = i;
|
||||
return putRaw<S>(&idx);
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't exit from the loop, the given buffer is not one of the
|
||||
// enum variants. Hence, we now create an error message (looping again).
|
||||
std::string variants;
|
||||
|
||||
for (int i = 0; i < data.no_str; ++i) {
|
||||
for (size_t i = 0; i < _enumVariants.size(); i++) {
|
||||
if (i > 0)
|
||||
variants += ", ";
|
||||
|
||||
variants += data.strs[i];
|
||||
|
||||
if (strcmp(data.strs[i], buf) == 0)
|
||||
return putRaw<S>(&i);
|
||||
variants += _enumVariants[i];
|
||||
}
|
||||
|
||||
// Given string did not match any of the variants -> return an
|
||||
@@ -692,6 +706,7 @@ void mEpicsCa<T>::connStateCallback(struct connection_handler_args args) {
|
||||
} else {
|
||||
// Any other event -> assume the channel is disconnected and reset the
|
||||
// internal fields
|
||||
self->_enumVariants.clear();
|
||||
self->_channelInfo.reset();
|
||||
self->_cached.reset();
|
||||
cm_msg(MDEBUG, __FILE__, "PV %s disconnected", self->_chanName.c_str());
|
||||
|
||||
Reference in New Issue
Block a user