Skip to content

Commit

Permalink
improve enum handling
Browse files Browse the repository at this point in the history
Read enum type info with strings at connect
Identify enum types during intial read and assign string maps
Update enum strings in mbbi/mbbo records at intial read
Convert enums to and from strings for string records and char waveforms
  • Loading branch information
dirk-zimoch committed Oct 22, 2024
1 parent 00f58a3 commit 7717f13
Show file tree
Hide file tree
Showing 13 changed files with 658 additions and 315 deletions.
4 changes: 4 additions & 0 deletions devOpcuaSup/DataElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef DEVOPCUA_DATAELEMENT_H
#define DEVOPCUA_DATAELEMENT_H

#include <map>
#include <vector>
#include <memory>
#include <cstring>
Expand All @@ -24,6 +25,8 @@

namespace DevOpcua {

typedef std::map<epicsUInt32, std::string> EnumChoices;

class RecordConnector;

/**
Expand Down Expand Up @@ -795,6 +798,7 @@ class DataElement
virtual void requestRecordProcessing(const ProcessReason reason) const = 0;

const std::string name; /**< element name */
const EnumChoices* enumChoices = nullptr; /**< enum definition if this element is an enum */

protected:
/**
Expand Down
152 changes: 93 additions & 59 deletions devOpcuaSup/UaSdk/DataElementUaSdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <uaarraytemplates.h>
#include <opcua_builtintypes.h>
#include <statuscode.h>
#include <uaenumdefinition.h>

#include <errlog.h>
#include <epicsTime.h>
Expand Down Expand Up @@ -110,12 +111,17 @@ DataElementUaSdk::show (const int level, const unsigned int indent) const
void
DataElementUaSdk::setIncomingData(const UaVariant &value,
ProcessReason reason,
const std::string *timefrom)
const std::string *timefrom,
const UaNodeId *typeId)
{
// Make a copy of this element and cache it
incomingData = value;

if (isLeaf()) {
if (typeId && pconnector->state() == ConnectionStatus::initialRead) {
delete enumChoices;
enumChoices = pitem->session->getEnumChoices(typeId);
}
if ((pconnector->state() == ConnectionStatus::initialRead
&& (reason == ProcessReason::readComplete || reason == ProcessReason::readFailure))
|| (pconnector->state() == ConnectionStatus::up)) {
Expand Down Expand Up @@ -183,6 +189,8 @@ DataElementUaSdk::setIncomingData(const UaVariant &value,
for (int i = 0; i < definition.childrenCount(); i++) {
if (pelem->name == definition.child(i).name().toUtf8()) {
elementMap.insert({i, it});
delete pelem->enumChoices;
pelem->enumChoices = pitem->session->getEnumChoices(definition.child(i).enumDefinition());
pelem->setIncomingData(genericValue.value(i), reason);
}
}
Expand Down Expand Up @@ -462,6 +470,7 @@ DataElementUaSdk::readScalar (char *value, const size_t num,
ProcessReason nReason;
std::shared_ptr<UpdateUaSdk> upd = incomingQueue.popUpdate(&nReason);
dbgReadScalar(upd.get(), "CString", num);
prec->udf = false;

switch (upd->getType()) {
case ProcessReason::readFailure:
Expand All @@ -486,9 +495,20 @@ DataElementUaSdk::readScalar (char *value, const size_t num,
if (OpcUa_IsUncertain(stat)) {
(void) recGblSetSevr(prec, READ_ALARM, MINOR_ALARM);
}
strncpy(value, upd->getData().toString().toUtf8(), num);
UaVariant& data = upd->getData();
if (enumChoices) {
OpcUa_Int32 enumIndex;
data.toInt32(enumIndex);
auto it = enumChoices->find(enumIndex);
if (it != enumChoices->end() && !it->second.empty()) {
strncpy(value, it->second.c_str(), num);
} else {
strncpy(value, data.toString().toUtf8(), num);
}
} else {
strncpy(value, data.toString().toUtf8(), num);
}
value[num-1] = '\0';
prec->udf = false;
}
if (statusCode) *statusCode = stat;
if (statusText) {
Expand Down Expand Up @@ -560,6 +580,7 @@ DataElementUaSdk::readArray (char *value, const epicsUInt32 len,
ProcessReason nReason;
std::shared_ptr<UpdateUaSdk> upd = incomingQueue.popUpdate(&nReason);
dbgReadArray(upd.get(), num, epicsTypeString(value));
prec->udf = false;

switch (upd->getType()) {
case ProcessReason::readFailure:
Expand Down Expand Up @@ -602,7 +623,6 @@ DataElementUaSdk::readArray (char *value, const epicsUInt32 len,
strncpy(value + i * len, UaString(arr[i]).toUtf8(), len);
(value + i * len)[len - 1] = '\0';
}
prec->udf = false;
}
}
if (statusCode) *statusCode = stat;
Expand Down Expand Up @@ -650,6 +670,7 @@ DataElementUaSdk::readArray<epicsUInt8, UaByteArray> (epicsUInt8 *value, const e
ProcessReason nReason;
std::shared_ptr<UpdateUaSdk> upd = incomingQueue.popUpdate(&nReason);
dbgReadArray(upd.get(), num, epicsTypeString(*value));
prec->udf = false;

switch (upd->getType()) {
case ProcessReason::readFailure:
Expand Down Expand Up @@ -690,7 +711,6 @@ DataElementUaSdk::readArray<epicsUInt8, UaByteArray> (epicsUInt8 *value, const e
elemsWritten = static_cast<epicsUInt32>(arr.size());
if (num < elemsWritten) elemsWritten = num;
memcpy(value, arr.data(), sizeof(epicsUInt8) * elemsWritten);
prec->udf = false;
}
}
if (statusCode) *statusCode = stat;
Expand Down Expand Up @@ -887,17 +907,19 @@ DataElementUaSdk::writeScalar (const epicsFloat64 &value, dbCommon *prec)
long
DataElementUaSdk::writeScalar (const char *value, const epicsUInt32 len, dbCommon *prec)
{
long ret = 0;
long ret = 1;
long l;
unsigned long ul;
double d;
char* end = nullptr;

switch (incomingData.type()) {
case OpcUaType_String:
{ // Scope of Guard G
Guard G(outgoingLock);
outgoingData.setString(static_cast<UaString>(value));
markAsDirty();
ret = 0;
break;
}
case OpcUaType_LocalizedText:
Expand All @@ -908,6 +930,7 @@ DataElementUaSdk::writeScalar (const char *value, const epicsUInt32 len, dbCommo
localizedText.setText(static_cast<UaString>(value)); // keep the Locale
outgoingData.setLocalizedText(localizedText);
markAsDirty();
ret = 0;
break;
}
case OpcUaType_Boolean:
Expand All @@ -918,122 +941,133 @@ DataElementUaSdk::writeScalar (const char *value, const epicsUInt32 len, dbCommo
else
outgoingData.setBoolean(false);
markAsDirty();
ret = 0;
break;
}
case OpcUaType_Byte:
ul = strtoul(value, nullptr, 0);
if (isWithinRange<OpcUa_Byte>(ul)) {
ul = strtoul(value, &end, 0);
if (end != value && isWithinRange<OpcUa_Byte>(ul)) {
Guard G(outgoingLock);
outgoingData.setByte(static_cast<OpcUa_Byte>(ul));
markAsDirty();
} else {
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
ret = 1;
ret = 0;
}
break;
case OpcUaType_SByte:
l = strtol(value, nullptr, 0);
if (isWithinRange<OpcUa_SByte>(l)) {
l = strtol(value, &end, 0);
if (end != value && isWithinRange<OpcUa_SByte>(l)) {
Guard G(outgoingLock);
outgoingData.setSByte(static_cast<OpcUa_SByte>(l));
markAsDirty();
} else {
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
ret = 1;
ret = 0;
}
break;
case OpcUaType_UInt16:
ul = strtoul(value, nullptr, 0);
if (isWithinRange<OpcUa_UInt16>(ul)) {
ul = strtoul(value, &end, 0);
if (end != value && isWithinRange<OpcUa_UInt16>(ul)) {
Guard G(outgoingLock);
outgoingData.setUInt16(static_cast<OpcUa_UInt16>(ul));
markAsDirty();
} else {
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
ret = 1;
ret = 0;
}
break;
case OpcUaType_Int16:
l = strtol(value, nullptr, 0);
if (isWithinRange<OpcUa_Int16>(l)) {
l = strtol(value, &end, 0);
if (end != value && isWithinRange<OpcUa_Int16>(l)) {
Guard G(outgoingLock);
outgoingData.setInt16(static_cast<OpcUa_Int16>(l));
markAsDirty();
} else {
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
ret = 1;
ret = 0;
}
break;
case OpcUaType_UInt32:
ul = strtoul(value, nullptr, 0);
if (isWithinRange<OpcUa_UInt32>(ul)) {
ul = strtoul(value, &end, 0);
if (end != value && isWithinRange<OpcUa_UInt32>(ul)) {
Guard G(outgoingLock);
outgoingData.setUInt32(static_cast<OpcUa_UInt32>(ul));
markAsDirty();
} else {
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
ret = 1;
ret = 0;
}
break;
case OpcUaType_Int32:
l = strtol(value, nullptr, 0);
if (isWithinRange<OpcUa_Int32>(l)) {
l = strtol(value, &end, 0);
if (enumChoices) {
// first test enum strings then numeric values
// in case a string starts with a number but means a different value
for (auto it: *enumChoices)
if (it.second == value) {
l = static_cast<long>(it.first);
ret = 0;
end = nullptr;
break;
}
if (ret != 0 && end != value)
for (auto it: *enumChoices)
if (l == it.first) {
ret = 0;
break;
}
if (ret != 0)
break;
}
if (end != value && isWithinRange<OpcUa_Int32>(l)) {
Guard G(outgoingLock);
outgoingData.setInt32(static_cast<OpcUa_Int32>(l));
markAsDirty();
} else {
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
ret = 1;
ret = 0;
}
break;
case OpcUaType_UInt64:
ul = strtoul(value, nullptr, 0);
if (isWithinRange<OpcUa_UInt64>(ul)) {
ul = strtoul(value, &end, 0);
if (end != value && isWithinRange<OpcUa_UInt64>(ul)) {
Guard G(outgoingLock);
outgoingData.setUInt64(static_cast<OpcUa_UInt64>(ul));
markAsDirty();
} else {
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
ret = 1;
ret = 0;
}
break;
case OpcUaType_Int64:
l = strtol(value, nullptr, 0);
if (isWithinRange<OpcUa_Int64>(l)) {
l = strtol(value, &end, 0);
if (end != value && isWithinRange<OpcUa_Int64>(l)) {
Guard G(outgoingLock);
outgoingData.setInt64(static_cast<OpcUa_Int64>(l));
markAsDirty();
} else {
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
ret = 1;
ret = 0;
}
break;
case OpcUaType_Float:
d = strtod(value, nullptr);
if (isWithinRange<OpcUa_Float>(d)) {
d = strtod(value, &end);
if (end != value && isWithinRange<OpcUa_Float>(d)) {
Guard G(outgoingLock);
outgoingData.setFloat(static_cast<OpcUa_Float>(d));
markAsDirty();
} else {
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
ret = 1;
ret = 0;
}
break;
case OpcUaType_Double:
{
d = strtod(value, nullptr);
Guard G(outgoingLock);
outgoingData.setDouble(static_cast<OpcUa_Double>(d));
markAsDirty();
d = strtod(value, &end);
if (end != value) {
Guard G(outgoingLock);
outgoingData.setDouble(static_cast<OpcUa_Double>(d));
markAsDirty();
ret = 0;
}
break;
}
default:
errlogPrintf("%s : unsupported conversion for outgoing data\n",
prec->name);
errlogPrintf("%s : unsupported conversion from string to %s for outgoing data\n",
prec->name, variantTypeString(incomingData.type()));
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
return -1;
}

dbgWriteScalar();
if (ret != 0) {
errlogPrintf("%s : value \"%s\" out of range\n",
prec->name, value);
(void) recGblSetSevr(prec, WRITE_ALARM, INVALID_ALARM);
}
if (ret == 0)
dbgWriteScalar();
return ret;
}

Expand Down
Loading

0 comments on commit 7717f13

Please sign in to comment.