diff options
Diffstat (limited to 'src/plugins/opcua/open62541/qopen62541subscription.cpp')
-rw-r--r-- | src/plugins/opcua/open62541/qopen62541subscription.cpp | 295 |
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 |