summaryrefslogtreecommitdiffstats
path: root/src/plugins/opcua/open62541
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/opcua/open62541')
-rw-r--r--src/plugins/opcua/open62541/qopen62541backend.cpp203
-rw-r--r--src/plugins/opcua/open62541/qopen62541backend.h31
-rw-r--r--src/plugins/opcua/open62541/qopen62541client.cpp8
-rw-r--r--src/plugins/opcua/open62541/qopen62541client.h1
-rw-r--r--src/plugins/opcua/open62541/qopen62541node.cpp28
-rw-r--r--src/plugins/opcua/open62541/qopen62541node.h3
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.cpp295
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.h56
8 files changed, 474 insertions, 151 deletions
diff --git a/src/plugins/opcua/open62541/qopen62541backend.cpp b/src/plugins/opcua/open62541/qopen62541backend.cpp
index 4411a04..7c5226f 100644
--- a/src/plugins/opcua/open62541/qopen62541backend.cpp
+++ b/src/plugins/opcua/open62541/qopen62541backend.cpp
@@ -60,10 +60,20 @@ struct UaLocalizedTextMemberDeleter
Open62541AsyncBackend::Open62541AsyncBackend(QOpen62541Client *parent)
: QOpcUaBackend()
- , m_clientImpl(parent)
, m_uaclient(nullptr)
- , m_subscriptionTimer(nullptr)
+ , m_clientImpl(parent)
+ , m_subscriptionTimer(this)
+ , m_sendPublishRequests(false)
+ , m_shortestInterval(std::numeric_limits<qint32>::max())
+{
+ m_subscriptionTimer.setSingleShot(true);
+ QObject::connect(&m_subscriptionTimer, &QTimer::timeout,
+ this, &Open62541AsyncBackend::sendPublishRequest);
+}
+
+Open62541AsyncBackend::~Open62541AsyncBackend()
{
+ qDeleteAll(m_subscriptions);
}
void Open62541AsyncBackend::readAttributes(uintptr_t handle, UA_NodeId id, QOpcUaNode::NodeAttributes attr)
@@ -155,6 +165,109 @@ void Open62541AsyncBackend::writeAttributes(uintptr_t handle, UA_NodeId id, QOpc
UA_NodeId_deleteMembers(&id);
}
+void Open62541AsyncBackend::enableMonitoring(uintptr_t handle, UA_NodeId id, QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
+{
+ QOpen62541Subscription *usedSubscription = nullptr;
+
+ // Create a new subscription if necessary
+ if (settings.subscriptionId()) {
+ auto sub = m_subscriptions.find(settings.subscriptionId());
+ if (sub == m_subscriptions.end()) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "There is no subscription with id %u", settings.subscriptionId());
+
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attribute){
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadSubscriptionIdInvalid);
+ emit monitoringEnableDisable(handle, attribute, true, s);
+ });
+ return;
+ }
+ usedSubscription = sub.value(); // Ignore interval != subscription.interval
+ } else {
+ usedSubscription = getSubscription(settings);
+ }
+
+ if (!usedSubscription) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not create subscription with interval %f", settings.publishingInterval());
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attribute){
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadSubscriptionIdInvalid);
+ emit monitoringEnableDisable(handle, attribute, true, s);
+ });
+ return;
+ }
+
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attribute){
+ bool success = usedSubscription->addAttributeMonitoredItem(handle, attribute, id, settings);
+ if (success)
+ m_attributeMapping[handle][attribute] = usedSubscription;
+ });
+
+ if (usedSubscription->monitoredItemsCount() == 0)
+ removeSubscription(usedSubscription->subscriptionId()); // No items were added
+
+ modifyPublishRequests();
+}
+
+void Open62541AsyncBackend::disableMonitoring(uintptr_t handle, QOpcUaNode::NodeAttributes attr)
+{
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attribute){
+ QOpen62541Subscription *sub = getSubscriptionForItem(handle, attribute);
+ if (sub) {
+ sub->removeAttributeMonitoredItem(handle, attribute);
+ if (sub->monitoredItemsCount() == 0)
+ removeSubscription(sub->subscriptionId());
+ }
+ });
+ modifyPublishRequests();
+}
+
+void Open62541AsyncBackend::modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value)
+{
+ QOpen62541Subscription *subscription = getSubscriptionForItem(handle, attr);
+ if (!subscription) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify parameter for %lu, the monitored item does not exist", handle);
+ QOpcUaMonitoringParameters p;
+ p.setStatusCode(QOpcUa::UaStatusCode::BadMonitoredItemIdInvalid);
+ emit monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+
+ subscription->modifyMonitoring(handle, attr, item, value);
+ modifyPublishRequests();
+}
+
+QOpen62541Subscription *Open62541AsyncBackend::getSubscription(const QOpcUaMonitoringParameters &settings)
+{
+ if (settings.shared() == QOpcUaMonitoringParameters::SubscriptionType::Shared) {
+ for (auto entry : qAsConst(m_subscriptions)) {
+ if (qFuzzyCompare(entry->interval(), settings.publishingInterval()) && entry->shared() == QOpcUaMonitoringParameters::SubscriptionType::Shared)
+ return entry;
+ }
+ }
+
+ QOpen62541Subscription *sub = new QOpen62541Subscription(this, settings);
+ UA_UInt32 id = sub->createOnServer();
+ if (!id) {
+ delete sub;
+ return nullptr;
+ }
+ m_subscriptions[id] = sub;
+ return sub;
+}
+
+bool Open62541AsyncBackend::removeSubscription(UA_UInt32 subscriptionId)
+{
+ auto sub = m_subscriptions.find(subscriptionId);
+ if (sub != m_subscriptions.end()) {
+ sub.value()->removeOnServer();
+ delete sub.value();
+ m_subscriptions.remove(subscriptionId);
+ return true;
+ }
+ return false;
+}
+
static UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *pass)
{
Q_UNUSED(referenceTypeId);
@@ -208,24 +321,6 @@ QStringList Open62541AsyncBackend::childrenIds(const UA_NodeId *parentNode)
return result;
}
-UA_UInt32 Open62541AsyncBackend::createSubscription(int interval)
-{
- UA_UInt32 result;
- UA_SubscriptionSettings settings = UA_SubscriptionSettings_default;
- settings.requestedPublishingInterval = interval;
- UA_StatusCode ret = UA_Client_Subscriptions_new(m_uaclient, settings, &result);
- if (ret != UA_STATUSCODE_GOOD)
- return 0;
- return result;
-}
-
-void Open62541AsyncBackend::deleteSubscription(UA_UInt32 id)
-{
- UA_StatusCode ret = UA_Client_Subscriptions_remove(m_uaclient, id);
- if (ret != UA_STATUSCODE_GOOD)
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "QOpcUa::Open62541: Could not remove subscription";
-}
-
void Open62541AsyncBackend::connectToEndpoint(const QUrl &url)
{
m_uaclient = UA_Client_new(UA_ClientConfig_default);
@@ -251,12 +346,6 @@ void Open62541AsyncBackend::connectToEndpoint(const QUrl &url)
return;
}
- if (!m_subscriptionTimer) {
- m_subscriptionTimer = new QTimer(this);
- m_subscriptionTimer->setInterval(5000);
- QObject::connect(m_subscriptionTimer, &QTimer::timeout,
- this, &Open62541AsyncBackend::updatePublishSubscriptionRequests);
- }
emit stateAndOrErrorChanged(QOpcUaClient::Connected, QOpcUaClient::NoError);
}
@@ -270,42 +359,58 @@ void Open62541AsyncBackend::disconnectFromEndpoint()
UA_Client_delete(m_uaclient);
m_uaclient = nullptr;
- m_subscriptionTimer->stop();
+ m_subscriptionTimer.stop();
emit stateAndOrErrorChanged(QOpcUaClient::Disconnected, QOpcUaClient::NoError);
}
-void Open62541AsyncBackend::updatePublishSubscriptionRequests() const
+void Open62541AsyncBackend::sendPublishRequest()
{
- if (m_uaclient)
- UA_Client_Subscriptions_manuallySendPublishRequest(m_uaclient);
+ if (!m_uaclient)
+ return;
+
+ if (!m_sendPublishRequests) {
+ return;
+ }
+
+ if (UA_Client_Subscriptions_manuallySendPublishRequest(m_uaclient) == UA_STATUSCODE_BADSERVERNOTCONNECTED) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Unable to send publish request");
+ m_sendPublishRequests = false;
+ return;
+ }
+
+ m_subscriptionTimer.start(m_shortestInterval);
}
-void Open62541AsyncBackend::activateSubscriptionTimer(int timeout)
+void Open62541AsyncBackend::modifyPublishRequests()
{
- // ### TODO: Check all available subscriptions and their timeout value
- // Get minimum value
- if (timeout <= 0)
+ if (m_subscriptions.count() == 0) {
+ m_subscriptionTimer.stop();
+ m_sendPublishRequests = false;
+ m_shortestInterval = std::numeric_limits<qint32>::max();
return;
+ }
- m_subscriptionIntervals.insert(timeout);
+ for (auto it : qAsConst(m_subscriptions))
+ if (it->interval() < m_shortestInterval)
+ m_shortestInterval = it->interval();
- int minInterval = timeout;
- for (auto it : m_subscriptionIntervals) {
- if (it < minInterval)
- minInterval = it;
- }
- // Update subscriptionTimer timeout
- m_subscriptionTimer->setInterval(minInterval);
- // Start / Stop timer
- m_subscriptionTimer->start();
+ m_subscriptionTimer.stop();
+ m_sendPublishRequests = true;
+ sendPublishRequest();
}
-void Open62541AsyncBackend::removeSubscriptionTimer(int timeout)
+QOpen62541Subscription *Open62541AsyncBackend::getSubscriptionForItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr)
{
- if (!m_subscriptionIntervals.remove(timeout))
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Trying to remove non-existent interval.";
- if (m_subscriptionIntervals.isEmpty())
- m_subscriptionTimer->stop();
+ auto nodeEntry = m_attributeMapping.find(handle);
+ if (nodeEntry == m_attributeMapping.end())
+ return nullptr;
+
+ auto subscription = nodeEntry->find(attr);
+ if (subscription == nodeEntry->end()) {
+ return nullptr;
+ }
+
+ return subscription.value();
}
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/open62541/qopen62541backend.h b/src/plugins/opcua/open62541/qopen62541backend.h
index 4445f90..d6e2a80 100644
--- a/src/plugins/opcua/open62541/qopen62541backend.h
+++ b/src/plugins/opcua/open62541/qopen62541backend.h
@@ -35,6 +35,7 @@
****************************************************************************/
#include "qopen62541client.h"
+#include "qopen62541subscription.h"
#include <private/qopcuabackend_p.h>
#include <QtCore/qset.h>
@@ -50,6 +51,7 @@ class Open62541AsyncBackend : public QOpcUaBackend
Q_OBJECT
public:
Open62541AsyncBackend(QOpen62541Client *parent);
+ ~Open62541AsyncBackend();
public Q_SLOTS:
void connectToEndpoint(const QUrl &url);
@@ -61,18 +63,31 @@ public Q_SLOTS:
void writeAttribute(uintptr_t handle, UA_NodeId id, QOpcUaNode::NodeAttribute attrId, QVariant value, QOpcUa::Types type);
void writeAttributes(uintptr_t handle, UA_NodeId id, QOpcUaNode::AttributeMap toWrite, QOpcUa::Types valueAttributeType);
+ void enableMonitoring(uintptr_t handle, UA_NodeId id, QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings);
+ void disableMonitoring(uintptr_t handle, QOpcUaNode::NodeAttributes attr);
+ void modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value);
// Subscription
- UA_UInt32 createSubscription(int interval);
- void deleteSubscription(UA_UInt32 id);
- void updatePublishSubscriptionRequests() const;
- void activateSubscriptionTimer(int timeout);
- void removeSubscriptionTimer(int timeout);
+ QOpen62541Subscription *getSubscription(const QOpcUaMonitoringParameters &settings);
+ bool removeSubscription(UA_UInt32 subscriptionId);
+ void sendPublishRequest();
+ void modifyPublishRequests();
+
public:
- QOpen62541Client *m_clientImpl;
UA_Client *m_uaclient;
- QTimer *m_subscriptionTimer;
- QSet<int> m_subscriptionIntervals;
+
+private:
+ QOpen62541Subscription *getSubscriptionForItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr);
+
+ QOpen62541Client *m_clientImpl;
+ QTimer m_subscriptionTimer;
+
+ QHash<quint32, QOpen62541Subscription *> m_subscriptions;
+
+ QHash<uintptr_t, QHash<QOpcUaNode::NodeAttribute, QOpen62541Subscription *>> m_attributeMapping; // Handle -> Attribute -> Subscription
+
+ bool m_sendPublishRequests;
+ qint32 m_shortestInterval;
};
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/open62541/qopen62541client.cpp b/src/plugins/opcua/open62541/qopen62541client.cpp
index bbf6467..63867e2 100644
--- a/src/plugins/opcua/open62541/qopen62541client.cpp
+++ b/src/plugins/opcua/open62541/qopen62541client.cpp
@@ -91,14 +91,6 @@ QOpcUaNode *QOpen62541Client::node(const QString &nodeId)
return new QOpcUaNode(new QOpen62541Node(uaNodeId, this, nodeId), m_client);
}
-QOpcUaSubscription *QOpen62541Client::createSubscription(quint32 interval)
-{
- QOpen62541Subscription *backendSubscription = new QOpen62541Subscription(m_backend, interval);
- QOpcUaSubscription *subscription = new QOpcUaSubscription(backendSubscription, interval);
- backendSubscription->m_qsubscription = subscription;
- return subscription;
-}
-
QString QOpen62541Client::backend() const
{
return QStringLiteral("open62541");
diff --git a/src/plugins/opcua/open62541/qopen62541client.h b/src/plugins/opcua/open62541/qopen62541client.h
index 7b605f3..3721d98 100644
--- a/src/plugins/opcua/open62541/qopen62541client.h
+++ b/src/plugins/opcua/open62541/qopen62541client.h
@@ -59,7 +59,6 @@ public:
void disconnectFromEndpoint() override;
QOpcUaNode *node(const QString &nodeId) override;
- QOpcUaSubscription *createSubscription(quint32 interval) override;
QString backend() const override;
diff --git a/src/plugins/opcua/open62541/qopen62541node.cpp b/src/plugins/opcua/open62541/qopen62541node.cpp
index 8634f65..d765784 100644
--- a/src/plugins/opcua/open62541/qopen62541node.cpp
+++ b/src/plugins/opcua/open62541/qopen62541node.cpp
@@ -72,6 +72,34 @@ bool QOpen62541Node::readAttributes(QOpcUaNode::NodeAttributes attr)
Q_ARG(QOpcUaNode::NodeAttributes, attr));
}
+bool QOpen62541Node::enableMonitoring(QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
+{
+ return QMetaObject::invokeMethod(m_client->m_backend, "enableMonitoring",
+ Qt::QueuedConnection,
+ Q_ARG(uintptr_t, reinterpret_cast<uintptr_t>(this)),
+ Q_ARG(UA_NodeId, m_nodeId),
+ Q_ARG(QOpcUaNode::NodeAttributes, attr),
+ Q_ARG(QOpcUaMonitoringParameters, settings));
+}
+
+bool QOpen62541Node::disableMonitoring(QOpcUaNode::NodeAttributes attr)
+{
+ return QMetaObject::invokeMethod(m_client->m_backend, "disableMonitoring",
+ Qt::QueuedConnection,
+ Q_ARG(uintptr_t, reinterpret_cast<uintptr_t>(this)),
+ Q_ARG(QOpcUaNode::NodeAttributes, attr));
+}
+
+bool QOpen62541Node::modifyMonitoring(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value)
+{
+ return QMetaObject::invokeMethod(m_client->m_backend, "modifyMonitoring",
+ Qt::QueuedConnection,
+ Q_ARG(uintptr_t, reinterpret_cast<uintptr_t>(this)),
+ Q_ARG(QOpcUaNode::NodeAttribute, attr),
+ Q_ARG(QOpcUaMonitoringParameters::Parameter, item),
+ Q_ARG(QVariant, value));
+}
+
QStringList QOpen62541Node::childrenIds() const
{
QStringList result = m_client->m_backend->childrenIds(&m_nodeId);
diff --git a/src/plugins/opcua/open62541/qopen62541node.h b/src/plugins/opcua/open62541/qopen62541node.h
index ef54618..95e794c 100644
--- a/src/plugins/opcua/open62541/qopen62541node.h
+++ b/src/plugins/opcua/open62541/qopen62541node.h
@@ -51,6 +51,9 @@ public:
~QOpen62541Node() override;
bool readAttributes(QOpcUaNode::NodeAttributes attr) override;
+ bool enableMonitoring(QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings);
+ bool disableMonitoring(QOpcUaNode::NodeAttributes attr);
+ bool modifyMonitoring(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value);
QStringList childrenIds() const override;
QString nodeId() const override;
diff --git a/src/plugins/opcua/open62541/qopen62541subscription.cpp b/src/plugins/opcua/open62541/qopen62541subscription.cpp
index 4f40d3c..8acc41d 100644
--- a/src/plugins/opcua/open62541/qopen62541subscription.cpp
+++ b/src/plugins/opcua/open62541/qopen62541subscription.cpp
@@ -39,7 +39,6 @@
#include "qopen62541node.h"
#include "qopen62541subscription.h"
#include "qopen62541valueconverter.h"
-#include <private/qopcuamonitoredvalue_p.h>
#include <private/qopcuanode_p.h>
#include <QtCore/qloggingcategory.h>
@@ -54,115 +53,273 @@ static void monitoredValueHandler(UA_UInt32 monId, UA_DataValue *value, void *co
subscription->monitoredValueUpdated(monId, value);
}
-QOpen62541Subscription::QOpen62541Subscription(Open62541AsyncBackend *backend, quint32 interval)
+QOpen62541Subscription::QOpen62541Subscription(Open62541AsyncBackend *backend, const QOpcUaMonitoringParameters &settings)
: m_backend(backend)
- , m_interval(interval)
+ , m_interval(settings.publishingInterval())
, m_subscriptionId(0)
+ , m_lifetimeCount(settings.lifetimeCount() ? settings.lifetimeCount() : UA_SubscriptionSettings_default.requestedLifetimeCount)
+ , m_maxKeepaliveCount(settings.maxKeepAliveCount() ? settings.maxKeepAliveCount() : UA_SubscriptionSettings_default.requestedMaxKeepAliveCount)
+ , m_shared(settings.shared())
+ , m_priority(settings.priority())
{
}
QOpen62541Subscription::~QOpen62541Subscription()
{
- removeNativeSubscription();
+ removeOnServer();
+ qDeleteAll(m_itemIdToItemMapping);
}
-QOpcUaMonitoredEvent *QOpen62541Subscription::addEvent(QOpcUaNode *node)
+UA_UInt32 QOpen62541Subscription::createOnServer()
{
- Q_UNUSED(node);
- Q_UNIMPLEMENTED();
- return nullptr;
+ UA_UInt32 subscriptionId = 0;
+ UA_SubscriptionSettings settings = UA_SubscriptionSettings_default;
+ settings.requestedPublishingInterval = m_interval;
+ settings.requestedLifetimeCount = m_lifetimeCount;
+ settings.requestedMaxKeepAliveCount = m_maxKeepaliveCount;
+ settings.priority = m_priority;
+ UA_StatusCode res = UA_Client_Subscriptions_new(m_backend->m_uaclient, settings, &subscriptionId);
+
+ if (res != UA_STATUSCODE_GOOD) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not create subscription with interval %f: 0x%X", m_interval, res);
+ return 0;
+ }
+
+ m_subscriptionId = subscriptionId;
+ return subscriptionId;
}
-void QOpen62541Subscription::removeEvent(QOpcUaMonitoredEvent *event)
+bool QOpen62541Subscription::removeOnServer()
{
- Q_UNUSED(event);
- Q_UNIMPLEMENTED();
+ if (m_subscriptionId == 0)
+ return false;
+
+ UA_StatusCode res = UA_Client_Subscriptions_remove(m_backend->m_uaclient, m_subscriptionId);
+ m_subscriptionId = 0;
+
+ return (res == UA_STATUSCODE_GOOD) ? true : false;
}
-QOpcUaMonitoredValue *QOpen62541Subscription::addValue(QOpcUaNode *node)
+void QOpen62541Subscription::modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value)
{
- if (!ensureNativeSubscription())
- return nullptr;
+ QOpcUaMonitoringParameters p;
+ p.setStatusCode(QOpcUa::UaStatusCode::BadNotImplemented);
+
+ if (!getItemForAttribute(handle, attr)) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify parameter for %lu, there are no monitored items", handle);
+ p.setStatusCode(QOpcUa::UaStatusCode::BadAttributeIdInvalid);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
- QOpen62541Node *open62541node = static_cast<QOpen62541Node *>(node->d_func()->m_impl.data());
+ // SetPublishingMode service
+ if (item == QOpcUaMonitoringParameters::Parameter::PublishingEnabled) {
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+
+ // SetMonitoringMode service
+ if (item == QOpcUaMonitoringParameters::Parameter::MonitoringMode) {
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+
+ // ModifySubscription service
+ {
+ UA_ModifySubscriptionRequest req;
+ UA_ModifySubscriptionRequest_init(&req);
+ req.subscriptionId = m_subscriptionId;
+ req.requestedPublishingInterval = m_interval;
+ req.requestedLifetimeCount = m_lifetimeCount;
+ req.requestedMaxKeepAliveCount = m_maxKeepaliveCount;
+
+ bool match = false;
+
+ switch (item) {
+ case QOpcUaMonitoringParameters::Parameter::PublishingInterval: {
+ bool ok;
+ req.requestedPublishingInterval = value.toDouble(&ok);
+ if (!ok) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify PublishingInterval for %lu, value is not a double", handle);
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+ match = true;
+ break;
+ }
+ case QOpcUaMonitoringParameters::Parameter::LifetimeCount: {
+ bool ok;
+ req.requestedLifetimeCount = value.toUInt(&ok);
+ if (!ok) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify LifetimeCount for %lu, value is not an integer", handle);
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+ match = true;
+ break;
+ }
+ case QOpcUaMonitoringParameters::Parameter::MaxKeepAliveCount: {
+ bool ok;
+ req.requestedMaxKeepAliveCount = value.toUInt(&ok);
+ if (!ok) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify MaxKeepAliveCount for %lu, value is not an integer", handle);
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+ match = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (match) {
+ UA_ModifySubscriptionResponse res = UA_Client_Service_modifySubscription(m_backend->m_uaclient, req);
+
+ if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+ p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult));
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ } else {
+ p.setStatusCode(QOpcUa::UaStatusCode::Good);
+ p.setPublishingInterval(res.revisedPublishingInterval);
+ p.setLifetimeCount(res.revisedLifetimeCount);
+ p.setMaxKeepAliveCount(res.revisedMaxKeepAliveCount);
+
+ QOpcUaMonitoringParameters::Parameters changed = item;
+ if (!qFuzzyCompare(p.publishingInterval(), m_interval))
+ changed |= QOpcUaMonitoringParameters::Parameter::PublishingInterval;
+ if (p.lifetimeCount() != m_lifetimeCount)
+ changed |= QOpcUaMonitoringParameters::Parameter::LifetimeCount;
+ if (p.maxKeepAliveCount() != m_maxKeepaliveCount)
+ changed |= QOpcUaMonitoringParameters::Parameter::MaxKeepAliveCount;
+
+ for (auto it : qAsConst(m_itemIdToItemMapping))
+ emit m_backend->monitoringStatusChanged(it->handle, it->attr, changed, p);
+
+ m_lifetimeCount = res.revisedLifetimeCount;
+ m_maxKeepaliveCount = res.revisedMaxKeepAliveCount;
+ m_interval = res.revisedPublishingInterval;
+ }
+ return;
+ }
+ }
+
+ // ModifyMonitoredItems service
+ {
+ // TODO: Add support as soon as Open62541 supports this.
+ }
+
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Revising attribute is not implemented:" << item;
+ p.setStatusCode(QOpcUa::UaStatusCode::BadNotImplemented);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+}
+
+bool QOpen62541Subscription::addAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr, const UA_NodeId &id, QOpcUaMonitoringParameters settings)
+{
+ Q_UNUSED(settings); // This is for later applications like including parameters for the monitored item into settings
UA_UInt32 monitoredItemId = 0;
- UA_StatusCode ret = UA_Client_Subscriptions_addMonitoredItem(m_backend->m_uaclient, m_subscriptionId,
- open62541node->nativeNodeId(), UA_ATTRIBUTEID_VALUE,
+ UA_StatusCode ret = UA_Client_Subscriptions_addMonitoredItem(m_backend->m_uaclient, m_subscriptionId, id,
+ QOpen62541ValueConverter::toUaAttributeId(attr),
monitoredValueHandler, this, &monitoredItemId);
+
if (ret != UA_STATUSCODE_GOOD) {
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not add monitored item:" << ret;
- return nullptr;
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not add monitored item to subscription %u: 0x%X", m_subscriptionId, ret);
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(static_cast<QOpcUa::UaStatusCode>(ret));
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
+ return false;
}
- QOpcUaMonitoredValue *monitoredValue = new QOpcUaMonitoredValue(node, m_qsubscription);
- if (m_dataChangeHandles.contains(monitoredItemId)) {
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "monitoredItemId already handled:" << monitoredItemId;
- } else {
- m_dataChangeHandles[monitoredItemId] = monitoredValue;
- }
+ MonitoredItem *temp = new MonitoredItem(handle, attr, monitoredItemId);
+ m_handleToItemMapping[handle][attr] = temp;
+ m_itemIdToItemMapping[monitoredItemId] = temp;
- // Open62541 does not support automated subscriptions, hence we need to poll for the initial
- // value.
- UA_Client_Subscriptions_manuallySendPublishRequest(m_backend->m_uaclient);
- QMetaObject::invokeMethod(m_backend, "activateSubscriptionTimer", Qt::QueuedConnection, Q_ARG(int, m_interval));
+ QOpcUaMonitoringParameters s;
+ s.setSubscriptionId(m_subscriptionId);
+ s.setPublishingInterval(m_interval);
+ s.setMaxKeepAliveCount(m_maxKeepaliveCount);
+ s.setLifetimeCount(m_lifetimeCount);
+ s.setStatusCode(static_cast<QOpcUa::UaStatusCode>(ret));
+ s.setSamplingInterval(m_interval);
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
- return monitoredValue;
+ return true;
}
-void QOpen62541Subscription::removeValue(QOpcUaMonitoredValue *monitoredValue)
+bool QOpen62541Subscription::removeAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr)
{
- auto it = m_dataChangeHandles.begin();
- while (it != m_dataChangeHandles.end()) {
- if (it.value() == monitoredValue) {
- UA_StatusCode ret = UA_Client_Subscriptions_removeMonitoredItem(m_backend->m_uaclient, m_subscriptionId, it.key());
- if (ret != UA_STATUSCODE_GOOD) {
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not remove monitored value from subscription:" << it.key();
- }
- m_dataChangeHandles.erase(it);
- QMetaObject::invokeMethod(m_backend, "removeSubscriptionTimer", Qt::QueuedConnection, Q_ARG(int, m_interval));
- break;
- }
- ++it;
+ MonitoredItem *item = getItemForAttribute(handle, attr);
+ if (!item) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "There is no monitored item for this attribute");
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadMonitoredItemIdInvalid);
+ emit m_backend->monitoringEnableDisable(handle, attr, false, s);
+ return false;
}
+
+ UA_StatusCode res = UA_Client_Subscriptions_removeMonitoredItem(m_backend->m_uaclient, m_subscriptionId, item->monitoredItemId);
+ if (res != UA_STATUSCODE_GOOD)
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not remove monitored item %u from subscription %u: 0x%X",
+ item->monitoredItemId, m_subscriptionId, res);
+
+ m_itemIdToItemMapping.remove(item->monitoredItemId);
+ auto it = m_handleToItemMapping.find(handle);
+ it->remove(attr);
+ if (it->empty())
+ m_handleToItemMapping.erase(it);
+
+ delete item;
+
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res));
+ emit m_backend->monitoringEnableDisable(handle, attr, false, s);
+
+ return true;
}
void QOpen62541Subscription::monitoredValueUpdated(UA_UInt32 monId, UA_DataValue *value)
{
- auto monitoredValue = m_dataChangeHandles.find(monId);
- if (monitoredValue == m_dataChangeHandles.end()) {
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not find object for monitoredItemId:" << monId;
+ auto item = m_itemIdToItemMapping.constFind(monId);
+ if (item == m_itemIdToItemMapping.constEnd())
return;
- }
+ emit m_backend->attributeUpdated(item.value()->handle, item.value()->attr, QOpen62541ValueConverter::toQVariant(value->value));
+}
- if (!value || !value->hasValue)
- return;
+double QOpen62541Subscription::interval() const
+{
+ return m_interval;
+}
- QVariant var = QOpen62541ValueConverter::toQVariant(value->value);
- if (var.isValid())
- (*monitoredValue)->d_func()->triggerValueChanged(var);
- else
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not convert value for node:" << (*monitoredValue)->node().nodeId();
+UA_UInt32 QOpen62541Subscription::subscriptionId() const
+{
+ return m_subscriptionId;
}
-bool QOpen62541Subscription::ensureNativeSubscription()
+int QOpen62541Subscription::monitoredItemsCount() const
{
- if (m_subscriptionId == 0) {
- QMetaObject::invokeMethod(m_backend, "createSubscription",
- Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(UA_UInt32, m_subscriptionId),
- Q_ARG(int, m_interval));
- }
- return m_subscriptionId != 0;
+ return m_itemIdToItemMapping.size();
}
-void QOpen62541Subscription::removeNativeSubscription()
+QOpcUaMonitoringParameters::SubscriptionType QOpen62541Subscription::shared() const
{
- if (m_subscriptionId != 0) {
- QMetaObject::invokeMethod(m_backend, "deleteSubscription",
- Qt::BlockingQueuedConnection,
- Q_ARG(UA_UInt32, m_subscriptionId));
- m_subscriptionId = 0;
- }
+ return m_shared;
+}
+
+QOpen62541Subscription::MonitoredItem *QOpen62541Subscription::getItemForAttribute(uintptr_t handle, QOpcUaNode::NodeAttribute attr)
+{
+ auto nodeEntry = m_handleToItemMapping.constFind(handle);
+
+ if (nodeEntry == m_handleToItemMapping.constEnd())
+ return nullptr;
+
+ auto item = nodeEntry->constFind(attr);
+ if (item == nodeEntry->constEnd())
+ return nullptr;
+
+ return item.value();
}
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/open62541/qopen62541subscription.h b/src/plugins/opcua/open62541/qopen62541subscription.h
index e527c7b..a75aa90 100644
--- a/src/plugins/opcua/open62541/qopen62541subscription.h
+++ b/src/plugins/opcua/open62541/qopen62541subscription.h
@@ -38,39 +38,63 @@
#define QOPEN62541SUBSCRIPTION_H
#include "qopen62541.h"
-#include <QtOpcUa/qopcuamonitoredevent.h>
-#include <QtOpcUa/qopcuamonitoredvalue.h>
-#include <private/qopcuasubscriptionimpl_p.h>
+#include <QtOpcUa/qopcuanode.h>
QT_BEGIN_NAMESPACE
class QOpen62541Client;
class Open62541AsyncBackend;
-class QOpen62541Subscription : public QOpcUaSubscriptionImpl
+class QOpen62541Subscription
{
public:
- explicit QOpen62541Subscription(Open62541AsyncBackend *backend, quint32 interval);
- ~QOpen62541Subscription() override;
+ QOpen62541Subscription(Open62541AsyncBackend *backend, const QOpcUaMonitoringParameters &settings);
+ ~QOpen62541Subscription();
- QOpcUaMonitoredEvent *addEvent(QOpcUaNode *node) override;
- void removeEvent(QOpcUaMonitoredEvent *event) override;
+ UA_UInt32 createOnServer();
+ bool removeOnServer();
- QOpcUaMonitoredValue *addValue(QOpcUaNode *node) override;
- void removeValue(QOpcUaMonitoredValue *v) override;
+ void modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value);
+
+ bool addAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr, const UA_NodeId &id, QOpcUaMonitoringParameters settings);
+ bool removeAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr);
void monitoredValueUpdated(UA_UInt32 monId, UA_DataValue *value);
- QOpcUaSubscription *m_qsubscription;
+ struct MonitoredItem {
+ uintptr_t handle;
+ QOpcUaNode::NodeAttribute attr;
+ UA_UInt32 monitoredItemId;
+ MonitoredItem(uintptr_t h, QOpcUaNode::NodeAttribute a, UA_UInt32 id)
+ : handle(h)
+ , attr(a)
+ , monitoredItemId(id)
+ {}
+ MonitoredItem()
+ : handle(0)
+ , monitoredItemId(0)
+ {}
+ };
+
+ double interval() const;
+ UA_UInt32 subscriptionId() const;
+ int monitoredItemsCount() const;
+
+ QOpcUaMonitoringParameters::SubscriptionType shared() const;
private:
- bool ensureNativeSubscription();
- void removeNativeSubscription();
+ MonitoredItem *getItemForAttribute(uintptr_t handle, QOpcUaNode::NodeAttribute attr);
+
Open62541AsyncBackend *m_backend;
- quint32 m_interval;
+ double m_interval;
UA_UInt32 m_subscriptionId;
- QMap<UA_UInt32, QOpcUaMonitoredValue *> m_dataChangeHandles;
- QMap<UA_UInt32, QOpcUaMonitoredEvent *> m_eventHandles;
+ UA_UInt32 m_lifetimeCount;
+ UA_UInt32 m_maxKeepaliveCount;
+ QOpcUaMonitoringParameters::SubscriptionType m_shared;
+ quint8 m_priority;
+
+ QHash<uintptr_t, QHash<QOpcUaNode::NodeAttribute, MonitoredItem *>> m_handleToItemMapping; // Handle -> Attribute -> MonitoredItem
+ QHash<UA_UInt32, MonitoredItem *> m_itemIdToItemMapping; // ItemId -> Item for fast lookup on data change
};
QT_END_NAMESPACE