| 1 | // Copyright (C) 2017 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "qopen62541backend.h" |
| 5 | #include "qopen62541client.h" |
| 6 | #include "qopen62541node.h" |
| 7 | #include "qopen62541subscription.h" |
| 8 | #include "qopen62541utils.h" |
| 9 | #include "qopen62541valueconverter.h" |
| 10 | #include "qopen62541utils.h" |
| 11 | #include <private/qopcuanode_p.h> |
| 12 | |
| 13 | #include "qopcuacontentfilterelementresult.h" |
| 14 | |
| 15 | #include <QtCore/qloggingcategory.h> |
| 16 | |
| 17 | QT_BEGIN_NAMESPACE |
| 18 | |
| 19 | Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_OPEN62541) |
| 20 | |
| 21 | static void monitoredValueHandler(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, UA_DataValue *value) |
| 22 | { |
| 23 | Q_UNUSED(client) |
| 24 | Q_UNUSED(subId) |
| 25 | Q_UNUSED(subContext) |
| 26 | QOpen62541Subscription *subscription = static_cast<QOpen62541Subscription *>(monContext); |
| 27 | subscription->monitoredValueUpdated(monId, value); |
| 28 | } |
| 29 | |
| 30 | static void stateChangeHandler(UA_Client *client, UA_UInt32 subId, void *subContext, UA_StatusChangeNotification *notification) |
| 31 | { |
| 32 | Q_UNUSED(client); |
| 33 | Q_UNUSED(subId); |
| 34 | |
| 35 | if (notification->status != UA_STATUSCODE_BADTIMEOUT) |
| 36 | return; |
| 37 | |
| 38 | QOpen62541Subscription *sub = static_cast<QOpen62541Subscription *>(subContext); |
| 39 | sub->sendTimeoutNotification(); |
| 40 | } |
| 41 | |
| 42 | static void eventHandler(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, |
| 43 | size_t numFields, UA_Variant *eventFields) |
| 44 | { |
| 45 | Q_UNUSED(client); |
| 46 | Q_UNUSED(subId); |
| 47 | Q_UNUSED(subContext); |
| 48 | |
| 49 | QOpen62541Subscription *subscription = static_cast<QOpen62541Subscription *>(monContext); |
| 50 | |
| 51 | QVariantList list; |
| 52 | for (size_t i = 0; i < numFields; ++i) |
| 53 | list.append(t: QOpen62541ValueConverter::toQVariant(eventFields[i])); |
| 54 | subscription->eventReceived(monId, list); |
| 55 | } |
| 56 | |
| 57 | QOpen62541Subscription::QOpen62541Subscription(Open62541AsyncBackend *backend, const QOpcUaMonitoringParameters &settings) |
| 58 | : m_backend(backend) |
| 59 | , m_interval(settings.publishingInterval()) |
| 60 | , m_subscriptionId(0) |
| 61 | , m_lifetimeCount(settings.lifetimeCount() ? settings.lifetimeCount() : UA_CreateSubscriptionRequest_default().requestedLifetimeCount) |
| 62 | , m_maxKeepaliveCount(settings.maxKeepAliveCount() ? settings.maxKeepAliveCount() : UA_CreateSubscriptionRequest_default().requestedMaxKeepAliveCount) |
| 63 | , m_shared(settings.subscriptionType()) |
| 64 | , m_priority(settings.priority()) |
| 65 | , m_maxNotificationsPerPublish(settings.maxNotificationsPerPublish()) |
| 66 | , m_clientHandle(0) |
| 67 | , m_timeout(false) |
| 68 | { |
| 69 | } |
| 70 | |
| 71 | QOpen62541Subscription::~QOpen62541Subscription() |
| 72 | { |
| 73 | removeOnServer(); |
| 74 | } |
| 75 | |
| 76 | UA_UInt32 QOpen62541Subscription::createOnServer() |
| 77 | { |
| 78 | UA_CreateSubscriptionRequest req = UA_CreateSubscriptionRequest_default(); |
| 79 | req.requestedPublishingInterval = m_interval; |
| 80 | req.requestedLifetimeCount = m_lifetimeCount; |
| 81 | req.requestedMaxKeepAliveCount = m_maxKeepaliveCount; |
| 82 | req.priority = m_priority; |
| 83 | req.maxNotificationsPerPublish = m_maxNotificationsPerPublish; |
| 84 | UA_CreateSubscriptionResponse res = UA_Client_Subscriptions_create(client: m_backend->m_uaclient, request: req, subscriptionContext: this, statusChangeCallback: stateChangeHandler, deleteCallback: nullptr); |
| 85 | |
| 86 | if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
| 87 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not create subscription with interval" << m_interval << UA_StatusCode_name(code: res.responseHeader.serviceResult); |
| 88 | return 0; |
| 89 | } |
| 90 | |
| 91 | m_subscriptionId = res.subscriptionId; |
| 92 | m_maxKeepaliveCount = res.revisedMaxKeepAliveCount; |
| 93 | m_lifetimeCount = res.revisedLifetimeCount; |
| 94 | m_interval = res.revisedPublishingInterval; |
| 95 | return m_subscriptionId; |
| 96 | } |
| 97 | |
| 98 | bool QOpen62541Subscription::removeOnServer() |
| 99 | { |
| 100 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
| 101 | if (m_subscriptionId) { |
| 102 | res = UA_Client_Subscriptions_deleteSingle(client: m_backend->m_uaclient, subscriptionId: m_subscriptionId); |
| 103 | m_subscriptionId = 0; |
| 104 | } |
| 105 | |
| 106 | for (auto it : std::as_const(t&: m_itemIdToItemMapping)) { |
| 107 | QOpcUaMonitoringParameters s; |
| 108 | s.setStatusCode(m_timeout ? QOpcUa::UaStatusCode::BadTimeout : QOpcUa::UaStatusCode::BadDisconnect); |
| 109 | emit m_backend->monitoringEnableDisable(handle: it->handle, attr: it->attr, subscribe: false, status: s); |
| 110 | } |
| 111 | |
| 112 | qDeleteAll(c: m_itemIdToItemMapping); |
| 113 | |
| 114 | m_itemIdToItemMapping.clear(); |
| 115 | m_nodeHandleToItemMapping.clear(); |
| 116 | |
| 117 | return (res == UA_STATUSCODE_GOOD) ? true : false; |
| 118 | } |
| 119 | |
| 120 | void QOpen62541Subscription::modifyMonitoring(quint64 handle, QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value) |
| 121 | { |
| 122 | MonitoredItem *monItem = getItemForAttribute(nodeHandle: handle, attr); |
| 123 | if (!monItem) { |
| 124 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify parameter" << item << "there are no monitored items" ; |
| 125 | QOpcUaMonitoringParameters p; |
| 126 | p.setStatusCode(QOpcUa::UaStatusCode::BadAttributeIdInvalid); |
| 127 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 128 | return; |
| 129 | } |
| 130 | |
| 131 | QOpcUaMonitoringParameters p = monItem->parameters; |
| 132 | p.setStatusCode(QOpcUa::UaStatusCode::BadNotImplemented); |
| 133 | |
| 134 | // SetPublishingMode service |
| 135 | if (item == QOpcUaMonitoringParameters::Parameter::PublishingEnabled) { |
| 136 | if (value.metaType().id() != QMetaType::Bool) { |
| 137 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "New value for PublishingEnabled is not a boolean" ; |
| 138 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 139 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 140 | return; |
| 141 | } |
| 142 | |
| 143 | UA_SetPublishingModeRequest req; |
| 144 | UA_SetPublishingModeRequest_init(p: &req); |
| 145 | UaDeleter<UA_SetPublishingModeRequest> requestDeleter(&req, UA_SetPublishingModeRequest_clear); |
| 146 | req.publishingEnabled = value.toBool(); |
| 147 | req.subscriptionIdsSize = 1; |
| 148 | req.subscriptionIds = UA_UInt32_new(); |
| 149 | *req.subscriptionIds = m_subscriptionId; |
| 150 | UA_SetPublishingModeResponse res = UA_Client_Subscriptions_setPublishingMode(client: m_backend->m_uaclient, request: req); |
| 151 | UaDeleter<UA_SetPublishingModeResponse> responseDeleter(&res, UA_SetPublishingModeResponse_clear); |
| 152 | |
| 153 | if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
| 154 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to set publishing mode:" << res.responseHeader.serviceResult; |
| 155 | p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult)); |
| 156 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 157 | return; |
| 158 | } |
| 159 | |
| 160 | if (res.resultsSize && res.results[0] == UA_STATUSCODE_GOOD) |
| 161 | p.setPublishingEnabled(value.toBool()); |
| 162 | |
| 163 | p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.results[0])); |
| 164 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 165 | |
| 166 | return; |
| 167 | } |
| 168 | |
| 169 | // SetMonitoringMode service |
| 170 | if (item == QOpcUaMonitoringParameters::Parameter::MonitoringMode) { |
| 171 | if (value.userType() != QMetaType::fromType<QOpcUaMonitoringParameters::MonitoringMode>().id()) { |
| 172 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "New value for MonitoringMode is not a monitoring mode" ; |
| 173 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 174 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 175 | return; |
| 176 | } |
| 177 | |
| 178 | UA_SetMonitoringModeRequest req; |
| 179 | UA_SetMonitoringModeRequest_init(p: &req); |
| 180 | UaDeleter<UA_SetMonitoringModeRequest> requestDeleter(&req, UA_SetMonitoringModeRequest_clear); |
| 181 | req.monitoringMode = static_cast<UA_MonitoringMode>(value.value<QOpcUaMonitoringParameters::MonitoringMode>()); |
| 182 | req.monitoredItemIdsSize = 1; |
| 183 | req.monitoredItemIds = UA_UInt32_new(); |
| 184 | *req.monitoredItemIds = monItem->monitoredItemId; |
| 185 | req.subscriptionId = m_subscriptionId; |
| 186 | UA_SetMonitoringModeResponse res = UA_Client_MonitoredItems_setMonitoringMode(client: m_backend->m_uaclient, request: req); |
| 187 | UaDeleter<UA_SetMonitoringModeResponse> responseDeleter(&res, UA_SetMonitoringModeResponse_clear); |
| 188 | |
| 189 | if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
| 190 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to set monitoring mode:" << res.responseHeader.serviceResult; |
| 191 | p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult)); |
| 192 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 193 | return; |
| 194 | } |
| 195 | |
| 196 | if (res.resultsSize && res.results[0] == UA_STATUSCODE_GOOD) |
| 197 | p.setMonitoringMode(value.value<QOpcUaMonitoringParameters::MonitoringMode>()); |
| 198 | |
| 199 | p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.results[0])); |
| 200 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 201 | return; |
| 202 | } |
| 203 | |
| 204 | // SetTriggering service |
| 205 | if (item == QOpcUaMonitoringParameters::Parameter::TriggeredItemIds) { |
| 206 | if (!value.canConvert<QSet<quint32>>()) { |
| 207 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify triggering item id, value is not a set of quint32" ; |
| 208 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 209 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 210 | return; |
| 211 | } |
| 212 | |
| 213 | auto triggeredItemIds = value.value<QSet<quint32>>(); |
| 214 | |
| 215 | UA_SetTriggeringRequest triggeringReq; |
| 216 | UA_SetTriggeringRequest_init(p: &triggeringReq); |
| 217 | triggeringReq.subscriptionId = m_subscriptionId; |
| 218 | triggeringReq.triggeringItemId = monItem->monitoredItemId; |
| 219 | |
| 220 | QList<quint32> itemsToRemove; |
| 221 | QList<quint32> itemsToAdd; |
| 222 | |
| 223 | if (triggeredItemIds.isEmpty() && !monItem->parameters.triggeredItemIds().isEmpty()) { |
| 224 | itemsToRemove = monItem->parameters.triggeredItemIds().values(); |
| 225 | } else if (!triggeredItemIds.isEmpty()) { |
| 226 | itemsToAdd = triggeredItemIds.values(); |
| 227 | itemsToRemove = monItem->parameters.triggeredItemIds().subtract(other: triggeredItemIds).values(); |
| 228 | } |
| 229 | |
| 230 | if (itemsToAdd.isEmpty() && itemsToRemove.isEmpty()) { |
| 231 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Nothing to do for TriggeredItemIds" ; |
| 232 | p.setStatusCode(QOpcUa::UaStatusCode::Good); |
| 233 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 234 | return; |
| 235 | } |
| 236 | |
| 237 | if (!itemsToAdd.isEmpty()) { |
| 238 | triggeringReq.linksToAddSize = itemsToAdd.size(); |
| 239 | triggeringReq.linksToAdd = itemsToAdd.data(); |
| 240 | } |
| 241 | |
| 242 | if (!itemsToRemove.isEmpty()) { |
| 243 | triggeringReq.linksToRemoveSize = itemsToRemove.size(); |
| 244 | triggeringReq.linksToRemove = itemsToRemove.data(); |
| 245 | } |
| 246 | |
| 247 | auto triggeringRes = UA_Client_MonitoredItems_setTriggering(client: m_backend->m_uaclient, request: triggeringReq); |
| 248 | |
| 249 | QHash<quint32, QOpcUa::UaStatusCode> failedItems; |
| 250 | |
| 251 | if (triggeringRes.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
| 252 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Modifying TriggeredItemIds failed with" |
| 253 | << UA_StatusCode_name(code: triggeringRes.responseHeader.serviceResult); |
| 254 | |
| 255 | for (const auto &entry : std::as_const(t&: itemsToAdd)) |
| 256 | failedItems[entry] = QOpcUa::UaStatusCode(triggeringRes.responseHeader.serviceResult); |
| 257 | p.setFailedTriggeredItemsStatus(failedItems); |
| 258 | p.setStatusCode(QOpcUa::UaStatusCode(triggeringRes.responseHeader.serviceResult)); |
| 259 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 260 | UA_SetTriggeringResponse_clear(p: &triggeringRes); |
| 261 | return; |
| 262 | } |
| 263 | |
| 264 | for (size_t i = 0; i < triggeringRes.addResultsSize; ++i) { |
| 265 | if (triggeringRes.addResults[i] != UA_STATUSCODE_GOOD) { |
| 266 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to add trigger link" << triggeringReq.triggeringItemId |
| 267 | << "->" << itemsToAdd.at(i) << "on subscription" << m_subscriptionId |
| 268 | << "with status" |
| 269 | << UA_StatusCode_name(code: triggeringRes.addResults[i]); |
| 270 | failedItems.insert(key: itemsToAdd.at(i), value: QOpcUa::UaStatusCode(triggeringRes.addResults[i])); |
| 271 | triggeredItemIds.remove(value: itemsToAdd.at(i)); |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | UA_SetTriggeringResponse_clear(p: &triggeringRes); |
| 276 | |
| 277 | monItem->parameters.setTriggeredItemIds(triggeredItemIds); |
| 278 | p.setStatusCode(QOpcUa::UaStatusCode::Good); |
| 279 | p.setTriggeredItemIds(triggeredItemIds); |
| 280 | p.setFailedTriggeredItemsStatus(failedItems); |
| 281 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 282 | |
| 283 | return; |
| 284 | } |
| 285 | |
| 286 | if (modifySubscriptionParameters(nodeHandle: handle, attr, item, value)) |
| 287 | return; |
| 288 | if (modifyMonitoredItemParameters(nodeHandle: handle, attr, item, value)) |
| 289 | return; |
| 290 | |
| 291 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Modifying" << item << "is not implemented" ; |
| 292 | p.setStatusCode(QOpcUa::UaStatusCode::BadNotImplemented); |
| 293 | emit m_backend->monitoringStatusChanged(handle, attr, items: item, param: p); |
| 294 | } |
| 295 | |
| 296 | bool QOpen62541Subscription::addAttributeMonitoredItem(quint64 handle, QOpcUa::NodeAttribute attr, const UA_NodeId &id, QOpcUaMonitoringParameters settings) |
| 297 | { |
| 298 | UA_MonitoredItemCreateRequest req; |
| 299 | UA_MonitoredItemCreateRequest_init(p: &req); |
| 300 | UaDeleter<UA_MonitoredItemCreateRequest> requestDeleter(&req, UA_MonitoredItemCreateRequest_clear); |
| 301 | req.itemToMonitor.attributeId = QOpen62541ValueConverter::toUaAttributeId(attr); |
| 302 | UA_NodeId_copy(src: &id, dst: &(req.itemToMonitor.nodeId)); |
| 303 | if (settings.indexRange().size()) |
| 304 | QOpen62541ValueConverter::scalarFromQt<UA_String, QString>(var: settings.indexRange(), ptr: &req.itemToMonitor.indexRange); |
| 305 | req.monitoringMode = static_cast<UA_MonitoringMode>(settings.monitoringMode()); |
| 306 | req.requestedParameters.samplingInterval = qFuzzyCompare(p1: settings.samplingInterval(), p2: 0.0) ? m_interval : settings.samplingInterval(); |
| 307 | req.requestedParameters.queueSize = settings.queueSize() == 0 ? 1 : settings.queueSize(); |
| 308 | req.requestedParameters.discardOldest = settings.discardOldest(); |
| 309 | req.requestedParameters.clientHandle = ++m_clientHandle; |
| 310 | |
| 311 | if (settings.filter().isValid()) { |
| 312 | UA_ExtensionObject filter = createFilter(filterData: settings.filter()); |
| 313 | if (filter.content.decoded.data) |
| 314 | req.requestedParameters.filter = filter; |
| 315 | else { |
| 316 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not create monitored item, filter creation failed" ; |
| 317 | QOpcUaMonitoringParameters s; |
| 318 | s.setStatusCode(QOpcUa::UaStatusCode::BadInternalError); |
| 319 | emit m_backend->monitoringEnableDisable(handle, attr, subscribe: true, status: s); |
| 320 | return false; |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | UA_MonitoredItemCreateResult res; |
| 325 | UaDeleter<UA_MonitoredItemCreateResult> resultDeleter(&res, UA_MonitoredItemCreateResult_clear); |
| 326 | |
| 327 | if (attr == QOpcUa::NodeAttribute::EventNotifier && settings.filter().canConvert<QOpcUaMonitoringParameters::EventFilter>()) |
| 328 | res = UA_Client_MonitoredItems_createEvent(client: m_backend->m_uaclient, subscriptionId: m_subscriptionId, |
| 329 | timestampsToReturn: UA_TIMESTAMPSTORETURN_BOTH, item: req, context: this, callback: eventHandler, deleteCallback: nullptr); |
| 330 | else |
| 331 | res = UA_Client_MonitoredItems_createDataChange(client: m_backend->m_uaclient, subscriptionId: m_subscriptionId, timestampsToReturn: UA_TIMESTAMPSTORETURN_BOTH, item: req, context: this, callback: monitoredValueHandler, deleteCallback: nullptr); |
| 332 | |
| 333 | if (res.statusCode != UA_STATUSCODE_GOOD) { |
| 334 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not add monitored item for" << attr << "of node" << Open62541Utils::nodeIdToQString(id) << ":" << UA_StatusCode_name(code: res.statusCode); |
| 335 | QOpcUaMonitoringParameters s; |
| 336 | s.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.statusCode)); |
| 337 | emit m_backend->monitoringEnableDisable(handle, attr, subscribe: true, status: s); |
| 338 | return false; |
| 339 | } |
| 340 | |
| 341 | QSet<quint32> successfulTriggerLinks; |
| 342 | QHash<quint32, QOpcUa::UaStatusCode> failedTriggerLinks; |
| 343 | if (!settings.triggeredItemIds().isEmpty()) { |
| 344 | auto triggeredItems = settings.triggeredItemIds().values(); |
| 345 | |
| 346 | UA_SetTriggeringRequest req; |
| 347 | UA_SetTriggeringRequest_init(p: &req); |
| 348 | req.subscriptionId = m_subscriptionId; |
| 349 | req.triggeringItemId = res.monitoredItemId; |
| 350 | req.linksToAddSize = triggeredItems.size(); |
| 351 | req.linksToAdd = triggeredItems.data(); |
| 352 | |
| 353 | auto triggeringRes = UA_Client_MonitoredItems_setTriggering(client: m_backend->m_uaclient, request: req); |
| 354 | |
| 355 | if (triggeringRes.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
| 356 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not set triggering item it for" << attr << "of node" << Open62541Utils::nodeIdToQString(id) << ":" |
| 357 | << UA_StatusCode_name(code: triggeringRes.responseHeader.serviceResult); |
| 358 | |
| 359 | // Remove the new monitored item |
| 360 | UA_DeleteMonitoredItemsRequest deleteRequest; |
| 361 | UA_DeleteMonitoredItemsRequest_init(p: &deleteRequest); |
| 362 | deleteRequest.subscriptionId = m_subscriptionId; |
| 363 | deleteRequest.monitoredItemIdsSize = 1; |
| 364 | deleteRequest.monitoredItemIds = &res.monitoredItemId; |
| 365 | UA_Client_MonitoredItems_delete(client: m_backend->m_uaclient, deleteRequest); |
| 366 | |
| 367 | for (const auto &entry : std::as_const(t&: triggeredItems)) |
| 368 | failedTriggerLinks.insert(key: entry, value: QOpcUa::UaStatusCode(triggeringRes.responseHeader.serviceResult)); |
| 369 | |
| 370 | QOpcUaMonitoringParameters s; |
| 371 | s.setStatusCode(static_cast<QOpcUa::UaStatusCode>(triggeringRes.responseHeader.serviceResult)); |
| 372 | s.setFailedTriggeredItemsStatus(failedTriggerLinks); |
| 373 | emit m_backend->monitoringEnableDisable(handle, attr, subscribe: true, status: s); |
| 374 | |
| 375 | UA_SetTriggeringResponse_clear(p: &triggeringRes); |
| 376 | |
| 377 | return false; |
| 378 | } |
| 379 | |
| 380 | for (size_t i = 0; i < triggeringRes.addResultsSize; ++i) { |
| 381 | if (triggeringRes.addResults[i] == UA_STATUSCODE_GOOD) { |
| 382 | successfulTriggerLinks.insert(value: triggeredItems.at(i)); |
| 383 | } else { |
| 384 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to add trigger link" << res.monitoredItemId |
| 385 | << "->" << triggeredItems.at(i) << "on subscription" << m_subscriptionId |
| 386 | << "with status" |
| 387 | << UA_StatusCode_name(code: triggeringRes.addResults[i]); |
| 388 | failedTriggerLinks.insert(key: triggeredItems.at(i), value: QOpcUa::UaStatusCode(triggeringRes.addResults[i])); |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | UA_SetTriggeringResponse_clear(p: &triggeringRes); |
| 393 | } |
| 394 | |
| 395 | MonitoredItem *temp = new MonitoredItem(handle, attr, res.monitoredItemId); |
| 396 | m_nodeHandleToItemMapping[handle][attr] = temp; |
| 397 | m_itemIdToItemMapping[res.monitoredItemId] = temp; |
| 398 | |
| 399 | QOpcUaMonitoringParameters s = settings; |
| 400 | s.setSubscriptionId(m_subscriptionId); |
| 401 | s.setPublishingInterval(m_interval); |
| 402 | s.setMaxKeepAliveCount(m_maxKeepaliveCount); |
| 403 | s.setLifetimeCount(m_lifetimeCount); |
| 404 | s.setStatusCode(QOpcUa::UaStatusCode::Good); |
| 405 | s.setSamplingInterval(res.revisedSamplingInterval); |
| 406 | s.setQueueSize(res.revisedQueueSize); |
| 407 | s.setMonitoredItemId(res.monitoredItemId); |
| 408 | s.setTriggeredItemIds(successfulTriggerLinks); |
| 409 | s.setFailedTriggeredItemsStatus(failedTriggerLinks); |
| 410 | temp->parameters = s; |
| 411 | temp->clientHandle = m_clientHandle; |
| 412 | |
| 413 | if (res.filterResult.encoding >= UA_EXTENSIONOBJECT_DECODED && |
| 414 | res.filterResult.content.decoded.type == &UA_TYPES[UA_TYPES_EVENTFILTERRESULT]) |
| 415 | s.setFilterResult(convertEventFilterResult(obj: &res.filterResult)); |
| 416 | else |
| 417 | s.clearFilterResult(); |
| 418 | |
| 419 | emit m_backend->monitoringEnableDisable(handle, attr, subscribe: true, status: s); |
| 420 | |
| 421 | return true; |
| 422 | } |
| 423 | |
| 424 | bool QOpen62541Subscription::removeAttributeMonitoredItem(quint64 handle, QOpcUa::NodeAttribute attr) |
| 425 | { |
| 426 | MonitoredItem *item = getItemForAttribute(nodeHandle: handle, attr); |
| 427 | if (!item) { |
| 428 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "There is no monitored item for this attribute" ; |
| 429 | QOpcUaMonitoringParameters s; |
| 430 | s.setStatusCode(QOpcUa::UaStatusCode::BadMonitoredItemIdInvalid); |
| 431 | emit m_backend->monitoringEnableDisable(handle, attr, subscribe: false, status: s); |
| 432 | return false; |
| 433 | } |
| 434 | |
| 435 | UA_StatusCode res = UA_Client_MonitoredItems_deleteSingle(client: m_backend->m_uaclient, subscriptionId: m_subscriptionId, monitoredItemId: item->monitoredItemId); |
| 436 | if (res != UA_STATUSCODE_GOOD) |
| 437 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not remove monitored item" << item->monitoredItemId << "from subscription" << m_subscriptionId << ":" << UA_StatusCode_name(code: res); |
| 438 | |
| 439 | m_itemIdToItemMapping.remove(key: item->monitoredItemId); |
| 440 | const auto it = m_nodeHandleToItemMapping.find(key: handle); |
| 441 | it->remove(key: attr); |
| 442 | if (it->empty()) |
| 443 | m_nodeHandleToItemMapping.remove(key: it.key()); |
| 444 | |
| 445 | delete item; |
| 446 | |
| 447 | QOpcUaMonitoringParameters s; |
| 448 | s.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res)); |
| 449 | emit m_backend->monitoringEnableDisable(handle, attr, subscribe: false, status: s); |
| 450 | |
| 451 | return true; |
| 452 | } |
| 453 | |
| 454 | void QOpen62541Subscription::monitoredValueUpdated(UA_UInt32 monId, UA_DataValue *value) |
| 455 | { |
| 456 | auto item = m_itemIdToItemMapping.constFind(key: monId); |
| 457 | if (item == m_itemIdToItemMapping.constEnd()) |
| 458 | return; |
| 459 | QOpcUaReadResult res; |
| 460 | |
| 461 | if (!value || value == UA_EMPTY_ARRAY_SENTINEL) { |
| 462 | res.setStatusCode(QOpcUa::UaStatusCode::Good); |
| 463 | emit m_backend->dataChangeOccurred(handle: item.value()->handle, res); |
| 464 | return; |
| 465 | } |
| 466 | |
| 467 | res.setValue(QOpen62541ValueConverter::toQVariant(value->value)); |
| 468 | res.setAttribute(item.value()->attr); |
| 469 | if (value->hasServerTimestamp) |
| 470 | res.setServerTimestamp(QOpen62541ValueConverter::scalarToQt<QDateTime, UA_DateTime>(data: &value->serverTimestamp)); |
| 471 | if (value->hasSourceTimestamp) |
| 472 | res.setSourceTimestamp(QOpen62541ValueConverter::scalarToQt<QDateTime, UA_DateTime>(data: &value->sourceTimestamp)); |
| 473 | res.setStatusCode(value->hasStatus ? QOpcUa::UaStatusCode(value->status) : QOpcUa::UaStatusCode::Good); |
| 474 | emit m_backend->dataChangeOccurred(handle: item.value()->handle, res); |
| 475 | } |
| 476 | |
| 477 | void QOpen62541Subscription::sendTimeoutNotification() |
| 478 | { |
| 479 | QList<QPair<quint64, QOpcUa::NodeAttribute>> items; |
| 480 | for (const auto &it : std::as_const(t&: m_nodeHandleToItemMapping)) { |
| 481 | for (auto item : it) { |
| 482 | items.push_back(t: {item->handle, item->attr}); |
| 483 | } |
| 484 | } |
| 485 | emit timeout(sub: this, items); |
| 486 | m_timeout = true; |
| 487 | } |
| 488 | |
| 489 | void QOpen62541Subscription::eventReceived(UA_UInt32 monId, QVariantList list) |
| 490 | { |
| 491 | auto item = m_itemIdToItemMapping.constFind(key: monId); |
| 492 | if (item == m_itemIdToItemMapping.constEnd()) |
| 493 | return; |
| 494 | emit m_backend->eventOccurred(handle: item.value()->handle, fields: list); |
| 495 | } |
| 496 | |
| 497 | double QOpen62541Subscription::interval() const |
| 498 | { |
| 499 | return m_interval; |
| 500 | } |
| 501 | |
| 502 | UA_UInt32 QOpen62541Subscription::subscriptionId() const |
| 503 | { |
| 504 | return m_subscriptionId; |
| 505 | } |
| 506 | |
| 507 | int QOpen62541Subscription::monitoredItemsCount() const |
| 508 | { |
| 509 | return m_itemIdToItemMapping.size(); |
| 510 | } |
| 511 | |
| 512 | QOpcUaMonitoringParameters::SubscriptionType QOpen62541Subscription::shared() const |
| 513 | { |
| 514 | return m_shared; |
| 515 | } |
| 516 | |
| 517 | QOpen62541Subscription::MonitoredItem *QOpen62541Subscription::getItemForAttribute(quint64 nodeHandle, QOpcUa::NodeAttribute attr) |
| 518 | { |
| 519 | auto nodeEntry = m_nodeHandleToItemMapping.constFind(key: nodeHandle); |
| 520 | |
| 521 | if (nodeEntry == m_nodeHandleToItemMapping.constEnd()) |
| 522 | return nullptr; |
| 523 | |
| 524 | auto item = nodeEntry->constFind(key: attr); |
| 525 | if (item == nodeEntry->constEnd()) |
| 526 | return nullptr; |
| 527 | |
| 528 | return item.value(); |
| 529 | } |
| 530 | |
| 531 | UA_ExtensionObject QOpen62541Subscription::createFilter(const QVariant &filterData) |
| 532 | { |
| 533 | UA_ExtensionObject obj; |
| 534 | UA_ExtensionObject_init(p: &obj); |
| 535 | |
| 536 | if (filterData.canConvert<QOpcUaMonitoringParameters::DataChangeFilter>()) { |
| 537 | createDataChangeFilter(filter: filterData.value<QOpcUaMonitoringParameters::DataChangeFilter>(), out: &obj); |
| 538 | return obj; |
| 539 | } |
| 540 | |
| 541 | if (filterData.canConvert<QOpcUaMonitoringParameters::EventFilter>()) { |
| 542 | Open62541Utils::createEventFilter(filter: filterData.value<QOpcUaMonitoringParameters::EventFilter>(), out: &obj); |
| 543 | return obj; |
| 544 | } |
| 545 | |
| 546 | if (filterData.isValid()) |
| 547 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not create filter, invalid input." ; |
| 548 | |
| 549 | return obj; |
| 550 | } |
| 551 | |
| 552 | void QOpen62541Subscription::createDataChangeFilter(const QOpcUaMonitoringParameters::DataChangeFilter &filter, UA_ExtensionObject *out) |
| 553 | { |
| 554 | UA_DataChangeFilter *uaFilter = UA_DataChangeFilter_new(); |
| 555 | uaFilter->deadbandType = static_cast<UA_UInt32>(filter.deadbandType()); |
| 556 | uaFilter->deadbandValue = filter.deadbandValue(); |
| 557 | uaFilter->trigger = static_cast<UA_DataChangeTrigger>(filter.trigger()); |
| 558 | out->encoding = UA_EXTENSIONOBJECT_DECODED; |
| 559 | out->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGEFILTER]; |
| 560 | out->content.decoded.data = uaFilter; |
| 561 | } |
| 562 | |
| 563 | QOpcUaEventFilterResult QOpen62541Subscription::convertEventFilterResult(UA_ExtensionObject *obj) |
| 564 | { |
| 565 | QOpcUaEventFilterResult result; |
| 566 | |
| 567 | if (!obj) |
| 568 | return result; |
| 569 | |
| 570 | if (obj->encoding == UA_EXTENSIONOBJECT_DECODED && obj->content.decoded.type == &UA_TYPES[UA_TYPES_EVENTFILTERRESULT]) { |
| 571 | UA_EventFilterResult *filterResult = static_cast<UA_EventFilterResult *>(obj->content.decoded.data); |
| 572 | |
| 573 | for (size_t i = 0; i < filterResult->selectClauseResultsSize; ++i) |
| 574 | result.selectClauseResultsRef().append(t: static_cast<QOpcUa::UaStatusCode>(filterResult->selectClauseResults[i])); |
| 575 | |
| 576 | for (size_t i = 0; i < filterResult->whereClauseResult.elementResultsSize; ++i) { |
| 577 | QOpcUaContentFilterElementResult temp; |
| 578 | temp.setStatusCode(static_cast<QOpcUa::UaStatusCode>(filterResult->whereClauseResult.elementResults[i].statusCode)); |
| 579 | for (size_t j = 0; j < filterResult->whereClauseResult.elementResults[i].operandStatusCodesSize; ++j) |
| 580 | temp.operandStatusCodesRef().append(t: static_cast<QOpcUa::UaStatusCode>( |
| 581 | filterResult->whereClauseResult.elementResults[i].operandStatusCodes[j])); |
| 582 | result.whereClauseResultsRef().append(t: temp); |
| 583 | } |
| 584 | } |
| 585 | |
| 586 | return result; |
| 587 | } |
| 588 | |
| 589 | bool QOpen62541Subscription::modifySubscriptionParameters(quint64 nodeHandle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value) |
| 590 | { |
| 591 | UA_ModifySubscriptionRequest req; |
| 592 | UA_ModifySubscriptionRequest_init(p: &req); |
| 593 | req.subscriptionId = m_subscriptionId; |
| 594 | req.requestedPublishingInterval = m_interval; |
| 595 | req.requestedLifetimeCount = m_lifetimeCount; |
| 596 | req.requestedMaxKeepAliveCount = m_maxKeepaliveCount; |
| 597 | req.maxNotificationsPerPublish = m_maxNotificationsPerPublish; |
| 598 | |
| 599 | bool match = true; |
| 600 | |
| 601 | switch (item) { |
| 602 | case QOpcUaMonitoringParameters::Parameter::PublishingInterval: { |
| 603 | bool ok; |
| 604 | req.requestedPublishingInterval = value.toDouble(ok: &ok); |
| 605 | if (!ok) { |
| 606 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify PublishingInterval, value is not a double" ; |
| 607 | QOpcUaMonitoringParameters p; |
| 608 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 609 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 610 | return true; |
| 611 | } |
| 612 | break; |
| 613 | } |
| 614 | case QOpcUaMonitoringParameters::Parameter::LifetimeCount: { |
| 615 | bool ok; |
| 616 | req.requestedLifetimeCount = value.toUInt(ok: &ok); |
| 617 | if (!ok) { |
| 618 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify LifetimeCount, value is not an integer" ; |
| 619 | QOpcUaMonitoringParameters p; |
| 620 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 621 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 622 | return true; |
| 623 | } |
| 624 | break; |
| 625 | } |
| 626 | case QOpcUaMonitoringParameters::Parameter::MaxKeepAliveCount: { |
| 627 | bool ok; |
| 628 | req.requestedMaxKeepAliveCount = value.toUInt(ok: &ok); |
| 629 | if (!ok) { |
| 630 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify MaxKeepAliveCount, value is not an integer" ; |
| 631 | QOpcUaMonitoringParameters p; |
| 632 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 633 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 634 | return true; |
| 635 | } |
| 636 | break; |
| 637 | } |
| 638 | case QOpcUaMonitoringParameters::Parameter::Priority: { |
| 639 | bool ok; |
| 640 | req.priority = value.toUInt(ok: &ok); |
| 641 | if (!ok) { |
| 642 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify Priority, value is not an integer" ; |
| 643 | QOpcUaMonitoringParameters p; |
| 644 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 645 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 646 | return true; |
| 647 | } |
| 648 | break; |
| 649 | } |
| 650 | case QOpcUaMonitoringParameters::Parameter::MaxNotificationsPerPublish: { |
| 651 | bool ok; |
| 652 | req.maxNotificationsPerPublish = value.toUInt(ok: &ok); |
| 653 | if (!ok) { |
| 654 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify MaxNotificationsPerPublish, value is not an integer" ; |
| 655 | QOpcUaMonitoringParameters p; |
| 656 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 657 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 658 | return true; |
| 659 | } |
| 660 | break; |
| 661 | } |
| 662 | default: |
| 663 | match = false; |
| 664 | break; |
| 665 | } |
| 666 | |
| 667 | if (match) { |
| 668 | UA_ModifySubscriptionResponse res = UA_Client_Subscriptions_modify(client: m_backend->m_uaclient, request: req); |
| 669 | |
| 670 | if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD) { |
| 671 | QOpcUaMonitoringParameters p; |
| 672 | p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult)); |
| 673 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 674 | } else { |
| 675 | QOpcUaMonitoringParameters::Parameters changed = item; |
| 676 | if (!qFuzzyCompare(p1: res.revisedPublishingInterval, p2: m_interval)) |
| 677 | changed |= QOpcUaMonitoringParameters::Parameter::PublishingInterval; |
| 678 | if (res.revisedLifetimeCount != m_lifetimeCount) |
| 679 | changed |= QOpcUaMonitoringParameters::Parameter::LifetimeCount; |
| 680 | if (res.revisedMaxKeepAliveCount != m_maxKeepaliveCount) |
| 681 | changed |= QOpcUaMonitoringParameters::Parameter::MaxKeepAliveCount; |
| 682 | |
| 683 | m_lifetimeCount = res.revisedLifetimeCount; |
| 684 | m_maxKeepaliveCount = res.revisedMaxKeepAliveCount; |
| 685 | m_interval = res.revisedPublishingInterval; |
| 686 | if (item == QOpcUaMonitoringParameters::Parameter::Priority) |
| 687 | m_priority = value.toUInt(); |
| 688 | if (item == QOpcUaMonitoringParameters::Parameter::MaxNotificationsPerPublish) |
| 689 | m_maxNotificationsPerPublish = value.toUInt(); |
| 690 | |
| 691 | QOpcUaMonitoringParameters p; |
| 692 | p.setStatusCode(QOpcUa::UaStatusCode::Good); |
| 693 | p.setPublishingInterval(m_interval); |
| 694 | p.setLifetimeCount(m_lifetimeCount); |
| 695 | p.setMaxKeepAliveCount(m_maxKeepaliveCount); |
| 696 | p.setPriority(m_priority); |
| 697 | p.setMaxNotificationsPerPublish(m_maxNotificationsPerPublish); |
| 698 | |
| 699 | for (auto it : std::as_const(t&: m_itemIdToItemMapping)) |
| 700 | emit m_backend->monitoringStatusChanged(handle: it->handle, attr: it->attr, items: changed, param: p); |
| 701 | } |
| 702 | return true; |
| 703 | } |
| 704 | return false; |
| 705 | } |
| 706 | |
| 707 | bool QOpen62541Subscription::modifyMonitoredItemParameters(quint64 nodeHandle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value) |
| 708 | { |
| 709 | MonitoredItem *monItem = getItemForAttribute(nodeHandle, attr); |
| 710 | QOpcUaMonitoringParameters p = monItem->parameters; |
| 711 | |
| 712 | UA_ModifyMonitoredItemsRequest req; |
| 713 | UA_ModifyMonitoredItemsRequest_init(p: &req); |
| 714 | UaDeleter<UA_ModifyMonitoredItemsRequest> requestDeleter(&req, UA_ModifyMonitoredItemsRequest_clear); |
| 715 | req.subscriptionId = m_subscriptionId; |
| 716 | req.itemsToModifySize = 1; |
| 717 | req.itemsToModify = UA_MonitoredItemModifyRequest_new(); |
| 718 | UA_MonitoredItemModifyRequest_init(p: req.itemsToModify); |
| 719 | req.itemsToModify->monitoredItemId = monItem->monitoredItemId; |
| 720 | req.itemsToModify->requestedParameters.discardOldest = monItem->parameters.discardOldest(); |
| 721 | req.itemsToModify->requestedParameters.queueSize = monItem->parameters.queueSize(); |
| 722 | req.itemsToModify->requestedParameters.samplingInterval = monItem->parameters.samplingInterval(); |
| 723 | req.itemsToModify->monitoredItemId = monItem->monitoredItemId; |
| 724 | req.itemsToModify->requestedParameters.clientHandle = monItem->clientHandle; |
| 725 | |
| 726 | bool match = true; |
| 727 | |
| 728 | switch (item) { |
| 729 | case QOpcUaMonitoringParameters::Parameter::DiscardOldest: { |
| 730 | if (value.metaType().id() != QMetaType::Bool) { |
| 731 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify DiscardOldest, value is not a bool" ; |
| 732 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 733 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 734 | return true; |
| 735 | } |
| 736 | req.itemsToModify->requestedParameters.discardOldest = value.toBool(); |
| 737 | break; |
| 738 | } |
| 739 | case QOpcUaMonitoringParameters::Parameter::QueueSize: { |
| 740 | if (value.metaType().id() != QMetaType::UInt) { |
| 741 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify QueueSize, value is not an integer" ; |
| 742 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 743 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 744 | return true; |
| 745 | } |
| 746 | req.itemsToModify->requestedParameters.queueSize = value.toUInt(); |
| 747 | break; |
| 748 | } |
| 749 | case QOpcUaMonitoringParameters::Parameter::SamplingInterval: { |
| 750 | if (value.metaType().id() != QMetaType::Double) { |
| 751 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify SamplingInterval, value is not a double" ; |
| 752 | p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch); |
| 753 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 754 | return true; |
| 755 | } |
| 756 | req.itemsToModify->requestedParameters.samplingInterval = value.toDouble(); |
| 757 | break; |
| 758 | } |
| 759 | case QOpcUaMonitoringParameters::Parameter::Filter: { |
| 760 | UA_ExtensionObject filter = createFilter(filterData: value); |
| 761 | if (filter.content.decoded.data) |
| 762 | req.itemsToModify->requestedParameters.filter = filter; |
| 763 | else { |
| 764 | qCDebug(QT_OPCUA_PLUGINS_OPEN62541) << "Unable to modify filter, filter creation failed" ; |
| 765 | p.setStatusCode(QOpcUa::UaStatusCode::BadInternalError); |
| 766 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 767 | return true; |
| 768 | } |
| 769 | break; |
| 770 | } |
| 771 | default: |
| 772 | match = false; |
| 773 | break; |
| 774 | } |
| 775 | |
| 776 | if (match) { |
| 777 | if (item != QOpcUaMonitoringParameters::Parameter::Filter && p.filter().isValid()) { |
| 778 | UA_ExtensionObject filter = createFilter(filterData: monItem->parameters.filter()); |
| 779 | if (filter.content.decoded.data) |
| 780 | req.itemsToModify->requestedParameters.filter = filter; |
| 781 | else { |
| 782 | qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify monitored item, filter creation failed" ; |
| 783 | p.setStatusCode(QOpcUa::UaStatusCode::BadInternalError); |
| 784 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 785 | return true; |
| 786 | } |
| 787 | } |
| 788 | |
| 789 | UA_ModifyMonitoredItemsResponse res = UA_Client_MonitoredItems_modify(client: m_backend->m_uaclient, request: req); |
| 790 | UaDeleter<UA_ModifyMonitoredItemsResponse> responseDeleter( |
| 791 | &res, UA_ModifyMonitoredItemsResponse_clear); |
| 792 | |
| 793 | if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD || res.results[0].statusCode != UA_STATUSCODE_GOOD) { |
| 794 | p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult == UA_STATUSCODE_GOOD ? res.results[0].statusCode : res.responseHeader.serviceResult)); |
| 795 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: item, param: p); |
| 796 | return true; |
| 797 | } else { |
| 798 | p.setStatusCode(QOpcUa::UaStatusCode::Good); |
| 799 | QOpcUaMonitoringParameters::Parameters changed = item; |
| 800 | if (!qFuzzyCompare(p1: p.samplingInterval(), p2: res.results[0].revisedSamplingInterval)) { |
| 801 | p.setSamplingInterval(res.results[0].revisedSamplingInterval); |
| 802 | changed |= QOpcUaMonitoringParameters::Parameter::SamplingInterval; |
| 803 | } |
| 804 | if (p.queueSize() != res.results[0].revisedQueueSize) { |
| 805 | p.setQueueSize(res.results[0].revisedQueueSize); |
| 806 | changed |= QOpcUaMonitoringParameters::Parameter::QueueSize; |
| 807 | } |
| 808 | |
| 809 | if (item == QOpcUaMonitoringParameters::Parameter::DiscardOldest) { |
| 810 | p.setDiscardOldest(value.toBool()); |
| 811 | changed |= QOpcUaMonitoringParameters::Parameter::DiscardOldest; |
| 812 | } |
| 813 | |
| 814 | if (item == QOpcUaMonitoringParameters::Parameter::Filter) { |
| 815 | changed |= QOpcUaMonitoringParameters::Parameter::Filter; |
| 816 | if (value.canConvert<QOpcUaMonitoringParameters::DataChangeFilter>()) |
| 817 | p.setFilter(value.value<QOpcUaMonitoringParameters::DataChangeFilter>()); |
| 818 | else if (value.canConvert<QOpcUaMonitoringParameters::EventFilter>()) |
| 819 | p.setFilter(value.value<QOpcUaMonitoringParameters::EventFilter>()); |
| 820 | if (res.results[0].filterResult.content.decoded.type == &UA_TYPES[UA_TYPES_EVENTFILTERRESULT]) |
| 821 | p.setFilterResult(convertEventFilterResult(obj: &res.results[0].filterResult)); |
| 822 | } |
| 823 | |
| 824 | emit m_backend->monitoringStatusChanged(handle: nodeHandle, attr, items: changed, param: p); |
| 825 | |
| 826 | monItem->parameters = p; |
| 827 | } |
| 828 | return true; |
| 829 | } |
| 830 | return false; |
| 831 | } |
| 832 | |
| 833 | QT_END_NAMESPACE |
| 834 | |