- Collapsed putRaw and getRaw into single, templated methods - Simplified readout of status and severity in callback.
This commit is contained in:
+6
-16
@@ -153,15 +153,15 @@ template <typename T> class mEpicsCa {
|
||||
double _timeout;
|
||||
|
||||
/**
|
||||
* @brief When using the subscription mechanism, this method returns the
|
||||
* cached value, if there is any.
|
||||
* @brief When using the subscription mechanism, this method returns a copy
|
||||
* of the cached value, if there is any.
|
||||
*
|
||||
* If the subscription mechanism is not used or the driver is not connected
|
||||
* to an EPICS channel, this returns `std::nullopt`.
|
||||
*
|
||||
* @return const std::optional<T>&
|
||||
* @return const std::optional<T>
|
||||
*/
|
||||
const std::optional<T> &cached() const;
|
||||
const std::optional<T> cached() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the last read value of the `STAT` field belonging to the
|
||||
@@ -208,22 +208,12 @@ template <typename T> class mEpicsCa {
|
||||
// The following methods hold the actual implementations of `mEpicsCa::get`
|
||||
// for different types of `T`.
|
||||
int getRaw(char *buf, size_t len);
|
||||
int getRaw(int *value);
|
||||
int getRaw(short *value);
|
||||
int getRaw(long *value);
|
||||
int getRaw(float *value);
|
||||
int getRaw(double *value);
|
||||
int getRaw(uint16_t *value);
|
||||
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);
|
||||
int putRaw(int *value);
|
||||
int putRaw(short *value);
|
||||
int putRaw(long *value);
|
||||
int putRaw(float *value);
|
||||
int putRaw(double *value);
|
||||
int putRaw(uint16_t *value);
|
||||
template <typename V> int putRaw(V *value);
|
||||
|
||||
/**
|
||||
* @brief Adjusts the status argument according to the values of the
|
||||
|
||||
+78
-255
@@ -11,59 +11,88 @@
|
||||
/**
|
||||
* @brief Struct for type checking the `T` of `mEpicsCa<T>`
|
||||
*
|
||||
* This struct is instantiated in the `dbfFromType` method and makes sure that
|
||||
* `T` (and `V` in case of `mEpicsCa::get` / `mEpicsCa::put`) can only be an
|
||||
* EPICS-compatible type. The `type` field maps `T` to the corresponding EPICS
|
||||
* type.
|
||||
* This struct is instantiated in the `dbfFromType` method and makes
|
||||
* sure that `T` (and `V` in case of `mEpicsCa::get` / `mEpicsCa::put`) can only
|
||||
* be an EPICS-compatible type. The `type` field maps `T` to the corresponding
|
||||
* EPICS type.
|
||||
*
|
||||
* @tparam T
|
||||
*/
|
||||
template <typename T> struct dbf_type;
|
||||
template <typename T> struct channelType;
|
||||
|
||||
template <> struct dbf_type<int> {
|
||||
template <> struct channelType<int> {
|
||||
// On modern systems, int is long
|
||||
static constexpr int type = DBF_LONG;
|
||||
static constexpr int dbf = DBF_LONG;
|
||||
static constexpr int dbr = DBR_LONG;
|
||||
static constexpr int dbr_time = DBR_TIME_LONG;
|
||||
using dbr_time_t = dbr_time_long;
|
||||
};
|
||||
|
||||
template <> struct dbf_type<short> {
|
||||
static constexpr int type = DBF_SHORT;
|
||||
template <> struct channelType<short> {
|
||||
static constexpr int dbf = DBF_SHORT;
|
||||
static constexpr int dbr = DBR_SHORT;
|
||||
static constexpr int dbr_time = DBR_TIME_SHORT;
|
||||
using dbr_time_t = dbr_time_short;
|
||||
};
|
||||
|
||||
template <> struct dbf_type<long> {
|
||||
static constexpr int type = DBF_LONG;
|
||||
template <> struct channelType<long> {
|
||||
static constexpr int dbf = DBF_LONG;
|
||||
static constexpr int dbr = DBR_LONG;
|
||||
static constexpr int dbr_time = DBR_TIME_LONG;
|
||||
using dbr_time_t = dbr_time_long;
|
||||
};
|
||||
|
||||
template <> struct dbf_type<float> {
|
||||
static constexpr int type = DBF_FLOAT;
|
||||
template <> struct channelType<float> {
|
||||
static constexpr int dbf = DBF_FLOAT;
|
||||
static constexpr int dbr = DBR_FLOAT;
|
||||
static constexpr int dbr_time = DBR_TIME_FLOAT;
|
||||
using dbr_time_t = dbr_time_float;
|
||||
};
|
||||
|
||||
template <> struct dbf_type<double> {
|
||||
static constexpr int type = DBF_DOUBLE;
|
||||
template <> struct channelType<double> {
|
||||
static constexpr int dbf = DBF_DOUBLE;
|
||||
static constexpr int dbr = DBR_DOUBLE;
|
||||
static constexpr int dbr_time = DBR_TIME_DOUBLE;
|
||||
using dbr_time_t = dbr_time_double;
|
||||
};
|
||||
|
||||
template <> struct dbf_type<char *> {
|
||||
static constexpr int type = DBF_STRING;
|
||||
template <> struct channelType<char *> {
|
||||
static constexpr int dbf = DBF_STRING;
|
||||
static constexpr int dbr = DBR_STRING;
|
||||
static constexpr int dbr_time = DBR_TIME_STRING;
|
||||
using dbr_time_t = dbr_time_string;
|
||||
};
|
||||
|
||||
template <> struct dbf_type<const char *> {
|
||||
static constexpr int type = DBF_STRING;
|
||||
template <> struct channelType<const char *> {
|
||||
static constexpr int dbf = DBF_STRING;
|
||||
static constexpr int dbr = DBR_STRING;
|
||||
static constexpr int dbr_time = DBR_TIME_STRING;
|
||||
using dbr_time_t = dbr_time_string;
|
||||
};
|
||||
|
||||
template <> struct dbf_type<std::string> {
|
||||
static constexpr int type = DBF_STRING;
|
||||
template <> struct channelType<std::string> {
|
||||
static constexpr int dbf = DBF_STRING;
|
||||
static constexpr int dbr = DBR_STRING;
|
||||
static constexpr int dbr_time = DBR_TIME_STRING;
|
||||
using dbr_time_t = dbr_time_string;
|
||||
};
|
||||
|
||||
template <> struct dbf_type<uint16_t> {
|
||||
static constexpr int type = DBF_ENUM;
|
||||
template <> struct channelType<uint16_t> {
|
||||
static constexpr int dbf = DBF_ENUM;
|
||||
static constexpr int dbr = DBR_ENUM;
|
||||
static constexpr int dbr_time = DBR_TIME_ENUM;
|
||||
using dbr_time_t = dbr_time_enum;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Returns the `type` field of `dbf_type<T>`
|
||||
* @brief Returns the `dbf` field of `channelType<T>`
|
||||
*
|
||||
* @tparam T
|
||||
* @return constexpr int
|
||||
*/
|
||||
template <typename T> constexpr int dbfFromType() { return dbf_type<T>::type; }
|
||||
template <typename T> constexpr int dbfFromType() {
|
||||
return channelType<T>::dbf;
|
||||
}
|
||||
|
||||
template <typename T, typename V> constexpr void assertEqual() {
|
||||
static_assert(dbfFromType<T>() == dbfFromType<V>());
|
||||
@@ -195,51 +224,18 @@ void mEpicsCa<T>::eventCallback(struct event_handler_args args) {
|
||||
if (args.status != ECA_NORMAL)
|
||||
return;
|
||||
|
||||
// Read out status and severity
|
||||
switch (args.type) {
|
||||
case DBR_TIME_STRING: {
|
||||
auto *v = static_cast<const dbr_time_string *>(args.dbr);
|
||||
self->_status = static_cast<menuAlarmStat>(v->status);
|
||||
self->_severity = static_cast<menuAlarmSevr>(v->severity);
|
||||
break;
|
||||
}
|
||||
// DBR_TIME_INT == DBR_TIME_SHORT
|
||||
case DBR_TIME_SHORT: {
|
||||
/*
|
||||
Independently of the value type (dbr_time_string, dbr_time_short,
|
||||
dbr_time_float, ...), status and severity are always the first two fields
|
||||
of the structs. This allows us to interpret args.dbr as any one of these
|
||||
types (because the memory layout at the start of the struct is always the
|
||||
same). However, we must not read any other fields of v! Therefore, the
|
||||
lifetime of v is aggressively scoped.
|
||||
*/
|
||||
{
|
||||
auto *v = static_cast<const dbr_time_short *>(args.dbr);
|
||||
self->_status = static_cast<menuAlarmStat>(v->status);
|
||||
self->_severity = static_cast<menuAlarmSevr>(v->severity);
|
||||
break;
|
||||
}
|
||||
case DBR_TIME_FLOAT: {
|
||||
auto *v = static_cast<const dbr_time_float *>(args.dbr);
|
||||
self->_status = static_cast<menuAlarmStat>(v->status);
|
||||
self->_severity = static_cast<menuAlarmSevr>(v->severity);
|
||||
break;
|
||||
}
|
||||
case DBR_TIME_ENUM: {
|
||||
auto *v = static_cast<const dbr_time_enum *>(args.dbr);
|
||||
self->_status = static_cast<menuAlarmStat>(v->status);
|
||||
self->_severity = static_cast<menuAlarmSevr>(v->severity);
|
||||
break;
|
||||
}
|
||||
case DBR_TIME_CHAR: {
|
||||
auto *v = static_cast<const dbr_time_char *>(args.dbr);
|
||||
self->_status = static_cast<menuAlarmStat>(v->status);
|
||||
self->_severity = static_cast<menuAlarmSevr>(v->severity);
|
||||
break;
|
||||
}
|
||||
case DBR_TIME_LONG: {
|
||||
auto *v = static_cast<const dbr_time_long *>(args.dbr);
|
||||
self->_status = static_cast<menuAlarmStat>(v->status);
|
||||
self->_severity = static_cast<menuAlarmSevr>(v->severity);
|
||||
break;
|
||||
}
|
||||
case DBR_TIME_DOUBLE: {
|
||||
auto *v = static_cast<const dbr_time_double *>(args.dbr);
|
||||
self->_status = static_cast<menuAlarmStat>(v->status);
|
||||
self->_severity = static_cast<menuAlarmSevr>(v->severity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Read out the actual value and cache it.
|
||||
@@ -267,7 +263,7 @@ void mEpicsCa<T>::eventCallback(struct event_handler_args args) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> const std::optional<T> &mEpicsCa<T>::cached() const {
|
||||
template <typename T> const std::optional<T> mEpicsCa<T>::cached() const {
|
||||
return _cached;
|
||||
}
|
||||
|
||||
@@ -294,7 +290,8 @@ template <typename T> template <typename V> int mEpicsCa<T>::get(V *value) {
|
||||
(t == DBF_STRING && v == DBF_ENUM),
|
||||
"Incompatible EPICS types");
|
||||
} else {
|
||||
// Assert T and V are compatible (dbfFromType<T> == dbfFromType<V>)
|
||||
// Assert T and V are compatible (dbfFromType<T> ==
|
||||
// dbfFromType<V>)
|
||||
assertEqual<T, V>();
|
||||
}
|
||||
|
||||
@@ -367,144 +364,29 @@ template <typename T> int mEpicsCa<T>::get(char *buf, u_long len) {
|
||||
return getRaw(buf, len);
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::getRaw(int *value) {
|
||||
dbr_time_short dbr = {};
|
||||
template <typename T> template <typename V> int mEpicsCa<T>::getRaw(V *value) {
|
||||
using traits = channelType<V>;
|
||||
using dbr_t = typename traits::dbr_time_t;
|
||||
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_get(DBR_TIME_INT, this->_pChanID, &dbr), this->_chanName.c_str());
|
||||
dbr_t dbr{};
|
||||
|
||||
int status =
|
||||
convertAndHandleEcaCode(ca_get(traits::dbr_time, this->_pChanID, &dbr),
|
||||
this->_chanName.c_str());
|
||||
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
status =
|
||||
convertAndHandleEcaCode(ca_pend_io(_timeout), this->_chanName.c_str());
|
||||
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// Interpret answer AFTER caget actually populated dbr (before that, it is
|
||||
// uninitialized).
|
||||
*value = dbr.value;
|
||||
_status = static_cast<menuAlarmStat>(dbr.status);
|
||||
_severity = static_cast<menuAlarmSevr>(dbr.severity);
|
||||
return processStatAndSevr(status);
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::getRaw(short *value) {
|
||||
dbr_time_short dbr = {};
|
||||
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_get(DBR_TIME_SHORT, this->_pChanID, &dbr), this->_chanName.c_str());
|
||||
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
status =
|
||||
convertAndHandleEcaCode(ca_pend_io(_timeout), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// Interpret answer AFTER caget actually populated dbr (before that, it is
|
||||
// uninitialized).
|
||||
*value = dbr.value;
|
||||
_status = static_cast<menuAlarmStat>(dbr.status);
|
||||
_severity = static_cast<menuAlarmSevr>(dbr.severity);
|
||||
return processStatAndSevr(status);
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::getRaw(long *value) {
|
||||
dbr_time_long dbr = {};
|
||||
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_get(DBR_TIME_LONG, this->_pChanID, &dbr), this->_chanName.c_str());
|
||||
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
status =
|
||||
convertAndHandleEcaCode(ca_pend_io(_timeout), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// Interpret answer AFTER caget actually populated dbr (before that, it is
|
||||
// uninitialized).
|
||||
*value = dbr.value;
|
||||
_status = static_cast<menuAlarmStat>(dbr.status);
|
||||
_severity = static_cast<menuAlarmSevr>(dbr.severity);
|
||||
return processStatAndSevr(status);
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::getRaw(float *value) {
|
||||
dbr_time_float dbr = {};
|
||||
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_get(DBR_TIME_FLOAT, this->_pChanID, &dbr), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
status =
|
||||
convertAndHandleEcaCode(ca_pend_io(_timeout), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// Interpret answer AFTER caget actually populated dbr (before that, it is
|
||||
// uninitialized).
|
||||
*value = dbr.value;
|
||||
_status = static_cast<menuAlarmStat>(dbr.status);
|
||||
_severity = static_cast<menuAlarmSevr>(dbr.severity);
|
||||
return processStatAndSevr(status);
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::getRaw(double *value) {
|
||||
dbr_time_double dbr = {};
|
||||
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_get(DBR_TIME_DOUBLE, this->_pChanID, &dbr), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
status =
|
||||
convertAndHandleEcaCode(ca_pend_io(_timeout), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// Interpret answer AFTER caget actually populated dbr (before that, it is
|
||||
// uninitialized).
|
||||
*value = dbr.value;
|
||||
_status = static_cast<menuAlarmStat>(dbr.status);
|
||||
_severity = static_cast<menuAlarmSevr>(dbr.severity);
|
||||
return processStatAndSevr(status);
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::getRaw(uint16_t *value) {
|
||||
dbr_time_enum dbr = {};
|
||||
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_get(DBR_TIME_ENUM, this->_pChanID, &dbr), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
status =
|
||||
convertAndHandleEcaCode(ca_pend_io(_timeout), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// Interpret answer AFTER caget actually populated dbr (before that, it is
|
||||
// uninitialized).
|
||||
*value = dbr.value;
|
||||
_status = static_cast<menuAlarmStat>(dbr.status);
|
||||
_severity = static_cast<menuAlarmSevr>(dbr.severity);
|
||||
return processStatAndSevr(status);
|
||||
}
|
||||
|
||||
@@ -624,69 +506,10 @@ template <typename T> int mEpicsCa<T>::put(const char *buf, u_long len) {
|
||||
return putRaw(buf, len);
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::putRaw(int *value) {
|
||||
int status = convertAndHandleEcaCode(ca_put(DBR_INT, this->_pChanID, value),
|
||||
this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
return convertAndHandleEcaCode(ca_pend_io(_timeout),
|
||||
this->_chanName.c_str());
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::putRaw(short *value) {
|
||||
template <typename T> template <typename V> int mEpicsCa<T>::putRaw(V *value) {
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_put(DBR_SHORT, this->_pChanID, value), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
return convertAndHandleEcaCode(ca_pend_io(_timeout),
|
||||
this->_chanName.c_str());
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::putRaw(long *value) {
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_put(DBR_LONG, this->_pChanID, value), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
return convertAndHandleEcaCode(ca_pend_io(_timeout),
|
||||
this->_chanName.c_str());
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::putRaw(float *value) {
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_put(DBR_FLOAT, this->_pChanID, value), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
return convertAndHandleEcaCode(ca_pend_io(_timeout),
|
||||
this->_chanName.c_str());
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::putRaw(double *value) {
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_put(DBR_DOUBLE, this->_pChanID, value), this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
// caget is asynchronous, so the pointer value is only updated when
|
||||
// ca_pend_io returns ECA_NORMAL
|
||||
return convertAndHandleEcaCode(ca_pend_io(_timeout),
|
||||
this->_chanName.c_str());
|
||||
}
|
||||
|
||||
template <typename T> int mEpicsCa<T>::putRaw(u_int16_t *value) {
|
||||
int status = convertAndHandleEcaCode(
|
||||
ca_put(DBR_ENUM, this->_pChanID, value), this->_chanName.c_str());
|
||||
ca_put(channelType<V>::dbr, this->_pChanID, value),
|
||||
this->_chanName.c_str());
|
||||
if (status != CM_SUCCESS)
|
||||
return status;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user