| 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 : 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 : 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 |  |