From 9bb474703b6f4b59ec4333f145309d004c9c2375 Mon Sep 17 00:00:00 2001 From: Jannis Voelker Date: Mon, 25 Mar 2024 15:45:42 +0100 Subject: [PATCH] Add enum string handling for event fields --- src/languages/English_en_GB.ts | 198 ++++++++++++++++++--------------- src/languages/German_de_DE.ts | 198 ++++++++++++++++++--------------- src/monitoreditem.cpp | 82 +++++++++++++- src/monitoreditem.h | 3 + src/opcuahelper.cpp | 27 ++--- 5 files changed, 318 insertions(+), 190 deletions(-) diff --git a/src/languages/English_en_GB.ts b/src/languages/English_en_GB.ts index 1e2bde8..dc81886 100644 --- a/src/languages/English_en_GB.ts +++ b/src/languages/English_en_GB.ts @@ -98,108 +98,113 @@ Certificate - + Certificate - + Trust - + GetEndpoints failed using the discovery URL %1 returned from FindServers. Do you want to try the URL %2 with the hostname to discover FindServers? - + Connection to endpoint failed using the URL %1 returned from GetEndpoints. Do you want to try the URL %2 with the hostname to discover FindServers? - + The server certificate is unknown. - + + Common name - + Organization - + Organization unit - + Locality - + State - + Country - + + Valid from - + + Valid to - + + Fingerprint (SHA-256) - + + Serial number - + Do you want to trust the server certificate? - + Certificates @@ -209,87 +214,90 @@ - + + Recent connections - + + Discover - + Host - + Server - + Browse - + Endpoint - + Authentication - + Anonymous - + + Username - + Password - + Private key - + Disconnect - + Connect - + Close connection @@ -298,129 +306,131 @@ Dashboard - + Use last session - + Add data dashboard - + Add event dashboard - + Add alarms and conditions dashboard - + Select monitored variables manually - + Select events manually - + Default data dashboards - + Default event dashboards - + + Add dashboard - + Saved data dashboards - + + Saved event dashboards - + Dashboards - + Dashboard - + Event - + Add - + All selected event fields - + Saved variable dashboards - + Select event source nodes - + Select objects to create event monitored items for and then press OK - + Select event fields - + Select event fields to retrieve for events and then press OK @@ -429,31 +439,31 @@ ExpertMode - + Attributes - + References - + Reference - + Target - + Expert mode @@ -462,7 +472,7 @@ General - + Name @@ -471,7 +481,10 @@ - + + + + Cancel @@ -479,63 +492,67 @@ - + + + Ok - + + Back - + Delete - + Yes - + No - + Refresh - + + Settings - + Theme - + Dark - + Bright @@ -544,7 +561,10 @@ - + + + + MM/dd/yyyy @@ -554,37 +574,38 @@ - + + Imprint - + Germany - + Registration court - + Register number - + VAT-ID - + Executive Managers @@ -593,26 +614,27 @@ License - + Copyright - + + License - + This project is released under the GPLv3.0-or-later License. - + Libraries in use @@ -621,37 +643,37 @@ Logging - + Debug - + Warning - + Critical - + Fatal - + Info - + Log @@ -660,13 +682,13 @@ Settings - + Language - + Enter new dashboard name diff --git a/src/languages/German_de_DE.ts b/src/languages/German_de_DE.ts index 21906c5..fdb4435 100644 --- a/src/languages/German_de_DE.ts +++ b/src/languages/German_de_DE.ts @@ -98,108 +98,113 @@ Certificate - + Certificate Zertifikat - + Trust Vertrauen - + GetEndpoints failed using the discovery URL %1 returned from FindServers. Do you want to try the URL %2 with the hostname to discover FindServers? GetEndpoints ist mit der von FindServers zurückgegebenen Discovery-URL %1 fehlgeschlagen. Möchten Sie die URL %2 mit dem Hostnamen versuchen, um FindServers zu ermitteln? - + Connection to endpoint failed using the URL %1 returned from GetEndpoints. Do you want to try the URL %2 with the hostname to discover FindServers? Die Verbindung zum Endpunkt ist mit der von GetEndpoints zurückgegebenen URL %1 fehlgeschlagen. Möchten Sie die URL %2 mit dem Hostnamen versuchen, um FindServers zu ermitteln? - + The server certificate is unknown. Das Serverzertifikat ist unbekannt. - + + Common name Allgemeiner Name - + Organization Organisation - + Organization unit Organisationseinheit - + Locality Ort - + State Bundesland - + Country Land - + + Valid from Gültig von - + + Valid to Gültig bis - + + Fingerprint (SHA-256) Fingerprint (SHA-256) - + + Serial number Seriennummer - + Do you want to trust the server certificate? Möchten Sie dem Serverzertifikat vertrauen? - + Certificates Zertifikate @@ -209,87 +214,90 @@ - + + Recent connections Letzte Verbindungen - + + Discover Suchen - + Host Host - + Server Server - + Browse Durchsuchen - + Endpoint Endpunkt - + Authentication Authentifizierung - + Anonymous Anonym - + + Username Benutzername - + Password Passwort - + Private key Privater Schlüssel - + Disconnect Trennen - + Connect Verbinden - + Close connection Verbindung schließen @@ -298,129 +306,131 @@ Dashboard - + Use last session Letze Sitzung verwenden - + Add data dashboard Daten-Dashboard hinzufügen - + Add event dashboard Event-Dashboard hinzufügen - + Add alarms and conditions dashboard "Alarms-and-Conditions"-Dashboard hinzufügen - + Select monitored variables manually Überwachte Variablen manuell auswählen - + Select events manually Events manuell auswählen - + Default data dashboards Standard-Dashboards für Daten - + Default event dashboards Standard-Dashboards für Events - + + Add dashboard Dashboard hinzufügen - + Saved data dashboards Gespeicherte Daten-Dashboards - + + Saved event dashboards Gespeicherte Event-Dashboards - + Dashboards Dashboards - + Dashboard Dashboard - + Event Event - + Add Hinzufügen - + All selected event fields Alle ausgewählten Eventfelder - + Saved variable dashboards Gespeicherte Variablen-Dashboards - + Select event source nodes Quellknoten auswählen - + Select objects to create event monitored items for and then press OK Zu überwachende Objektknoten auswählen und dann OK drücken - + Select event fields Eventfelder auswählen - + Select event fields to retrieve for events and then press OK Für Events zu übermittelnde Felder auswählen und dann OK drücken @@ -429,31 +439,31 @@ ExpertMode - + Attributes Attribute - + References Referenzen - + Reference Referenz - + Target Ziel - + Expert mode Expertenmodus @@ -462,7 +472,7 @@ General - + Name Name @@ -471,7 +481,10 @@ - + + + + Cancel Abbruch @@ -479,63 +492,67 @@ - + + + Ok Ok - + + Back Zurück - + Delete Löschen - + Yes Ja - + No Nein - + Refresh Aktualisieren - + + Settings Einstellungen - + Theme Theme - + Dark Dunkel - + Bright Hell @@ -544,7 +561,10 @@ - + + + + MM/dd/yyyy dd.MM.yyyy @@ -554,37 +574,38 @@ - + + Imprint Impressum - + Germany Deutschland - + Registration court Registergericht - + Register number Registernummer - + VAT-ID USt.-ID - + Executive Managers Geschäftsführende Partner @@ -593,26 +614,27 @@ License - + Copyright Copyright - + + License Lizenz - + This project is released under the GPLv3.0-or-later License. Dieses Projekt ist unter der GPLv3.0-or-later Lizenz veröffentlicht. - + Libraries in use Verwendete Bibliotheken @@ -621,37 +643,37 @@ Logging - + Debug Debug - + Warning Warnung - + Critical Kritisch - + Fatal Fatal - + Info Info - + Log Log @@ -660,13 +682,13 @@ Settings - + Language Sprache - + Enter new dashboard name Neuen Dashboardnamen eingeben diff --git a/src/monitoreditem.cpp b/src/monitoreditem.cpp index acab40b..4f56948 100644 --- a/src/monitoreditem.cpp +++ b/src/monitoreditem.cpp @@ -5,6 +5,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +#include #include #include @@ -44,6 +45,8 @@ MonitoredItem::MonitoredItem(QOpcUaNode *node, mEventFieldNames.push_back({}); } + eventFieldTypeLookup(); + if (node != nullptr) { mNodeId = node->nodeId(); @@ -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; } } @@ -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 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 &targets, + const QList &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) { diff --git a/src/monitoreditem.h b/src/monitoreditem.h index c7e9f8b..2471041 100644 --- a/src/monitoreditem.h +++ b/src/monitoreditem.h @@ -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); @@ -65,6 +67,7 @@ private slots: Type mType; QStringList mEventFieldNames; + QStringList mEventFieldTypeNodeIds; QList mLastEvents; QOpcUaMonitoringParameters::EventFilter mEventFilter; }; diff --git a/src/opcuahelper.cpp b/src/opcuahelper.cpp index 75678cd..aee0214 100644 --- a/src/opcuahelper.cpp +++ b/src/opcuahelper.cpp @@ -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(); + 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().valueToKey(value.toInt()); return name ? QLatin1String(name) : QLatin1String("Unknown StatusCode"); @@ -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(); - 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