summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJannis Voelker <jannis.voelker@basyskom.com>2018-02-20 09:16:38 +0100
committerMaurice Kalinowski <maurice.kalinowski@qt.io>2018-03-16 05:57:17 +0000
commitd4c3634267488aed7ef9badd5f269aedcc38af1f (patch)
tree7680d55172e305cbc118095ef909650c88b31f67
parent61f6ad6a5c16c46e4e56471a2306de7c72deca52 (diff)
Fix and extend support for modifyMonitoring in open62541
The open62541 backend now supports all parameters enumerated in QOpcUaMonitoringParameters::Parameter. Change-Id: I04bc55b57661ec9dcbeceac87b68d3324bc22dec Reviewed-by: Maurice Kalinowski <maurice.kalinowski@qt.io> Reviewed-by: Frank Meerkoetter <frank.meerkoetter@basyskom.com>
-rw-r--r--src/opcua/client/qopcuamonitoringparameters.cpp14
-rw-r--r--src/opcua/client/qopcuamonitoringparameters.h1
-rw-r--r--src/opcua/client/qopcuanode.cpp12
-rw-r--r--src/opcua/client/qopcuanode.h1
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.cpp386
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.h8
-rw-r--r--tests/auto/qopcuaclient/tst_client.cpp209
7 files changed, 519 insertions, 112 deletions
diff --git a/src/opcua/client/qopcuamonitoringparameters.cpp b/src/opcua/client/qopcuamonitoringparameters.cpp
index 18e5433..307bdb1 100644
--- a/src/opcua/client/qopcuamonitoringparameters.cpp
+++ b/src/opcua/client/qopcuamonitoringparameters.cpp
@@ -108,31 +108,31 @@ QT_BEGIN_NAMESPACE
\li X
\row
\li PublishingEnabled
- \li
+ \li X
\li
\row
\li Filter
- \li
+ \li X
\li
\row
\li QueueSize
- \li
+ \li X
\li
\row
\li DiscardOldest
- \li
+ \li X
\li
\row
\li MonitoringMode
- \li
+ \li X
\li
\row
\li IndexRange
- \li
+ \li X
\li
\row
\li MaxNotificationsPerPublish
- \li
+ \li X
\li
\endtable
*/
diff --git a/src/opcua/client/qopcuamonitoringparameters.h b/src/opcua/client/qopcuamonitoringparameters.h
index 8a730de..30200c4 100644
--- a/src/opcua/client/qopcuamonitoringparameters.h
+++ b/src/opcua/client/qopcuamonitoringparameters.h
@@ -164,5 +164,6 @@ Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::DataChangeFilter::DataChangeTrigg
Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType)
Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::Parameter)
Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::Parameters)
+Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::MonitoringMode)
#endif // QOPCUAMONITORINGPARAMETERS_H
diff --git a/src/opcua/client/qopcuanode.cpp b/src/opcua/client/qopcuanode.cpp
index 990e438..38dcd3d 100644
--- a/src/opcua/client/qopcuanode.cpp
+++ b/src/opcua/client/qopcuanode.cpp
@@ -385,6 +385,18 @@ QOpcUaMonitoringParameters QOpcUaNode::monitoringStatus(QOpcUa::NodeAttribute at
}
/*!
+ Modifies an existing data change monitoring to use \a filter as data change filter.
+
+ Returns \c true if the filter modification request has been successfully dispatched to the backend.
+
+ \l monitoringStatusChanged for \a attr is emitted after the operation has finished.
+*/
+bool QOpcUaNode::modifyDataChangeFilter(QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::DataChangeFilter &filter)
+{
+ return modifyMonitoring(attr, QOpcUaMonitoringParameters::Parameter::Filter, QVariant::fromValue(filter));
+}
+
+/*!
Writes \a value to the attribute given in \a attribute using the type information from \a type.
Returns \c true if the asynchronous call has been successfully dispatched.
diff --git a/src/opcua/client/qopcuanode.h b/src/opcua/client/qopcuanode.h
index fc12e53..4ab1138 100644
--- a/src/opcua/client/qopcuanode.h
+++ b/src/opcua/client/qopcuanode.h
@@ -82,6 +82,7 @@ public:
bool disableMonitoring(QOpcUa::NodeAttributes attr);
bool modifyMonitoring(QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value);
QOpcUaMonitoringParameters monitoringStatus(QOpcUa::NodeAttribute attr);
+ bool modifyDataChangeFilter(QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::DataChangeFilter &filter);
bool browseChildren(QOpcUa::ReferenceTypeId referenceType = QOpcUa::ReferenceTypeId::HierarchicalReferences,
QOpcUa::NodeClasses nodeClassMask = QOpcUa::NodeClass::Undefined);
diff --git a/src/plugins/opcua/open62541/qopen62541subscription.cpp b/src/plugins/opcua/open62541/qopen62541subscription.cpp
index 8100610..36332f9 100644
--- a/src/plugins/opcua/open62541/qopen62541subscription.cpp
+++ b/src/plugins/opcua/open62541/qopen62541subscription.cpp
@@ -64,6 +64,8 @@ QOpen62541Subscription::QOpen62541Subscription(Open62541AsyncBackend *backend, c
, m_maxKeepaliveCount(settings.maxKeepAliveCount() ? settings.maxKeepAliveCount() : UA_SubscriptionSettings_default.requestedMaxKeepAliveCount)
, m_shared(settings.shared())
, m_priority(settings.priority())
+ , m_maxNotificationsPerPublish(settings.maxNotificationsPerPublish())
+ , m_clientHandle(0)
{
}
@@ -79,6 +81,7 @@ UA_UInt32 QOpen62541Subscription::createOnServer()
req.requestedLifetimeCount = m_lifetimeCount;
req.requestedMaxKeepAliveCount = m_maxKeepaliveCount;
req.priority = m_priority;
+ req.maxNotificationsPerPublish = m_maxNotificationsPerPublish;
UA_CreateSubscriptionResponse res = UA_Client_Subscriptions_create(m_backend->m_uaclient, req, this, NULL, NULL);
if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
@@ -87,6 +90,9 @@ UA_UInt32 QOpen62541Subscription::createOnServer()
}
m_subscriptionId = res.subscriptionId;
+ m_maxKeepaliveCount = res.revisedMaxKeepAliveCount;
+ m_lifetimeCount = res.revisedLifetimeCount;
+ m_interval = res.revisedPublishingInterval;
return m_subscriptionId;
}
@@ -117,113 +123,92 @@ void QOpen62541Subscription::modifyMonitoring(uintptr_t handle, QOpcUa::NodeAttr
QOpcUaMonitoringParameters p;
p.setStatusCode(QOpcUa::UaStatusCode::BadNotImplemented);
- if (!getItemForAttribute(handle, attr)) {
+ MonitoredItem *monItem = getItemForAttribute(handle, attr);
+ if (!monItem) {
qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify parameter for" << handle << ", there are no monitored items";
p.setStatusCode(QOpcUa::UaStatusCode::BadAttributeIdInvalid);
emit m_backend->monitoringStatusChanged(handle, attr, item, p);
return;
}
+ p = monItem->parameters;
+
// SetPublishingMode service
if (item == QOpcUaMonitoringParameters::Parameter::PublishingEnabled) {
+ if (value.type() != QVariant::Bool) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "New value for PublishingEnabled is not a boolean";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+
+ UA_SetPublishingModeRequest req;
+ UA_SetPublishingModeRequest_init(&req);
+ req.publishingEnabled = value.toBool();
+ req.subscriptionIdsSize = 1;
+ req.subscriptionIds = UA_UInt32_new();
+ *req.subscriptionIds = m_subscriptionId;
+ UA_SetPublishingModeResponse res = UA_Client_Subscriptions_setPublishingMode(m_backend->m_uaclient, req);
+
+ if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to set publishing mode:" << res.responseHeader.serviceResult;
+ p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult));
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+
+ if (res.resultsSize && res.results[0] == UA_STATUSCODE_GOOD)
+ p.setPublishingEnabled(value.toBool());
+
+ p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.results[0]));
emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+
+ UA_SetPublishingModeRequest_deleteMembers(&req);
+ UA_SetPublishingModeResponse_deleteMembers(&res);
return;
}
// SetMonitoringMode service
if (item == QOpcUaMonitoringParameters::Parameter::MonitoringMode) {
- emit m_backend->monitoringStatusChanged(handle, attr, item, p);
- return;
- }
+ if (value.type() != QVariant::UserType || value.userType() != QMetaType::type("QOpcUaMonitoringParameters::MonitoringMode")) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "New value for MonitoringMode is not a monitoring mode";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
- // ModifySubscription service
- {
- UA_ModifySubscriptionRequest req;
- UA_ModifySubscriptionRequest_init(&req);
+ UA_SetMonitoringModeRequest req;
+ UA_SetMonitoringModeRequest_init(&req);
+ req.monitoringMode = static_cast<UA_MonitoringMode>(value.value<QOpcUaMonitoringParameters::MonitoringMode>());
+ req.monitoredItemIdsSize = 1;
+ req.monitoredItemIds = UA_UInt32_new();
+ *req.monitoredItemIds = monItem->monitoredItemId;
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" << handle << ", value is not a double";
- 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" << handle << ", value is not an integer";
- 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" << handle << ", value is not an integer";
- p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
- emit m_backend->monitoringStatusChanged(handle, attr, item, p);
- return;
- }
- match = true;
- break;
- }
- default:
- break;
- }
+ UA_SetMonitoringModeResponse res = UA_Client_MonitoredItems_setMonitoringMode(m_backend->m_uaclient, req);
- if (match) {
- UA_ModifySubscriptionResponse res = UA_Client_Subscriptions_modify(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;
- }
+ if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to set monitoring mode:" << res.responseHeader.serviceResult;
+ p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult));
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
return;
}
- }
- // ModifyMonitoredItems service
- {
- // TODO: Add support as soon as Open62541 supports this.
+ if (res.resultsSize && res.results[0] == UA_STATUSCODE_GOOD)
+ p.setMonitoringMode(value.value<QOpcUaMonitoringParameters::MonitoringMode>());
+
+ p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.results[0]));
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+
+ UA_SetMonitoringModeRequest_deleteMembers(&req);
+ UA_SetMonitoringModeResponse_deleteMembers(&res);
+ return;
}
+ if (modifySubscriptionParameters(handle, attr, item, value))
+ return;
+ if (modifyMonitoredItemParameters(handle, attr, item, value))
+ return;
+
qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Modifying" << item << "is not implemented";
p.setStatusCode(QOpcUa::UaStatusCode::BadNotImplemented);
emit m_backend->monitoringStatusChanged(handle, attr, item, p);
@@ -241,8 +226,8 @@ bool QOpen62541Subscription::addAttributeMonitoredItem(uintptr_t handle, QOpcUa:
req.requestedParameters.samplingInterval = qFuzzyCompare(settings.samplingInterval(), 0.0) ? m_interval : settings.samplingInterval();
req.requestedParameters.queueSize = settings.queueSize() == 0 ? 1 : settings.queueSize();
req.requestedParameters.discardOldest = settings.discardOldest();
-
- if (settings.filter().type() == QVariant::Type::UserType && settings.filter().userType() == QMetaType::type("QOpcUaMonitoringParameters::DataChangeFilter"))
+ req.requestedParameters.clientHandle = ++m_clientHandle;
+ if (settings.filter().type() == QVariant::UserType && settings.filter().userType() == QMetaType::type("QOpcUaMonitoringParameters::DataChangeFilter"))
req.requestedParameters.filter = createFilter(settings.filter());
UA_MonitoredItemCreateResult res = UA_Client_MonitoredItems_createDataChange(m_backend->m_uaclient, m_subscriptionId, UA_TIMESTAMPSTORETURN_BOTH, req, this, monitoredValueHandler, nullptr);
@@ -261,13 +246,19 @@ bool QOpen62541Subscription::addAttributeMonitoredItem(uintptr_t handle, QOpcUa:
m_handleToItemMapping[handle][attr] = temp;
m_itemIdToItemMapping[res.monitoredItemId] = temp;
- QOpcUaMonitoringParameters s;
+ QOpcUaMonitoringParameters s = settings;
+ if (settings.filter().type() == QVariant::UserType && settings.filter().userType() == QMetaType::type("QOpcUaMonitoringParameters::DataChangeFilter"))
+ s.setFilter(QVariant());
s.setSubscriptionId(m_subscriptionId);
s.setPublishingInterval(m_interval);
s.setMaxKeepAliveCount(m_maxKeepaliveCount);
s.setLifetimeCount(m_lifetimeCount);
s.setStatusCode(QOpcUa::UaStatusCode::Good);
- s.setSamplingInterval(m_interval);
+ s.setSamplingInterval(res.revisedSamplingInterval);
+ s.setQueueSize(res.revisedQueueSize);
+ temp->parameters = s;
+ temp->clientHandle = m_clientHandle;
+
emit m_backend->monitoringEnableDisable(handle, attr, true, s);
return true;
@@ -376,4 +367,221 @@ UA_ExtensionObject QOpen62541Subscription::createFilter(const QVariant &filterDa
return obj;
}
+bool QOpen62541Subscription::modifySubscriptionParameters(uintptr_t handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value)
+{
+ QOpcUaMonitoringParameters p;
+
+ UA_ModifySubscriptionRequest req;
+ UA_ModifySubscriptionRequest_init(&req);
+ req.subscriptionId = m_subscriptionId;
+ req.requestedPublishingInterval = m_interval;
+ req.requestedLifetimeCount = m_lifetimeCount;
+ req.requestedMaxKeepAliveCount = m_maxKeepaliveCount;
+ req.maxNotificationsPerPublish = m_maxNotificationsPerPublish;
+
+ bool match = true;
+
+ 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" << handle << ", value is not a double";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return 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" << handle << ", value is not an integer";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return 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" << handle << ", value is not an integer";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return true;
+ }
+ break;
+ }
+ case QOpcUaMonitoringParameters::Parameter::Priority: {
+ bool ok;
+ req.priority = value.toUInt(&ok);
+ if (!ok) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify Priority for" << handle << ", value is not an integer";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return true;
+ }
+ break;
+ }
+ case QOpcUaMonitoringParameters::Parameter::MaxNotificationsPerPublish: {
+ bool ok;
+ req.maxNotificationsPerPublish = value.toUInt(&ok);
+ if (!ok) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify MaxNotificationsPerPublish for" << handle << ", value is not an integer";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return true;
+ }
+ break;
+ }
+ default:
+ match = false;
+ break;
+ }
+
+ if (match) {
+ UA_ModifySubscriptionResponse res = UA_Client_Subscriptions_modify(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 {
+ 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;
+
+ m_lifetimeCount = res.revisedLifetimeCount;
+ m_maxKeepaliveCount = res.revisedMaxKeepAliveCount;
+ m_interval = res.revisedPublishingInterval;
+ if (item == QOpcUaMonitoringParameters::Parameter::Priority)
+ m_priority = value.toUInt();
+ if (item == QOpcUaMonitoringParameters::Parameter::MaxNotificationsPerPublish)
+ m_maxNotificationsPerPublish = value.toUInt();
+
+ p.setStatusCode(QOpcUa::UaStatusCode::Good);
+ p.setPublishingInterval(m_interval);
+ p.setLifetimeCount(m_lifetimeCount);
+ p.setMaxKeepAliveCount(m_maxKeepaliveCount);
+ p.setPriority(m_priority);
+ p.setMaxNotificationsPerPublish(m_maxNotificationsPerPublish);
+
+ for (auto it : qAsConst(m_itemIdToItemMapping))
+ emit m_backend->monitoringStatusChanged(it->handle, it->attr, changed, p);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool QOpen62541Subscription::modifyMonitoredItemParameters(uintptr_t handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value)
+{
+ MonitoredItem *monItem = getItemForAttribute(handle, attr);
+ QOpcUaMonitoringParameters p = monItem->parameters;
+
+ UA_ModifyMonitoredItemsRequest req;
+ UA_ModifyMonitoredItemsRequest_init(&req);
+ req.subscriptionId = m_subscriptionId;
+ req.itemsToModifySize = 1;
+ req.itemsToModify = UA_MonitoredItemModifyRequest_new();
+ UA_MonitoredItemModifyRequest_init(req.itemsToModify);
+ req.itemsToModify->monitoredItemId = monItem->monitoredItemId;
+ req.itemsToModify->requestedParameters.discardOldest = monItem->parameters.discardOldest();
+ req.itemsToModify->requestedParameters.queueSize = monItem->parameters.queueSize();
+ req.itemsToModify->requestedParameters.samplingInterval = monItem->parameters.samplingInterval();
+ req.itemsToModify->monitoredItemId = monItem->monitoredItemId;
+ req.itemsToModify->requestedParameters.clientHandle = monItem->clientHandle;
+ if (item != QOpcUaMonitoringParameters::Parameter::Filter)
+ req.itemsToModify->requestedParameters.filter = createFilter(monItem->parameters.filter());
+
+ bool match = true;
+
+ switch (item) {
+ case QOpcUaMonitoringParameters::Parameter::DiscardOldest: {
+ if (value.type() != QVariant::Bool) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify DiscardOldest for" << handle << ", value is not a bool";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ UA_ModifyMonitoredItemsRequest_deleteMembers(&req);
+ return true;
+ }
+ req.itemsToModify->requestedParameters.discardOldest = value.toBool();
+ break;
+ }
+ case QOpcUaMonitoringParameters::Parameter::QueueSize: {
+ if (value.type() != QVariant::UInt) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify QueueSize for" << handle << ", value is not an integer";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ UA_ModifyMonitoredItemsRequest_deleteMembers(&req);
+ return true;
+ }
+ req.itemsToModify->requestedParameters.queueSize = value.toUInt();
+ break;
+ }
+ case QOpcUaMonitoringParameters::Parameter::SamplingInterval: {
+ if (value.type() != QVariant::Double) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify SamplingInterval for" << handle << ", value is not a double";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ UA_ModifyMonitoredItemsRequest_deleteMembers(&req);
+ return true;
+ }
+ req.itemsToModify->requestedParameters.samplingInterval = value.toDouble();
+ break;
+ }
+ case QOpcUaMonitoringParameters::Parameter::Filter: {
+ req.itemsToModify->requestedParameters.filter = createFilter(value);
+ break;
+ }
+ default:
+ match = false;
+ break;
+ }
+
+ if (match) {
+ UA_ModifyMonitoredItemsResponse res = UA_Client_MonitoredItems_modify(m_backend->m_uaclient, req);
+
+ if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD || res.results[0].statusCode != UA_STATUSCODE_GOOD) {
+ p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult == UA_STATUSCODE_GOOD ? res.results[0].statusCode : res.responseHeader.serviceResult));
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ UA_ModifyMonitoredItemsRequest_deleteMembers(&req);
+ UA_ModifyMonitoredItemsResponse_deleteMembers(&res);
+ return true;
+ } else {
+ p.setStatusCode(QOpcUa::UaStatusCode::Good);
+ QOpcUaMonitoringParameters::Parameters changed = item;
+ if (!qFuzzyCompare(p.samplingInterval(), res.results[0].revisedSamplingInterval)) {
+ p.setSamplingInterval(res.results[0].revisedSamplingInterval);
+ changed |= QOpcUaMonitoringParameters::Parameter::SamplingInterval;
+ }
+ if (p.queueSize() != res.results[0].revisedQueueSize) {
+ p.setQueueSize(res.results[0].revisedQueueSize);
+ changed |= QOpcUaMonitoringParameters::Parameter::QueueSize;
+ }
+
+ if (item == QOpcUaMonitoringParameters::Parameter::DiscardOldest) {
+ p.setDiscardOldest(value.toBool());
+ changed |= QOpcUaMonitoringParameters::Parameter::DiscardOldest;
+ }
+
+ emit m_backend->monitoringStatusChanged(handle, attr, changed, p);
+ monItem->parameters = p;
+ UA_ModifyMonitoredItemsRequest_deleteMembers(&req);
+ UA_ModifyMonitoredItemsResponse_deleteMembers(&res);
+ }
+ return true;
+ }
+
+ UA_ModifyMonitoredItemsRequest_deleteMembers(&req);
+
+ return false;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/open62541/qopen62541subscription.h b/src/plugins/opcua/open62541/qopen62541subscription.h
index 2f03a66..58dd407 100644
--- a/src/plugins/opcua/open62541/qopen62541subscription.h
+++ b/src/plugins/opcua/open62541/qopen62541subscription.h
@@ -64,6 +64,8 @@ public:
uintptr_t handle;
QOpcUa::NodeAttribute attr;
UA_UInt32 monitoredItemId;
+ UA_UInt32 clientHandle;
+ QOpcUaMonitoringParameters parameters;
MonitoredItem(uintptr_t h, QOpcUa::NodeAttribute a, UA_UInt32 id)
: handle(h)
, attr(a)
@@ -85,6 +87,9 @@ private:
MonitoredItem *getItemForAttribute(uintptr_t handle, QOpcUa::NodeAttribute attr);
UA_ExtensionObject createFilter(const QVariant &filterData);
+ bool modifySubscriptionParameters(uintptr_t handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value);
+ bool modifyMonitoredItemParameters(uintptr_t handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value);
+
Open62541AsyncBackend *m_backend;
double m_interval;
UA_UInt32 m_subscriptionId;
@@ -92,9 +97,12 @@ private:
UA_UInt32 m_maxKeepaliveCount;
QOpcUaMonitoringParameters::SubscriptionType m_shared;
quint8 m_priority;
+ quint32 m_maxNotificationsPerPublish;
QHash<uintptr_t, QHash<QOpcUa::NodeAttribute, MonitoredItem *>> m_handleToItemMapping; // Handle -> Attribute -> MonitoredItem
QHash<UA_UInt32, MonitoredItem *> m_itemIdToItemMapping; // ItemId -> Item for fast lookup on data change
+
+ quint32 m_clientHandle;
};
QT_END_NAMESPACE
diff --git a/tests/auto/qopcuaclient/tst_client.cpp b/tests/auto/qopcuaclient/tst_client.cpp
index 5326817..6eb6ee6 100644
--- a/tests/auto/qopcuaclient/tst_client.cpp
+++ b/tests/auto/qopcuaclient/tst_client.cpp
@@ -231,6 +231,13 @@ private slots:
void subscriptionIndexRange();
defineDataMethod(subscriptionDataChangeFilter_data)
void subscriptionDataChangeFilter();
+ defineDataMethod(modifyPublishingMode_data)
+ void modifyPublishingMode();
+ defineDataMethod(modifyMonitoringMode_data)
+ void modifyMonitoringMode();
+ defineDataMethod(modifyMonitoredItem_data)
+ void modifyMonitoredItem();
+
defineDataMethod(stringCharset_data)
void stringCharset();
@@ -670,20 +677,6 @@ void Tst_QOpcUaClient::dataChangeSubscription()
QCOMPARE(node->monitoringStatus(QOpcUa::NodeAttribute::Value).publishingInterval(), 200.0);
QCOMPARE(node->monitoringStatus(QOpcUa::NodeAttribute::DisplayName).publishingInterval(), 200.0);
-
- monitoringModifiedSpy.clear();
- QOpcUaMonitoringParameters::DataChangeFilter filter;
- filter.deadbandType = QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType::Absolute;
- filter.trigger = QOpcUaMonitoringParameters::DataChangeFilter::DataChangeTrigger::StatusValue;
- filter.deadbandValue = 10;
- node->modifyMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters::Parameter::Filter, QVariant::fromValue(filter));
- monitoringModifiedSpy.wait();
- QVERIFY(monitoringModifiedSpy.size() == 1);
- QVERIFY(monitoringModifiedSpy.at(0).at(0).value<QOpcUa::NodeAttribute>() == QOpcUa::NodeAttribute::Value);
- QVERIFY(monitoringModifiedSpy.at(0).at(1).value<QOpcUaMonitoringParameters::Parameters>() & QOpcUaMonitoringParameters::Parameter::Filter);
- QEXPECT_FAIL("", "Modifying monitored items is not yet supported by open62541/uacpp", Continue);
- QVERIFY(monitoringModifiedSpy.at(0).at(2).value<QOpcUa::UaStatusCode>() == QOpcUa::UaStatusCode::Good);
-
} else {
qDebug() << "Modifying monitoring settings is not supported by the freeopcua backend";
}
@@ -1869,15 +1862,79 @@ void Tst_QOpcUaClient::subscriptionDataChangeFilter()
QVERIFY(doubleNode != 0);
QOpcUaMonitoringParameters p(100);
+
+ QSignalSpy monitoringEnabledSpy(doubleNode.data(), &QOpcUaNode::enableMonitoringFinished);
+ QSignalSpy monitoringDisabledSpy(doubleNode.data(), &QOpcUaNode::disableMonitoringFinished);
+ QSignalSpy dataChangeSpy(doubleNode.data(), &QOpcUaNode::attributeUpdated);
+ QSignalSpy monitoringModifiedSpy(doubleNode.data(), &QOpcUaNode::monitoringStatusChanged);
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 1.0, QOpcUa::Types::Double);
+
+ doubleNode->enableMonitoring(QOpcUa::NodeAttribute::Value, p);
+ monitoringEnabledSpy.wait();
+ QCOMPARE(monitoringEnabledSpy.size(), 1);
+ QCOMPARE(monitoringEnabledSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value);
+ QCOMPARE(monitoringEnabledSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good);
+
+ dataChangeSpy.wait(); // Wait for the initial data change
+ QCOMPARE(dataChangeSpy.size(), 1);
+ dataChangeSpy.clear();
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 1.5, QOpcUa::Types::Double);
+
+ dataChangeSpy.wait();
+ QCOMPARE(dataChangeSpy.size(), 1); // Data change without filter
+ QCOMPARE(doubleNode->attribute(QOpcUa::NodeAttribute::Value), 1.5);
+ dataChangeSpy.clear();
+
QOpcUaMonitoringParameters::DataChangeFilter filter;
filter.deadbandType = QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType::Absolute;
filter.trigger = QOpcUaMonitoringParameters::DataChangeFilter::DataChangeTrigger::StatusValue;
filter.deadbandValue = 1.0;
- p.setDataChangeFilter(filter);
+ doubleNode->modifyDataChangeFilter(QOpcUa::NodeAttribute::Value, filter);
+ monitoringModifiedSpy.wait();
+ QVERIFY(monitoringModifiedSpy.size() == 1);
+ QVERIFY(monitoringModifiedSpy.at(0).at(0).value<QOpcUa::NodeAttribute>() == QOpcUa::NodeAttribute::Value);
+ QVERIFY(monitoringModifiedSpy.at(0).at(1).value<QOpcUaMonitoringParameters::Parameters>() & QOpcUaMonitoringParameters::Parameter::Filter);
+ QVERIFY(monitoringModifiedSpy.at(0).at(2).value<QOpcUa::UaStatusCode>() == QOpcUa::UaStatusCode::Good);
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 2.0, QOpcUa::Types::Double);
+
+ dataChangeSpy.wait();
+ QCOMPARE(dataChangeSpy.size(), 0); // Filter is active and delta is < 1
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 3.0, QOpcUa::Types::Double);
+
+ dataChangeSpy.wait();
+ QCOMPARE(dataChangeSpy.size(), 1); // delta == 1, a data change is expected
+ QCOMPARE(doubleNode->attribute(QOpcUa::NodeAttribute::Value), 3.0);
+
+ doubleNode->disableMonitoring(QOpcUa::NodeAttribute::Value);
+ monitoringDisabledSpy.wait();
+ QCOMPARE(monitoringDisabledSpy.size(), 1);
+ QCOMPARE(monitoringDisabledSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value);
+ QCOMPARE(monitoringDisabledSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good);
+}
+
+void Tst_QOpcUaClient::modifyPublishingMode()
+{
+ QFETCH(QOpcUaClient *, opcuaClient);
+ OpcuaConnector connector(opcuaClient, m_endpoint);
+
+ if (opcuaClient->backend() == QLatin1String("freeopcua"))
+ QSKIP("Modification of monitoring is not supported in the freeopcua plugin");
+ if (opcuaClient->backend() == QLatin1String("uacpp"))
+ QSKIP("Modification of monitoring is not supported in the unified automation plugin");
+
+ QScopedPointer<QOpcUaNode> doubleNode(opcuaClient->node("ns=2;s=Demo.Static.Scalar.Double"));
+ QVERIFY(doubleNode != 0);
+
+ QOpcUaMonitoringParameters p(100);
QSignalSpy monitoringEnabledSpy(doubleNode.data(), &QOpcUaNode::enableMonitoringFinished);
QSignalSpy monitoringDisabledSpy(doubleNode.data(), &QOpcUaNode::disableMonitoringFinished);
QSignalSpy dataChangeSpy(doubleNode.data(), &QOpcUaNode::attributeUpdated);
+ QSignalSpy monitoringStatusSpy(doubleNode.data(), &QOpcUaNode::monitoringStatusChanged);
WRITE_VALUE_ATTRIBUTE(doubleNode, 1.0, QOpcUa::Types::Double);
@@ -1894,13 +1951,133 @@ void Tst_QOpcUaClient::subscriptionDataChangeFilter()
WRITE_VALUE_ATTRIBUTE(doubleNode, 1.5, QOpcUa::Types::Double);
dataChangeSpy.wait();
+ QCOMPARE(dataChangeSpy.size(), 1);
+ dataChangeSpy.clear();
+
+ doubleNode->modifyMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters::Parameter::PublishingEnabled, false);
+ monitoringStatusSpy.wait();
+ QCOMPARE(monitoringStatusSpy.size(), 1);
+ QCOMPARE(monitoringStatusSpy.at(0).at(2).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good);
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 3.0, QOpcUa::Types::Double);
+
+ dataChangeSpy.wait();
QCOMPARE(dataChangeSpy.size(), 0);
+ doubleNode->disableMonitoring(QOpcUa::NodeAttribute::Value);
+ monitoringDisabledSpy.wait();
+ QCOMPARE(monitoringDisabledSpy.size(), 1);
+ QCOMPARE(monitoringDisabledSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value);
+ QCOMPARE(monitoringDisabledSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good);
+}
+
+void Tst_QOpcUaClient::modifyMonitoringMode()
+{
+ QFETCH(QOpcUaClient *, opcuaClient);
+ OpcuaConnector connector(opcuaClient, m_endpoint);
+
+ if (opcuaClient->backend() == QLatin1String("freeopcua"))
+ QSKIP("Modification of monitoring is not supported in the freeopcua plugin");
+ if (opcuaClient->backend() == QLatin1String("uacpp"))
+ QSKIP("Modification of monitoring is not supported in the unified automation plugin");
+
+ QScopedPointer<QOpcUaNode> doubleNode(opcuaClient->node("ns=2;s=Demo.Static.Scalar.Double"));
+ QVERIFY(doubleNode != 0);
+
+ QOpcUaMonitoringParameters p(100);
+
+ QSignalSpy monitoringEnabledSpy(doubleNode.data(), &QOpcUaNode::enableMonitoringFinished);
+ QSignalSpy monitoringDisabledSpy(doubleNode.data(), &QOpcUaNode::disableMonitoringFinished);
+ QSignalSpy dataChangeSpy(doubleNode.data(), &QOpcUaNode::attributeUpdated);
+ QSignalSpy monitoringStatusSpy(doubleNode.data(), &QOpcUaNode::monitoringStatusChanged);
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 1.0, QOpcUa::Types::Double);
+
+ doubleNode->enableMonitoring(QOpcUa::NodeAttribute::Value, p);
+ monitoringEnabledSpy.wait();
+ QCOMPARE(monitoringEnabledSpy.size(), 1);
+ QCOMPARE(monitoringEnabledSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value);
+ QCOMPARE(monitoringEnabledSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good);
+
+ dataChangeSpy.wait(); // Wait for the initial data change
+ QCOMPARE(dataChangeSpy.size(), 1);
+ dataChangeSpy.clear();
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 1.5, QOpcUa::Types::Double);
+
+ dataChangeSpy.wait();
+ QCOMPARE(dataChangeSpy.size(), 1);
+ dataChangeSpy.clear();
+
+ doubleNode->modifyMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters::Parameter::MonitoringMode,
+ QVariant::fromValue(QOpcUaMonitoringParameters::MonitoringMode::Disabled));
+ monitoringStatusSpy.wait();
+ QCOMPARE(monitoringStatusSpy.size(), 1);
+ QCOMPARE(monitoringStatusSpy.at(0).at(2).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good);
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 3.0, QOpcUa::Types::Double);
+
+ dataChangeSpy.wait();
+ QCOMPARE(dataChangeSpy.size(), 0);
+
+ doubleNode->disableMonitoring(QOpcUa::NodeAttribute::Value);
+ monitoringDisabledSpy.wait();
+ QCOMPARE(monitoringDisabledSpy.size(), 1);
+ QCOMPARE(monitoringDisabledSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value);
+ QCOMPARE(monitoringDisabledSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good);
+}
+
+void Tst_QOpcUaClient::modifyMonitoredItem()
+{
+ QFETCH(QOpcUaClient *, opcuaClient);
+ OpcuaConnector connector(opcuaClient, m_endpoint);
+
+ if (opcuaClient->backend() == QLatin1String("freeopcua"))
+ QSKIP("Modification of monitoring is not supported in the freeopcua plugin");
+ if (opcuaClient->backend() == QLatin1String("uacpp"))
+ QSKIP("Modification of monitoring is not supported in the unified automation plugin");
+
+
+ QScopedPointer<QOpcUaNode> doubleNode(opcuaClient->node("ns=2;s=Demo.Static.Scalar.Double"));
+ QVERIFY(doubleNode != 0);
+
+ QOpcUaMonitoringParameters p(100);
+
+ QSignalSpy monitoringEnabledSpy(doubleNode.data(), &QOpcUaNode::enableMonitoringFinished);
+ QSignalSpy monitoringDisabledSpy(doubleNode.data(), &QOpcUaNode::disableMonitoringFinished);
+ QSignalSpy dataChangeSpy(doubleNode.data(), &QOpcUaNode::attributeUpdated);
+ QSignalSpy monitoringStatusSpy(doubleNode.data(), &QOpcUaNode::monitoringStatusChanged);
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 1.0, QOpcUa::Types::Double);
+
+ doubleNode->enableMonitoring(QOpcUa::NodeAttribute::Value, p);
+ monitoringEnabledSpy.wait();
+ QCOMPARE(monitoringEnabledSpy.size(), 1);
+ QCOMPARE(monitoringEnabledSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value);
+ QCOMPARE(monitoringEnabledSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good);
+
+ dataChangeSpy.wait(); // Wait for the initial data change
+ QCOMPARE(dataChangeSpy.size(), 1);
+ dataChangeSpy.clear();
+
+ WRITE_VALUE_ATTRIBUTE(doubleNode, 1.5, QOpcUa::Types::Double);
+
+ dataChangeSpy.wait();
+ QCOMPARE(dataChangeSpy.size(), 1);
+ QCOMPARE(dataChangeSpy.at(0).at(1).value<double>(), 1.5);
+ dataChangeSpy.clear();
+
+ doubleNode->modifyMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters::Parameter::SamplingInterval, 50.0);
+ monitoringStatusSpy.wait();
+ QCOMPARE(monitoringStatusSpy.size(), 1);
+ QCOMPARE(monitoringStatusSpy.at(0).at(2).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good);
+ QCOMPARE(monitoringStatusSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value);
+ QVERIFY(monitoringStatusSpy.at(0).at(1).value<QOpcUaMonitoringParameters::Parameters>() & QOpcUaMonitoringParameters::Parameter::SamplingInterval);
+
WRITE_VALUE_ATTRIBUTE(doubleNode, 3.0, QOpcUa::Types::Double);
dataChangeSpy.wait();
QCOMPARE(dataChangeSpy.size(), 1);
- QCOMPARE(doubleNode->attribute(QOpcUa::NodeAttribute::Value), 3.0);
doubleNode->disableMonitoring(QOpcUa::NodeAttribute::Value);
monitoringDisabledSpy.wait();