Skip to content

Commit

Permalink
Add enum string handling for event fields
Browse files Browse the repository at this point in the history
  • Loading branch information
basyskom-jvoe committed Mar 26, 2024
1 parent 977b69b commit 8ece400
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 14 deletions.
82 changes: 81 additions & 1 deletion src/monitoreditem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/

#include <QOpcUaClient>
#include <QOpcUaNode>
#include <QOpcUaQualifiedName>

Expand Down Expand Up @@ -44,6 +45,8 @@ MonitoredItem::MonitoredItem(QOpcUaNode *node,
mEventFieldNames.push_back({});
}

eventFieldTypeLookup();

if (node != nullptr) {
mNodeId = node->nodeId();

Expand Down Expand Up @@ -134,7 +137,9 @@ void MonitoredItem::handleEvent(const QVariantList &eventFields)

for (int i = 0; i < eventFields.length(); ++i) {
if (!eventFields.at(i).isNull()) {
const auto stringValue = QOpcUaHelper::variantToString(nullptr, eventFields.at(i), {});

const auto stringValue = QOpcUaHelper::variantToString(
mOpcNode.get(), eventFields.at(i), mEventFieldTypeNodeIds.at(i));
eventStrings[mEventFieldNames.at(i)] = stringValue;
}
}
Expand All @@ -146,6 +151,81 @@ void MonitoredItem::handleEvent(const QVariantList &eventFields)
emit lastEventsChanged();
}

void MonitoredItem::eventFieldTypeLookup()
{
if (mType != Type::Event || !mOpcNode)
return;

if (mEventFieldNames.isEmpty())
return;

mEventFieldTypeNodeIds = QStringList(mEventFieldNames.size(), {});

for (int i = 0; i < mEventFilter.selectClauses().size(); ++i) {
if (mEventFilter.selectClauses().at(i).browsePath().isEmpty())
continue;

auto eventTypeId = mEventFilter.selectClauses().at(i).typeId();
if (eventTypeId.isEmpty())
eventTypeId = QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::BaseEventType);

auto typeNode = mOpcNode->client()->node(eventTypeId);

if (!typeNode)
continue;

QList<QOpcUaRelativePathElement> pathElements;
for (const auto &element : mEventFilter.selectClauses().at(i).browsePath()) {
QOpcUaRelativePathElement pathElement(
QOpcUaQualifiedName(element.namespaceIndex(), element.name()),
QOpcUa::ReferenceTypeId::HierarchicalReferences);
pathElement.setIncludeSubtypes(true);
pathElements.push_back(pathElement);
}

QObject::connect(
typeNode, &QOpcUaNode::resolveBrowsePathFinished, this,
[this, typeNode, i](const QList<QOpcUaBrowsePathTarget> &targets,
const QList<QOpcUaRelativePathElement> &path,
QOpcUa::UaStatusCode statusCode) {
Q_UNUSED(path)
Q_UNUSED(statusCode)

if (!targets.isEmpty() && targets.constFirst().isFullyResolved()
&& !targets.constFirst().targetId().nodeId().isEmpty()) {
auto fieldNode =
typeNode->client()->node(targets.constFirst().targetId().nodeId());

typeNode->deleteLater();

if (!fieldNode)
return;

QObject::connect(
fieldNode, &QOpcUaNode::attributeRead, this,
[this, fieldNode, i](const QOpcUa::NodeAttributes &attributes) {
if (attributes & QOpcUa::NodeAttribute::DataType)
mEventFieldTypeNodeIds[i] =
fieldNode
->attribute(QOpcUa::NodeAttribute::DataType)
.toString();

fieldNode->deleteLater();
});

const auto success =
fieldNode->readAttributes(QOpcUa::NodeAttribute::DataType);
if (!success)
fieldNode->deleteLater();
}
});

const auto success = typeNode->resolveBrowsePath(pathElements);
if (!success)
typeNode->deleteLater();
}
}

void MonitoredItem::setStatusCode(QOpcUa::UaStatusCode statusCode)
{
if (mStatusCode != statusCode) {
Expand Down
3 changes: 3 additions & 0 deletions src/monitoreditem.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ private slots:
void handleAttributes(const QOpcUa::NodeAttributes &attributes);
void handleEvent(const QVariantList &eventFields);

void eventFieldTypeLookup();

private:
void setStatusCode(QOpcUa::UaStatusCode statusCode);

Expand All @@ -65,6 +67,7 @@ private slots:
Type mType;

QStringList mEventFieldNames;
QStringList mEventFieldTypeNodeIds;
QList<QVariantMap> mLastEvents;
QOpcUaMonitoringParameters::EventFilter mEventFilter;
};
Expand Down
27 changes: 14 additions & 13 deletions src/opcuahelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,20 @@ QString QOpcUaHelper::variantToString(QOpcUaNode *node, const QVariant &value,
return concat;
}

// Handle enum strings
if (!typeNodeId.isEmpty()) {
const auto model = BackEnd::getOpcUaModelForNode(node);
if (model) {
const auto enumCandidate = model->getEnumStringsForDataTypeId(typeNodeId);
if (!enumCandidate.isEmpty()) {
const auto numericValue = value.value<qint32>();
const auto entry = enumCandidate.constFind(numericValue);
if (entry != enumCandidate.constEnd())
return QStringLiteral("%1 (%2)").arg(numericValue).arg(entry.value());
}
}
}

if (typeNodeId == QLatin1String("ns=0;i=19")) { // StatusCode
const char *name = QMetaEnum::fromType<QOpcUa::UaStatusCode>().valueToKey(value.toInt());
return name ? QLatin1String(name) : QLatin1String("Unknown StatusCode");
Expand Down Expand Up @@ -572,19 +586,6 @@ QString QOpcUaHelper::getRawAttributeValue(QOpcUaNode *node, QOpcUa::NodeAttribu
const QVariant attrValue = node->attribute(attr);
const auto valueString = variantToString(node, attrValue, type);

if (!valueString.isEmpty()) {
const auto model = BackEnd::getOpcUaModelForNode(node);
if (model) {
const auto enumCandidate = model->getEnumStringsForDataTypeId(type);
if (!enumCandidate.isEmpty()) {
const auto value = node->valueAttribute().value<qint32>();
const auto entry = enumCandidate.constFind(value);
if (entry != enumCandidate.constEnd())
return QStringLiteral("%1 (%2)").arg(valueString, entry.value());
}
}
}

return valueString;
}
#ifdef HAS_GENERIC_STRUCT_HANDLER
Expand Down

0 comments on commit 8ece400

Please sign in to comment.