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