summaryrefslogtreecommitdiffstats
path: root/src/plugins/opcua/open62541/qopen62541subscription.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/opcua/open62541/qopen62541subscription.cpp')
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.cpp295
1 files changed, 226 insertions, 69 deletions
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