summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-02-20 12:16:50 +0100
committerThomas Vanbesien <tvanbesi@proton.me>2026-02-20 12:16:50 +0100
commit690db0fa630ac57d0ec99010862c7b7e4a7ac589 (patch)
treeb2e847f9c3dddc71d18be03f0c26090d3939ec25 /src
parent5b93aac1f802d0db838d3d12802f2863eb13e0f8 (diff)
downloadBobinkQtOpcUa-690db0fa630ac57d0ec99010862c7b7e4a7ac589.tar.gz
BobinkQtOpcUa-690db0fa630ac57d0ec99010862c7b7e4a7ac589.zip
Implement OpcUaMonitoredNode attribute reading with OpcUaNodeInfo gadget
Diffstat (limited to 'src')
-rw-r--r--src/OpcUaMonitoredNode.cpp157
-rw-r--r--src/OpcUaMonitoredNode.h55
2 files changed, 212 insertions, 0 deletions
diff --git a/src/OpcUaMonitoredNode.cpp b/src/OpcUaMonitoredNode.cpp
index f7a5c9f..0ffe300 100644
--- a/src/OpcUaMonitoredNode.cpp
+++ b/src/OpcUaMonitoredNode.cpp
@@ -3,9 +3,17 @@
* @brief OpcUaMonitoredNode implementation.
*/
#include "OpcUaMonitoredNode.h"
+#include "OpcUaClient.h"
+
+#include <QMetaEnum>
+#include <QOpcUaLocalizedText>
OpcUaMonitoredNode::OpcUaMonitoredNode (QObject *parent) : QObject (parent) {}
+/* ======================================
+ * Properties
+ * ====================================== */
+
QString
OpcUaMonitoredNode::nodeId () const
{
@@ -19,6 +27,13 @@ OpcUaMonitoredNode::setNodeId (const QString &id)
return;
m_nodeId = id;
emit nodeIdChanged ();
+
+ if (!m_componentComplete)
+ return;
+
+ teardownNode ();
+ if (OpcUaClient::instance () && OpcUaClient::instance ()->connected ())
+ setupNode ();
}
bool
@@ -36,6 +51,22 @@ OpcUaMonitoredNode::setMonitored (bool monitored)
emit monitoredChanged ();
}
+QVariant
+OpcUaMonitoredNode::value () const
+{
+ return m_value;
+}
+
+OpcUaNodeInfo
+OpcUaMonitoredNode::info () const
+{
+ return m_info;
+}
+
+/* ======================================
+ * QQmlParserStatus
+ * ====================================== */
+
void
OpcUaMonitoredNode::classBegin ()
{
@@ -45,4 +76,130 @@ void
OpcUaMonitoredNode::componentComplete ()
{
m_componentComplete = true;
+
+ auto *client = OpcUaClient::instance ();
+ if (!client)
+ return;
+
+ connect (client, &OpcUaClient::connectedChanged, this,
+ &OpcUaMonitoredNode::handleClientConnectedChanged);
+
+ if (client->connected () && !m_nodeId.isEmpty ())
+ setupNode ();
+}
+
+/* ======================================
+ * Node lifecycle
+ * ====================================== */
+
+void
+OpcUaMonitoredNode::setupNode ()
+{
+ if (m_node || m_nodeId.isEmpty ())
+ return;
+
+ auto *client = OpcUaClient::instance ();
+ if (!client || !client->connected ())
+ return;
+
+ m_node = client->opcuaClient ()->node (m_nodeId);
+ if (!m_node)
+ return;
+
+ m_node->setParent (this);
+
+ connect (m_node, &QOpcUaNode::attributeUpdated, this,
+ &OpcUaMonitoredNode::handleAttributeUpdated);
+ connect (m_node, &QOpcUaNode::valueAttributeUpdated, this,
+ &OpcUaMonitoredNode::handleValueUpdated);
+
+ m_node->readAttributes (
+ QOpcUa::NodeAttribute::Value | QOpcUa::NodeAttribute::DisplayName
+ | QOpcUa::NodeAttribute::Description | QOpcUa::NodeAttribute::NodeClass
+ | QOpcUa::NodeAttribute::DataType | QOpcUa::NodeAttribute::AccessLevel);
+}
+
+void
+OpcUaMonitoredNode::teardownNode ()
+{
+ if (!m_node)
+ return;
+
+ delete m_node;
+ m_node = nullptr;
+
+ m_value.clear ();
+ emit valueChanged ();
+
+ m_info = OpcUaNodeInfo{};
+ emit infoChanged ();
+}
+
+/* ======================================
+ * Signal handlers
+ * ====================================== */
+
+void
+OpcUaMonitoredNode::handleAttributeUpdated (QOpcUa::NodeAttribute attr,
+ const QVariant &value)
+{
+ switch (attr)
+ {
+ case QOpcUa::NodeAttribute::DisplayName:
+ m_info.displayName = value.value<QOpcUaLocalizedText> ().text ();
+ break;
+ case QOpcUa::NodeAttribute::Description:
+ m_info.description = value.value<QOpcUaLocalizedText> ().text ();
+ break;
+ case QOpcUa::NodeAttribute::NodeClass:
+ {
+ auto me = QMetaEnum::fromType<QOpcUa::NodeClass> ();
+ auto nc = static_cast<QOpcUa::NodeClass> (value.toInt ());
+ const char *key = me.valueToKey (static_cast<int> (nc));
+ m_info.nodeClass
+ = key ? QLatin1StringView (key) : QStringLiteral ("Unknown");
+ }
+ break;
+ case QOpcUa::NodeAttribute::DataType:
+ m_info.dataType = value.toString ();
+ break;
+ case QOpcUa::NodeAttribute::AccessLevel:
+ m_info.accessLevel = value.toUInt ();
+ break;
+ default:
+ return;
+ }
+ emit infoChanged ();
+}
+
+void
+OpcUaMonitoredNode::handleValueUpdated (const QVariant &value)
+{
+ m_value = value;
+ emit valueChanged ();
+
+ m_info.status = QOpcUa::statusToString (m_node->valueAttributeError ());
+ m_info.sourceTimestamp
+ = m_node->sourceTimestamp (QOpcUa::NodeAttribute::Value);
+ m_info.serverTimestamp
+ = m_node->serverTimestamp (QOpcUa::NodeAttribute::Value);
+ emit infoChanged ();
+}
+
+void
+OpcUaMonitoredNode::handleClientConnectedChanged ()
+{
+ auto *client = OpcUaClient::instance ();
+ if (!client)
+ return;
+
+ if (client->connected ())
+ {
+ if (!m_nodeId.isEmpty ())
+ setupNode ();
+ }
+ else
+ {
+ teardownNode ();
+ }
}
diff --git a/src/OpcUaMonitoredNode.h b/src/OpcUaMonitoredNode.h
index 9d093b5..53e9537 100644
--- a/src/OpcUaMonitoredNode.h
+++ b/src/OpcUaMonitoredNode.h
@@ -8,9 +8,44 @@
#ifndef OPCUAMONITOREDNODE_H
#define OPCUAMONITOREDNODE_H
+#include <QDateTime>
#include <QObject>
+#include <QOpcUaNode>
#include <QQmlEngine>
#include <QQmlParserStatus>
+#include <QVariant>
+
+/**
+ * @brief Metadata bundle for a monitored OPC UA node.
+ *
+ * Exposed as a single Q_PROPERTY on OpcUaMonitoredNode so that
+ * the QML API stays simple (only `value` is top-level).
+ * Advanced users can access fields via dot notation:
+ * node.info.displayName, node.info.status, etc.
+ */
+struct OpcUaNodeInfo
+{
+ Q_GADGET
+ Q_PROPERTY (QString displayName MEMBER displayName)
+ Q_PROPERTY (QString description MEMBER description)
+ Q_PROPERTY (QString nodeClass MEMBER nodeClass)
+ Q_PROPERTY (QString dataType MEMBER dataType)
+ Q_PROPERTY (quint32 accessLevel MEMBER accessLevel)
+ Q_PROPERTY (QString status MEMBER status)
+ Q_PROPERTY (QDateTime sourceTimestamp MEMBER sourceTimestamp)
+ Q_PROPERTY (QDateTime serverTimestamp MEMBER serverTimestamp)
+
+public:
+ QString displayName;
+ QString description;
+ QString nodeClass;
+ QString dataType;
+ quint32 accessLevel = 0;
+ QString status;
+ QDateTime sourceTimestamp;
+ QDateTime serverTimestamp;
+};
+Q_DECLARE_METATYPE (OpcUaNodeInfo)
class OpcUaMonitoredNode : public QObject, public QQmlParserStatus
{
@@ -21,6 +56,8 @@ class OpcUaMonitoredNode : public QObject, public QQmlParserStatus
Q_PROPERTY (QString nodeId READ nodeId WRITE setNodeId NOTIFY nodeIdChanged)
Q_PROPERTY (
bool monitored READ monitored WRITE setMonitored NOTIFY monitoredChanged)
+ Q_PROPERTY (QVariant value READ value NOTIFY valueChanged)
+ Q_PROPERTY (OpcUaNodeInfo info READ info NOTIFY infoChanged)
public:
explicit OpcUaMonitoredNode (QObject *parent = nullptr);
@@ -31,17 +68,35 @@ public:
bool monitored () const;
void setMonitored (bool monitored);
+ QVariant value () const;
+ OpcUaNodeInfo info () const;
+
void classBegin () override;
void componentComplete () override;
signals:
void nodeIdChanged ();
void monitoredChanged ();
+ void valueChanged ();
+ void infoChanged ();
+
+private slots:
+ void handleAttributeUpdated (QOpcUa::NodeAttribute attr,
+ const QVariant &value);
+ void handleValueUpdated (const QVariant &value);
+ void handleClientConnectedChanged ();
private:
+ void setupNode ();
+ void teardownNode ();
+
QString m_nodeId;
bool m_monitored = true;
bool m_componentComplete = false;
+
+ QOpcUaNode *m_node = nullptr;
+ QVariant m_value;
+ OpcUaNodeInfo m_info;
};
#endif // OPCUAMONITOREDNODE_H