1 | // Copyright (C) 2019 The Qt Company Ltd. |
---|---|
2 | // Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
4 | |
5 | #include "qopcuaclient.h" |
6 | #include "qopcuaconnectionsettings.h" |
7 | #include "qopcuaexpandednodeid.h" |
8 | #include "qopcuaqualifiedname.h" |
9 | |
10 | #include <private/qopcuaclient_p.h> |
11 | |
12 | #include <QtCore/qloggingcategory.h> |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA) |
17 | |
18 | /*! |
19 | \class QOpcUaClient |
20 | \inmodule QtOpcUa |
21 | |
22 | \brief QOpcUaClient allows interaction with an OPC UA server. |
23 | |
24 | \section1 QOpcUaClient |
25 | |
26 | QOpcUaClient implements basic client capabilities to communicate with |
27 | OPC UA enabled devices and applications. This includes querying a discovery server |
28 | for known servers, requesting a list of endpoints from a server, connecting and |
29 | disconnecting. |
30 | |
31 | After successfully connecting to a server, QOpcUaClient allows getting \l QOpcUaNode |
32 | objects which enable further interaction with nodes on the OPC UA server. |
33 | For operations that concern multiple nodes, QOpcUaClient offers an API which supports |
34 | reading multiple attributes of multiple nodes in a single request to the server. |
35 | |
36 | QOpcUaClient also keeps a local copy of the server's namespace array which is created after |
37 | a successful connect. This information can be queried or updated while the connection lasts. |
38 | The copy of the namespace array is also used for the resolution of expanded node ids and the |
39 | creation of qualified names from a namespace URI. |
40 | |
41 | \section1 Addressing Nodes |
42 | |
43 | For an introduction to nodes and node ids, see \l QOpcUaNode. |
44 | |
45 | \section1 Usage |
46 | Create a \l QOpcUaClient using \l QOpcUaProvider, request a list of endpoints from the server |
47 | using \l requestEndpoints and call \l connectToEndpoint() to connect to one of the available endpoints. |
48 | After the connection is established, a \l QOpcUaNode object for the root node is requested. |
49 | \code |
50 | QOpcUaProvider provider; |
51 | if (provider.availableBackends().isEmpty()) |
52 | return; |
53 | QOpcUaClient *client = provider.createClient(provider.availableBackends()[0]); |
54 | if (!client) |
55 | return; |
56 | // Connect to the stateChanged signal. Compatible slots of QObjects can be used instead of a lambda. |
57 | QObject::connect(client, &QOpcUaClient::stateChanged, [client](QOpcUaClient::ClientState state) { |
58 | qDebug() << "Client state changed:" << state; |
59 | if (state == QOpcUaClient::ClientState::Connected) { |
60 | QOpcUaNode *node = client->node("ns=0;i=84"); |
61 | if (node) |
62 | qDebug() << "A node object has been created"; |
63 | } |
64 | }); |
65 | |
66 | QObject::connect(client, &QOpcUaClient::endpointsRequestFinished, |
67 | [client](QList<QOpcUaEndpointDescription> endpoints) { |
68 | qDebug() << "Endpoints returned:" << endpoints.count(); |
69 | if (endpoints.size()) |
70 | client->connectToEndpoint(endpoints.first()); // Connect to the first endpoint in the list |
71 | }); |
72 | |
73 | client->requestEndpoints(QUrl("opc.tcp://127.0.0.1:4840")); // Request a list of endpoints from the server |
74 | \endcode |
75 | */ |
76 | |
77 | /*! |
78 | \enum QOpcUaClient::ClientState |
79 | |
80 | This enum type specifies the connection state of the client. |
81 | |
82 | \value Disconnected |
83 | The client is not connected to a server. |
84 | \value Connecting |
85 | The client is currently connecting to a server. |
86 | \value Connected |
87 | The client is connected to a server. |
88 | \value Closing |
89 | The client has been connected and requests a disconnect from the server. |
90 | */ |
91 | |
92 | /*! |
93 | \enum QOpcUaClient::ClientError |
94 | |
95 | This enum type specifies the current error state of the client. |
96 | |
97 | \value NoError |
98 | No error occurred. |
99 | \value InvalidUrl |
100 | The url to connect to has been wrongly specified or a connection to this url failed. |
101 | \value AccessDenied |
102 | An attempt to connect to a server using username/password failed due to wrong credentials. |
103 | \value ConnectionError |
104 | An error occurred with the connection. |
105 | \value UnknownError |
106 | An unknown error occurred. |
107 | \value UnsupportedAuthenticationInformation |
108 | The given type or data of authentication information is not supported. |
109 | */ |
110 | |
111 | /*! |
112 | \property QOpcUaClient::error |
113 | \brief Specifies the current error state of the client. |
114 | */ |
115 | |
116 | /*! |
117 | \property QOpcUaClient::state |
118 | \brief Specifies the current connection state of the client. |
119 | */ |
120 | |
121 | /*! |
122 | \fn QOpcUaClient::connected() |
123 | |
124 | This signal is emitted when a connection has been established. |
125 | */ |
126 | |
127 | /*! |
128 | \fn QOpcUaClient::disconnected() |
129 | |
130 | This signal is emitted when a connection has been closed following to a close request. |
131 | */ |
132 | |
133 | /*! |
134 | \fn QOpcUaClient::connectError(QOpcUaErrorState *errorState) |
135 | \since QtOpcUa 5.13 |
136 | |
137 | This signal is emitted when an error happened during connection establishment. |
138 | The parameter \a errorState contains information about the error. |
139 | |
140 | In case of client side errors, these can be ignored by calling |
141 | \l QOpcUaErrorState::setIgnoreError on the object. |
142 | |
143 | During execution of a slot connected to this signal the backend is stopped and |
144 | waits for all slots to return. This allows to pop up a user dialog to ask the |
145 | enduser for example if to trust an unknown certificate before the backend continues. |
146 | */ |
147 | |
148 | /*! |
149 | \fn QOpcUaClient::passwordForPrivateKeyRequired(QString keyFilePath, QString *password, bool previousTryWasInvalid) |
150 | \since QtOpcUa 5.13 |
151 | |
152 | This signal is emitted when a password for an encrypted private key is required. |
153 | The parameter \a keyFilePath contains the file path to key which is used. |
154 | The parameter \a previousTryWasInvalid is true if a previous try to decrypt the key failed (aka invalid password). |
155 | The parameter \a password points to a QString that has to be filled with the actual password for the key. |
156 | In case the previous try failed it contains the previously used password. |
157 | |
158 | During execution of a slot connected to this signal the backend is stopped and |
159 | waits for all slots to return. This allows to pop up a user dialog to ask the |
160 | enduser for the password. |
161 | */ |
162 | |
163 | /*! |
164 | \fn void QOpcUaClient::namespaceArrayUpdated(QStringList namespaces) |
165 | |
166 | This signal is emitted after an updateNamespaceArray operation has finished. |
167 | \a namespaces contains the content of the server's namespace table. The index |
168 | of an entry in \a namespaces corresponds to the namespace index used in the node id. |
169 | |
170 | If the namespace array content stays the same after the update this signal is emitted nevertheless. |
171 | |
172 | \sa namespaceArrayChanged() updateNamespaceArray() |
173 | */ |
174 | |
175 | /*! |
176 | \fn void QOpcUaClient::namespaceArrayChanged(QStringList namespaces) |
177 | |
178 | This signal is emitted after the namespace array has changed. |
179 | \a namespaces contains the content of the server's namespace table. The index |
180 | of an entry in \a namespaces corresponds to the namespace index used in the node id. |
181 | |
182 | \sa namespaceArrayUpdated() updateNamespaceArray() |
183 | */ |
184 | |
185 | /*! |
186 | \fn void QOpcUaClient::endpointsRequestFinished(QList<QOpcUaEndpointDescription> endpoints, QOpcUa::UaStatusCode statusCode, QUrl requestUrl) |
187 | |
188 | This signal is emitted after a \l requestEndpoints() operation has finished. |
189 | \a statusCode contains the result of the operation. If the result is \l {QOpcUa::UaStatusCode} {Good}, |
190 | \a endpoints contains the descriptions of all endpoints that are available on the server. |
191 | \a requestUrl contains the URL that was used in the \l requestEndpoints() call. |
192 | */ |
193 | |
194 | /*! |
195 | \fn void QOpcUaClient::findServersFinished(QList<QOpcUaApplicationDescription> servers, QOpcUa::UaStatusCode statusCode, QUrl requestUrl); |
196 | |
197 | This signal is emitted after a \l findServers() operation has finished. |
198 | \a statusCode contains the result of the operation. If the result is \l {QOpcUa::UaStatusCode} {Good}, |
199 | \a servers contains the application descriptions of all servers known to the queried server that matched the filter criteria. |
200 | \a requestUrl contains the URL that was used in the \l findServers() call. |
201 | */ |
202 | |
203 | /*! |
204 | \fn void QOpcUaClient::readNodeAttributesFinished(QList<QOpcUaReadResult> results, QOpcUa::UaStatusCode serviceResult) |
205 | |
206 | This signal is emitted after a \l readNodeAttributes() operation has finished. |
207 | |
208 | The elements in \a results have the same order as the elements in the request. For each requested element, |
209 | there is a value together with timestamps and the status code in \a results. |
210 | \a serviceResult contains the status code from the OPC UA Read service. |
211 | |
212 | \sa readNodeAttributes() QOpcUaReadResult QOpcUaReadItem |
213 | */ |
214 | |
215 | /*! |
216 | \fn void QOpcUaClient::writeNodeAttributesFinished(QList<QOpcUaWriteResult> results, QOpcUa::UaStatusCode serviceResult) |
217 | |
218 | This signal is emitted after a \l writeNodeAttributes() operation has finished. |
219 | |
220 | The elements in \a results have the same order as the elements in the write request. |
221 | They contain the value, timestamps and status code received from the server as well as the node id, |
222 | attribute and index range from the write item. This facilitates matching the result with the request. |
223 | |
224 | \a serviceResult is the status code from the the OPC UA Write service. If \a serviceResult is not |
225 | \l {QOpcUa::UaStatusCode} {Good}, the entries in \a results also have an invalid status code and must |
226 | not be used. |
227 | |
228 | \sa writeNodeAttributes() QOpcUaWriteResult |
229 | */ |
230 | |
231 | /*! |
232 | \fn void QOpcUaClient::addNodeFinished(QOpcUaExpandedNodeId requestedNodeId, QString assignedNodeId, QOpcUa::UaStatusCode statusCode) |
233 | |
234 | This signal is emitted after an \l addNode() operation has finished. |
235 | \a requestedNodeId is the requested node id from the \l addNode() call, \a assignedNodeId is the node id the server has assigned to the new node. |
236 | \a statusCode contains the result of the operation. If the result is \l {QOpcUa::UaStatusCode} {Bad}, \a assignedNodeId is empty and no node |
237 | has been added to the server's address space. |
238 | */ |
239 | |
240 | /*! |
241 | \fn void QOpcUaClient::deleteNodeFinished(QString nodeId, QOpcUa::UaStatusCode statusCode) |
242 | |
243 | This signal is emitted after a \l deleteNode() operation has finished. |
244 | \a nodeId is the node id from the \l deleteNode() call. |
245 | \a statusCode contains the result of the operation. |
246 | */ |
247 | |
248 | /*! |
249 | \fn void QOpcUaClient::addReferenceFinished(QString sourceNodeId, QString referenceTypeId, QOpcUaExpandedNodeId targetNodeId, bool isForwardReference, QOpcUa::UaStatusCode statusCode) |
250 | |
251 | This signal is emitted after an \l addReference() operation has finished. |
252 | \a sourceNodeId, \a referenceTypeId, \a targetNodeId and \a isForwardReference are the values from the \l addReference() call. |
253 | \a statusCode contains the result of the operation. |
254 | */ |
255 | |
256 | /*! |
257 | \fn void QOpcUaClient::deleteReferenceFinished(QString sourceNodeId, QString referenceTypeId, QOpcUaExpandedNodeId targetNodeId, bool isForwardReference, QOpcUa::UaStatusCode statusCode) |
258 | |
259 | This signal is emitted after a \l deleteReference() operation has finished. |
260 | \a sourceNodeId, \a referenceTypeId, \a targetNodeId and \a isForwardReference are the values from the \l deleteReference() call. |
261 | \a statusCode contains the result of the operation. |
262 | */ |
263 | |
264 | /*! |
265 | \fn void QOpcUaClient::registerNodesFinished(const QStringList &nodesToRegister, const QStringList ®isteredNodeIds, QOpcUa::UaStatusCode statusCode) |
266 | \since 6.7 |
267 | |
268 | This signal is emitted after a \l registerNodes() operation has finished. |
269 | \a nodesToRegister contains the node ids from the request for correlation purposes. |
270 | The node ids returned by the server are in \a registeredNodeIds and have the same ordering as the ids in the request. |
271 | \a statusCode indicates if the operation was successful. |
272 | |
273 | \sa registerNodes() |
274 | */ |
275 | |
276 | /*! |
277 | \fn void QOpcUaClient::unregisterNodesFinished(const QStringList &nodesToUnregister, QOpcUa::UaStatusCode statusCode) |
278 | \since 6.7 |
279 | |
280 | This signal is emitted after a \l unregisterNodes() operation has finished. |
281 | \a nodesToUnregister contains the node ids from the request for correlation purposes. |
282 | \a statusCode indicates if the operation was successful. |
283 | |
284 | \sa unregisterNodes() |
285 | */ |
286 | |
287 | /*! |
288 | \internal QOpcUaClientImpl is an opaque type (as seen from the public API). |
289 | This prevents users of the public API to use this constructor (even though |
290 | it is public). |
291 | */ |
292 | QOpcUaClient::QOpcUaClient(QOpcUaClientImpl *impl, QObject *parent) |
293 | : QObject(*(new QOpcUaClientPrivate(impl)), parent) |
294 | { |
295 | impl->m_client = this; |
296 | |
297 | // callback from client implementation |
298 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::stateAndOrErrorChanged, context: this, |
299 | slot: [this](QOpcUaClient::ClientState state, QOpcUaClient::ClientError error) { |
300 | Q_D(QOpcUaClient); |
301 | d->setStateAndError(state, error); |
302 | if (state == QOpcUaClient::ClientState::Connected) { |
303 | d->updateNamespaceArray(); |
304 | d->setupNamespaceArrayMonitoring(); |
305 | } |
306 | }); |
307 | |
308 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::endpointsRequestFinished, |
309 | context: this, slot: &QOpcUaClient::endpointsRequestFinished); |
310 | |
311 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::findServersFinished, |
312 | context: this, slot: &QOpcUaClient::findServersFinished); |
313 | |
314 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::readNodeAttributesFinished, |
315 | context: this, slot: &QOpcUaClient::readNodeAttributesFinished); |
316 | |
317 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::writeNodeAttributesFinished, |
318 | context: this, slot: &QOpcUaClient::writeNodeAttributesFinished); |
319 | |
320 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::addNodeFinished, |
321 | context: this, slot: &QOpcUaClient::addNodeFinished); |
322 | |
323 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::deleteNodeFinished, |
324 | context: this, slot: &QOpcUaClient::deleteNodeFinished); |
325 | |
326 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::addReferenceFinished, |
327 | context: this, slot: &QOpcUaClient::addReferenceFinished); |
328 | |
329 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::deleteReferenceFinished, |
330 | context: this, slot: &QOpcUaClient::deleteReferenceFinished); |
331 | |
332 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::connectError, |
333 | context: this, slot: &QOpcUaClient::connectError); |
334 | |
335 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::passwordForPrivateKeyRequired, |
336 | context: this, slot: &QOpcUaClient::passwordForPrivateKeyRequired); |
337 | |
338 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::registerNodesFinished, |
339 | context: this, slot: &QOpcUaClient::registerNodesFinished); |
340 | |
341 | QObject::connect(sender: impl, signal: &QOpcUaClientImpl::unregisterNodesFinished, |
342 | context: this, slot: &QOpcUaClient::unregisterNodesFinished); |
343 | } |
344 | |
345 | /*! |
346 | Destroys the \l QOpcUaClient instance. |
347 | */ |
348 | QOpcUaClient::~QOpcUaClient() |
349 | { |
350 | } |
351 | |
352 | /*! |
353 | Sets the application identity for this \l QOpcUaClient instance to \a identity. |
354 | \since QtOpcUa 5.13 |
355 | */ |
356 | void QOpcUaClient::setApplicationIdentity(const QOpcUaApplicationIdentity &identity) |
357 | { |
358 | Q_D(QOpcUaClient); |
359 | d->setApplicationIdentity(identity); |
360 | } |
361 | |
362 | /*! |
363 | Returns the application identity of this \l QOpcUaClient instance. |
364 | \since QtOpcUa 5.13 |
365 | */ |
366 | QOpcUaApplicationIdentity QOpcUaClient::applicationIdentity() const |
367 | { |
368 | Q_D(const QOpcUaClient); |
369 | return d->applicationIdentity(); |
370 | } |
371 | |
372 | /*! |
373 | Sets the application PKI configuration for this \l QOpcUaClient instance to \a config. |
374 | \since QtOpcUa 5.13 |
375 | */ |
376 | void QOpcUaClient::setPkiConfiguration(const QOpcUaPkiConfiguration &config) |
377 | { |
378 | Q_D(QOpcUaClient); |
379 | d->setPkiConfiguration(config); |
380 | } |
381 | |
382 | /*! |
383 | Returns the application's PKI configuration of this \l QOpcUaClient instance. |
384 | \since QtOpcUa 5.13 |
385 | */ |
386 | QOpcUaPkiConfiguration QOpcUaClient::pkiConfiguration() const |
387 | { |
388 | Q_D(const QOpcUaClient); |
389 | return d->pkiConfiguration(); |
390 | } |
391 | |
392 | /*! |
393 | Connects to the OPC UA endpoint given in \a endpoint. |
394 | \since QtOpcUa 5.13 |
395 | |
396 | \code |
397 | QEndpointDescription endpointDescription; |
398 | ... |
399 | client->connectToEndpoint(endpointDescription); |
400 | \endcode |
401 | |
402 | A list of available endpoints is usually obtained by calling \l QOpcUaClient::requestEndpoints(). |
403 | |
404 | If the endpoint requires username authentication, at least a user name must be set in \l QOpcUaAuthenticationInformation. |
405 | Calling this function before setting an authentication information will use the anonymous authentication. |
406 | |
407 | \code |
408 | QOpcUaAuthenticationInformation authInfo; |
409 | authInfo.setUsernameAuthentication("user", "password"); |
410 | |
411 | client->setAuthenticationInformation(authInfo); |
412 | \endcode |
413 | |
414 | \sa connected(), stateChanged(), setAuthenticationInformation(), QOpcUaEndpointDescription |
415 | */ |
416 | void QOpcUaClient::connectToEndpoint(const QOpcUaEndpointDescription &endpoint) |
417 | { |
418 | Q_D(QOpcUaClient); |
419 | d->connectToEndpoint(endpoint); |
420 | } |
421 | |
422 | /*! |
423 | Disconnects from the server. |
424 | \sa disconnected(), connectToEndpoint() |
425 | */ |
426 | void QOpcUaClient::disconnectFromEndpoint() |
427 | { |
428 | Q_D(QOpcUaClient); |
429 | d->disconnectFromEndpoint(); |
430 | } |
431 | |
432 | /*! |
433 | Returns the description of the endpoint the client is currently connected to |
434 | or was last connected to. |
435 | */ |
436 | QOpcUaEndpointDescription QOpcUaClient::endpoint() const |
437 | { |
438 | Q_D(const QOpcUaClient); |
439 | return d->m_endpoint; |
440 | } |
441 | |
442 | QOpcUaClient::ClientState QOpcUaClient::state() const |
443 | { |
444 | Q_D(const QOpcUaClient); |
445 | return d->m_state; |
446 | } |
447 | |
448 | /*! |
449 | Returns the current error state of the client. |
450 | */ |
451 | QOpcUaClient::ClientError QOpcUaClient::error() const |
452 | { |
453 | Q_D(const QOpcUaClient); |
454 | return d->m_error; |
455 | } |
456 | |
457 | /*! |
458 | Returns a \l QOpcUaNode object associated with the OPC UA node identified |
459 | by \a nodeId. The caller becomes owner of the node object. |
460 | |
461 | If the client is not connected, \c nullptr is returned. The backends may also |
462 | return \c nullptr for other error cases (for example for a malformed node id). |
463 | */ |
464 | QOpcUaNode *QOpcUaClient::node(const QString &nodeId) |
465 | { |
466 | if (state() != QOpcUaClient::Connected) |
467 | return nullptr; |
468 | |
469 | Q_D(QOpcUaClient); |
470 | return d->m_impl->node(nodeId); |
471 | } |
472 | |
473 | /*! |
474 | Returns a \l QOpcUaNode object associated with the OPC UA node identified |
475 | by \a expandedNodeId. The caller becomes owner of the node object. |
476 | |
477 | If the node is not on the currently connected server, the namespace can't be resolved, |
478 | the node id is malformed or the client is not connected, \c nullptr is returned. |
479 | |
480 | \sa updateNamespaceArray() |
481 | */ |
482 | QOpcUaNode *QOpcUaClient::node(const QOpcUaExpandedNodeId &expandedNodeId) |
483 | { |
484 | if (expandedNodeId.serverIndex()) { |
485 | qCWarning(QT_OPCUA) << "Can't create a QOpcuaNode for a node on a different server."; |
486 | return nullptr; |
487 | } |
488 | |
489 | const QString nodeId = resolveExpandedNodeId(expandedNodeId); |
490 | |
491 | if (!nodeId.isEmpty()) |
492 | return node(nodeId); |
493 | else |
494 | return nullptr; |
495 | } |
496 | |
497 | /*! |
498 | Requests an update of the namespace array from the server. |
499 | Returns \c true if the operation has been successfully dispatched. |
500 | |
501 | The \l namespaceArrayUpdated() signal is emitted after the operation is finished. |
502 | |
503 | \sa namespaceArray() namespaceArrayUpdated() |
504 | */ |
505 | bool QOpcUaClient::updateNamespaceArray() |
506 | { |
507 | if (state() != QOpcUaClient::Connected) |
508 | return false; |
509 | |
510 | Q_D(QOpcUaClient); |
511 | return d->updateNamespaceArray(); |
512 | } |
513 | |
514 | /*! |
515 | Returns the cached value of the namespace array. |
516 | |
517 | The value is only valid after the \l namespaceArrayUpdated() signal has been emitted. |
518 | |
519 | \sa updateNamespaceArray() namespaceArrayUpdated() |
520 | */ |
521 | QStringList QOpcUaClient::namespaceArray() const |
522 | { |
523 | Q_D(const QOpcUaClient); |
524 | return d->namespaceArray(); |
525 | } |
526 | |
527 | /*! |
528 | Attempts to resolve \a expandedNodeId to a node id string with numeric namespace index. |
529 | Returns the node id string if the conversion was successful. |
530 | |
531 | An empty string is returned if the namespace index can't be resolved or if the identifier part |
532 | of the expanded node id is malformed. \a ok will be set to \c true if the conversion has been successful. |
533 | If the expanded node id could not be resolved, \a ok will be set to \c false. |
534 | */ |
535 | QString QOpcUaClient::resolveExpandedNodeId(const QOpcUaExpandedNodeId &expandedNodeId, bool *ok) const |
536 | { |
537 | if (expandedNodeId.serverIndex() && !expandedNodeId.namespaceUri().isEmpty()) { |
538 | qCWarning(QT_OPCUA) << "Can't resolve a namespace index on a different server."; |
539 | if (ok) |
540 | *ok = false; |
541 | return QString(); |
542 | } |
543 | |
544 | if (expandedNodeId.namespaceUri().isEmpty()) { |
545 | if (ok) |
546 | *ok = true; |
547 | return expandedNodeId.nodeId(); |
548 | } else { |
549 | if (!namespaceArray().size()) { |
550 | qCWarning(QT_OPCUA) << "Namespaces table missing, unable to resolve namespace URI."; |
551 | if (ok) |
552 | *ok = false; |
553 | return QString(); |
554 | } |
555 | |
556 | int index = namespaceArray().indexOf(str: expandedNodeId.namespaceUri()); |
557 | |
558 | if (index < 0) { |
559 | qCWarning(QT_OPCUA) << "Failed to resolve namespace"<< expandedNodeId.namespaceUri(); |
560 | if (ok) |
561 | *ok = false; |
562 | return QString(); |
563 | } |
564 | |
565 | QStringList splitId = expandedNodeId.nodeId().split(sep: QLatin1String(";")); |
566 | if (splitId.size() != 2) { |
567 | qCWarning(QT_OPCUA) << "Failed to split node id"<< expandedNodeId.nodeId(); |
568 | if (ok) |
569 | *ok = false; |
570 | return QString(); |
571 | } |
572 | |
573 | if (ok) |
574 | *ok = true; |
575 | return QStringLiteral("ns=%1;").arg(a: index).append(s: splitId.at(i: 1)); |
576 | } |
577 | } |
578 | |
579 | /*! |
580 | Attempts to create a qualified name from \a namespaceUri and the name string \a name. |
581 | Returns the resulting qualified name. An empty qualified name is returned if |
582 | \a namespaceUri can't be resolved. |
583 | |
584 | \a ok will be set to \c true if the namespace URI resolution has been successful. |
585 | If the namespace URI could not be resolved, \a ok will be set to \c false. |
586 | */ |
587 | QOpcUaQualifiedName QOpcUaClient::qualifiedNameFromNamespaceUri(const QString &namespaceUri, const QString &name, bool *ok) const |
588 | { |
589 | if (namespaceArray().isEmpty()) { |
590 | qCWarning(QT_OPCUA) << "Namespaces table missing, unable to resolve namespace URI."; |
591 | if (ok) |
592 | *ok = false; |
593 | return QOpcUaQualifiedName(); |
594 | } |
595 | |
596 | int index = namespaceArray().indexOf(str: namespaceUri); |
597 | |
598 | if (index < 0) { |
599 | qCWarning(QT_OPCUA) << "Failed to resolve namespace"<< namespaceUri; |
600 | if (ok) |
601 | *ok = false; |
602 | return QOpcUaQualifiedName(); |
603 | } |
604 | |
605 | if (ok) |
606 | *ok = true; |
607 | |
608 | return QOpcUaQualifiedName(index, name); |
609 | }; |
610 | |
611 | /*! |
612 | Adds the node described by \a nodeToAdd on the server. |
613 | |
614 | Returns \c true if the asynchronous call has been successfully dispatched. |
615 | |
616 | The success of the operation is returned in the \l addNodeFinished() signal. |
617 | |
618 | The following example code adds new a Variable node on the server: |
619 | |
620 | \code |
621 | QOpcUaNodeCreationAttributes attributes; |
622 | attributes.setDisplayName(QOpcUaLocalizedText("en", "My new Variable node")); |
623 | attributes.setDescription(QOpcUaLocalizedText("en", "A node which has been added at runtime")); |
624 | attributes.setValue(23.0, QOpcUa::Types::Double); |
625 | attributes.setDataTypeId(QOpcUa::ns0ID(QOpcUa::NodeIds::Namespace0::Double)); |
626 | attributes.setValueRank(-2); // Scalar or array |
627 | attributes.setAccessLevel(QOpcUa::AccessLevelBit::CurrentRead); |
628 | attributes.setUserAccessLevel(QOpcUa::AccessLevelBit::CurrentRead); |
629 | |
630 | QOpcUaAddNodeItem item; |
631 | item.setParentNodeId(QOpcUaExpandedNodeId("ns=3;s=TestFolder")); |
632 | item.setReferenceTypeId(QOpcUa::nodeIdFromReferenceType(QOpcUa::ReferenceTypeId::Organizes)); |
633 | item.setRequestedNewNodeId(QOpcUaExpandedNodeId("ns=3;s=MyNewVariableNode")); |
634 | item.setBrowseName(QOpcUaQualifiedName(3, "MyNewVariableNode")); |
635 | item.setNodeClass(QOpcUa::NodeClass::Variable); |
636 | item.setNodeAttributes(attributes); |
637 | |
638 | m_client->addNode(item); |
639 | \endcode |
640 | |
641 | \sa deleteNode() addNodeFinished() QOpcUaAddNodeItem |
642 | */ |
643 | bool QOpcUaClient::addNode(const QOpcUaAddNodeItem &nodeToAdd) |
644 | { |
645 | if (state() != QOpcUaClient::Connected) |
646 | return false; |
647 | |
648 | Q_D(QOpcUaClient); |
649 | return d->m_impl->addNode(nodeToAdd); |
650 | } |
651 | |
652 | /*! |
653 | Deletes the node with node id \a nodeId from the server. |
654 | If \a deleteTargetReferences is \c false, only the references with source node \a nodeId are deleted. |
655 | If \a deleteTargetReferences is \c true, references with \a nodeId as target are deleted too. |
656 | |
657 | Returns \c true if the asynchronous call has been successfully dispatched. |
658 | |
659 | The success of the operation is returned in the \l deleteNodeFinished() signal. |
660 | |
661 | The following example code deletes a node and all references to it from the server: |
662 | |
663 | \code |
664 | m_client->deleteNode(QOpcUaExpandedNodeId("ns=3;s=MyNewVariableNode"), true); |
665 | \endcode |
666 | |
667 | \sa addNode() deleteNodeFinished() |
668 | */ |
669 | bool QOpcUaClient::deleteNode(const QString &nodeId, bool deleteTargetReferences) |
670 | { |
671 | if (state() != QOpcUaClient::Connected) |
672 | return false; |
673 | |
674 | Q_D(QOpcUaClient); |
675 | return d->m_impl->deleteNode(nodeId, deleteTargetReferences); |
676 | } |
677 | |
678 | /*! |
679 | Adds the reference described by \a referenceToAdd to the server. |
680 | |
681 | Returns \c true if the asynchronous call has been successfully dispatched. |
682 | |
683 | The success of the operation is returned in the \l addReferenceFinished() signal. |
684 | |
685 | The following example code adds a reference to a node to the "Objects" folder: |
686 | |
687 | \code |
688 | QOpcUaAddReferenceItem item; |
689 | item.setSourceNodeId(QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ObjectsFolder)); |
690 | item.setReferenceTypeId(QOpcUa::nodeIdFromInteger(0, static_cast<quint32>(QOpcUa::ReferenceTypeId::Organizes))); |
691 | item.setIsForwardReference(true); |
692 | item.setTargetNodeId(QOpcUaExpandedNodeId("ns=3;s=MyNewVariableNode")); |
693 | item.setTargetNodeClass(QOpcUa::NodeClass::Variable); |
694 | |
695 | m_client->addReference(item); |
696 | \endcode |
697 | |
698 | \sa deleteReference() addReferenceFinished() QOpcUaAddReferenceItem |
699 | */ |
700 | bool QOpcUaClient::addReference(const QOpcUaAddReferenceItem &referenceToAdd) |
701 | { |
702 | if (state() != QOpcUaClient::Connected) |
703 | return false; |
704 | |
705 | Q_D(QOpcUaClient); |
706 | return d->m_impl->addReference(referenceToAdd); |
707 | } |
708 | |
709 | /*! |
710 | Deletes the reference described by \a referenceToDelete from the server. |
711 | |
712 | Returns \c true if the asynchronous call has been successfully dispatched. |
713 | |
714 | The success of the operation is returned in the \l deleteReferenceFinished() signal. |
715 | |
716 | The following example code deletes a reference to a node from the "Objects" folder: |
717 | |
718 | \code |
719 | QOpcUaDeleteReferenceItem item; |
720 | item.setSourceNodeId(QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ObjectsFolder)); |
721 | item.setReferenceTypeId(QOpcUa::nodeIdFromInteger(0, static_cast<quint32>(QOpcUa::ReferenceTypeId::Organizes))); |
722 | item.setIsForwardReference(true); |
723 | item.setTargetNodeId(QOpcUaExpandedNodeId("ns=3;s=MyNewVariableNode")); |
724 | item.setDeleteBidirectional(true); |
725 | |
726 | m_client->deleteReference(item); |
727 | \endcode |
728 | |
729 | \sa addReference() deleteReferenceFinished() QOpcUaDeleteReferenceItem |
730 | */ |
731 | bool QOpcUaClient::deleteReference(const QOpcUaDeleteReferenceItem &referenceToDelete) |
732 | { |
733 | if (state() != QOpcUaClient::Connected) |
734 | return false; |
735 | |
736 | Q_D(QOpcUaClient); |
737 | return d->m_impl->deleteReference(referenceToDelete); |
738 | } |
739 | |
740 | /*! |
741 | Starts an asynchronous \c GetEndpoints request to read a list of available endpoints |
742 | from the server at \a url. |
743 | Returns \c true if the asynchronous call has been successfully dispatched. |
744 | |
745 | The endpoint information is returned in the \l endpointsRequestFinished() signal. |
746 | */ |
747 | bool QOpcUaClient::requestEndpoints(const QUrl &url) |
748 | { |
749 | Q_D(QOpcUaClient); |
750 | return d->m_impl->requestEndpoints(url); |
751 | } |
752 | |
753 | /*! |
754 | Starts an asynchronous FindServers request to read a list of known servers from a server or |
755 | discovery server at \a url. |
756 | Returns \c true if the asynchronous call has been successfully dispatched. |
757 | |
758 | \a localeIds can be used to select the language of the application names returned by the request. |
759 | The format is specified in OPC UA 1.05 part 3, 8.4, for example "en" for English, or "de-DE" for |
760 | German (Germany). If more than one locale ID is specified, the server uses the first match. If there |
761 | is no match or \a localeIds is empty, a default locale is chosen by the server. |
762 | |
763 | \a serverUris may be used to restrict the results to servers with a matching applicationUri in their |
764 | application description. For example, finding the current URL of the server with the applicationUri |
765 | "MyPLC", the following call can be used: |
766 | |
767 | \code |
768 | client->findServers(discoveryServerUrl, QStringList(), QStringList({"MyPLC"})); |
769 | \endcode |
770 | |
771 | The results are returned in the \l findServersFinished() signal. |
772 | */ |
773 | bool QOpcUaClient::findServers(const QUrl &url, const QStringList &localeIds, const QStringList &serverUris) |
774 | { |
775 | Q_D(QOpcUaClient); |
776 | return d->m_impl->findServers(url, localeIds, serverUris); |
777 | } |
778 | |
779 | /*! |
780 | Starts a read of multiple attributes on different nodes. |
781 | The node id, the attribute and an index range can be specified for every entry in \a nodesToRead. |
782 | |
783 | Returns true if the asynchronous request has been successfully dispatched. |
784 | The results are returned in the \l readNodeAttributesFinished() signal. |
785 | |
786 | This read function offers an alternative way to read attributes of nodes which can be used |
787 | for scenarios where the values of a large number of node attributes on different nodes must be read |
788 | without requiring the other features of the \l QOpcUaNode based API like monitoring for value changes. |
789 | All read items in the request are sent to the server in a single request and are answered in a single |
790 | response which generates a single \l readNodeAttributesFinished() signal. This reduces the network overhead and |
791 | the number of signal slot connections if many different nodes are involved. |
792 | |
793 | In the following example, the display name attribute and the two index ranges "0:2" and "5:7" of the value |
794 | attribute of the same node and the entire value attribute of a second node are read using a single service call: |
795 | \code |
796 | QList<QOpcUaReadItem> request; |
797 | request.push_back(QOpcUaReadItem("ns=1;s=MyArrayNode", |
798 | QOpcUa::NodeAttribute::DisplayName)); |
799 | request.push_back(QOpcUaReadItem("ns=1;s=MyArrayNode", |
800 | QOpcUa::NodeAttribute::Value, "0:2")); |
801 | request.push_back(QOpcUaReadItem("ns=1;s=MyArrayNode", |
802 | QOpcUa::NodeAttribute::Value, "5:7")); |
803 | request.push_back(QOpcUaReadItem("ns=1;s=MyScalarNode)); |
804 | m_client->readNodeAttributes(request); |
805 | \endcode |
806 | |
807 | \sa QOpcUaReadItem readNodeAttributesFinished() |
808 | */ |
809 | bool QOpcUaClient::readNodeAttributes(const QList<QOpcUaReadItem> &nodesToRead) |
810 | { |
811 | if (state() != QOpcUaClient::Connected) |
812 | return false; |
813 | |
814 | Q_D(QOpcUaClient); |
815 | return d->m_impl->readNodeAttributes(nodesToRead); |
816 | } |
817 | |
818 | /*! |
819 | Starts a write for multiple attributes on different nodes. |
820 | The node id, the attribute, the value, the value type and an index range can be specified |
821 | for every entry in \a nodesToWrite. |
822 | |
823 | Returns \c true if the asynchronous request has been successfully dispatched. |
824 | The results are returned in the \l writeNodeAttributesFinished() signal. |
825 | |
826 | This write function offers an alternative way to write attributes of nodes which can be used |
827 | for scenarios where the values of a large number of node attributes on different nodes must be written |
828 | without requiring the other features of the \l QOpcUaNode based API like monitoring for value changes. |
829 | All write items in the request are sent to the server in a single request and are answered in a single |
830 | response which generates a single \l writeNodeAttributesFinished() signal. This reduces the network overhead and |
831 | the number of signal slot connections if many different nodes are involved. |
832 | |
833 | In the following example, the Values attributes of two different nodes are written in one call. |
834 | The second node has an array value of which only the first two elements are overwritten: |
835 | |
836 | \code |
837 | QList<QOpcUaWriteItem> request; |
838 | |
839 | request.append(QOpcUaWriteItem("ns=2;s=Demo.Static.Scalar.Double", QOpcUa::NodeAttribute::Value, |
840 | 23.0, QOpcUa::Types::Double)); |
841 | request.append(QOpcUaWriteItem("ns=2;s=Demo.Static.Arrays.UInt32", QOpcUa::NodeAttribute::Value, |
842 | QVariantList({0, 1, 2}), QOpcUa::Types::UInt32, "0:2")); |
843 | |
844 | m_client->writeNodeAttributes(request); |
845 | \endcode |
846 | |
847 | \sa QOpcUaWriteItem writeNodeAttributesFinished() |
848 | */ |
849 | bool QOpcUaClient::writeNodeAttributes(const QList<QOpcUaWriteItem> &nodesToWrite) |
850 | { |
851 | if (state() != QOpcUaClient::Connected) |
852 | return false; |
853 | |
854 | Q_D(QOpcUaClient); |
855 | return d->m_impl->writeNodeAttributes(nodesToWrite); |
856 | } |
857 | |
858 | /*! |
859 | Returns the name of the backend used by this instance of QOpcUaClient, |
860 | e.g. "open62541". |
861 | */ |
862 | QString QOpcUaClient::backend() const |
863 | { |
864 | Q_D(const QOpcUaClient); |
865 | return d->m_impl->backend(); |
866 | } |
867 | |
868 | /*! |
869 | Enables automatic update of the namespace table. |
870 | |
871 | Enabling this will keep the local copy of the namespace table updated automatically. |
872 | \l namespaceArrayUpdated will be emitted when the array changed. |
873 | \a isEnabled determines if autoupdate is being enabled or disabled. |
874 | |
875 | A subscription will be made on the node on the server to keep track of changes. |
876 | In case a server does not support subscriptions this will not work and |
877 | \l isNamespaceAutoupdateEnabled returns \c false. |
878 | |
879 | \sa namespaceArray() namespaceArrayUpdated() |
880 | */ |
881 | void QOpcUaClient::setNamespaceAutoupdate(bool isEnabled) |
882 | { |
883 | Q_D(QOpcUaClient); |
884 | d->m_enableNamespaceArrayAutoupdate = isEnabled; |
885 | d->setupNamespaceArrayMonitoring(); |
886 | } |
887 | |
888 | /*! |
889 | Returns whether autoupdate of the namespace array is enabled. |
890 | */ |
891 | bool QOpcUaClient::isNamespaceAutoupdateEnabled() const |
892 | { |
893 | Q_D(const QOpcUaClient); |
894 | return d->m_enableNamespaceArrayAutoupdate; |
895 | } |
896 | |
897 | /*! |
898 | Sets the interval for the namespace table subscription. |
899 | |
900 | The subscription may be revised by the server. |
901 | |
902 | \a interval determines the interval to check for changes in milliseconds. The default is once per second. |
903 | |
904 | \sa QOpcUaClient::setNamespaceAutoupdate(bool isEnabled) |
905 | */ |
906 | void QOpcUaClient::setNamespaceAutoupdateInterval(int interval) |
907 | { |
908 | Q_D(QOpcUaClient); |
909 | d->m_namespaceArrayUpdateInterval = interval; |
910 | d->setupNamespaceArrayMonitoring(); |
911 | } |
912 | |
913 | /*! |
914 | Returns the current revised update interval of the namespace array. |
915 | |
916 | \sa setNamespaceAutoupdateInterval(int interval) |
917 | */ |
918 | int QOpcUaClient::namespaceAutoupdateInterval() const |
919 | { |
920 | Q_D(const QOpcUaClient); |
921 | return d->m_namespaceArrayUpdateInterval; |
922 | } |
923 | |
924 | /*! |
925 | Sets the authentication information of this client to \a authenticationInformation. |
926 | |
927 | \sa connectToEndpoint() |
928 | */ |
929 | void QOpcUaClient::setAuthenticationInformation(const QOpcUaAuthenticationInformation &authenticationInformation) |
930 | { |
931 | Q_D(QOpcUaClient); |
932 | d->m_authenticationInformation = authenticationInformation; |
933 | } |
934 | |
935 | /*! |
936 | Returns the current authentication information. |
937 | */ |
938 | const QOpcUaAuthenticationInformation &QOpcUaClient::authenticationInformation() const |
939 | { |
940 | Q_D(const QOpcUaClient); |
941 | return d->m_authenticationInformation; |
942 | } |
943 | |
944 | /*! |
945 | \since 6.6 |
946 | |
947 | Sets the connection settings for this client to \a connectionSettings. |
948 | |
949 | Example: |
950 | \code |
951 | QOpcUaConnectionSettings settings; |
952 | // Ask the server to give localized texts in german with french as fallback |
953 | settings.setSessionLocaleIds({ "de", "fr" }); |
954 | // We need to call some long running methods, increase the request timeout |
955 | settings.setRequestTimeout(std::chrono::minutes(2)); |
956 | opcuaClient->setConnectionSettings(settings); |
957 | \endcode |
958 | |
959 | The values from \a connectionSettings are applied to any new connections after this point. |
960 | |
961 | \sa connectionSettings() |
962 | */ |
963 | void QOpcUaClient::setConnectionSettings(const QOpcUaConnectionSettings &connectionSettings) |
964 | { |
965 | Q_D(QOpcUaClient); |
966 | d->m_connectionSettings = connectionSettings; |
967 | } |
968 | |
969 | /*! |
970 | \since 6.6 |
971 | |
972 | Returns the connection settings for this client. |
973 | |
974 | \sa setConnectionSettings() |
975 | */ |
976 | QOpcUaConnectionSettings QOpcUaClient::connectionSettings() const |
977 | { |
978 | Q_D(const QOpcUaClient); |
979 | return d->m_connectionSettings; |
980 | } |
981 | |
982 | /*! |
983 | \since QtOpcUa 5.14 |
984 | |
985 | Returns the security policies supported by the used backend. |
986 | |
987 | This function is currently available as a Technology Preview, and therefore the API |
988 | and functionality provided by the function may be subject to change at any time without |
989 | prior notice. |
990 | */ |
991 | QStringList QOpcUaClient::supportedSecurityPolicies() const |
992 | { |
993 | Q_D(const QOpcUaClient); |
994 | return d->m_impl->supportedSecurityPolicies(); |
995 | } |
996 | |
997 | /*! |
998 | \since QtOpcUa 5.14 |
999 | |
1000 | Returns the user token types supported by the used backend. |
1001 | |
1002 | This function is currently available as a Technology Preview, and therefore the API |
1003 | and functionality provided by the function may be subject to change at any time without |
1004 | prior notice. |
1005 | |
1006 | \sa QOpcUaUserTokenPolicy::TokenType |
1007 | */ |
1008 | QList<QOpcUaUserTokenPolicy::TokenType> QOpcUaClient::supportedUserTokenTypes() const |
1009 | { |
1010 | Q_D(const QOpcUaClient); |
1011 | return d->m_impl->supportedUserTokenTypes(); |
1012 | } |
1013 | |
1014 | /*! |
1015 | \since 6.3 |
1016 | |
1017 | Starts a read raw history \a request for one or multiple nodes. This is the Qt OPC UA representation for the OPC UA |
1018 | ReadHistory service for reading raw historical data defined in |
1019 | \l {https://reference.opcfoundation.org/v105/Core/docs/Part4/5.10.3/} {OPC UA 1.05 part 4, 5.10.3}. |
1020 | |
1021 | The start timestamp, end timestamp, number of values per node, returnBounds and nodes to read |
1022 | can be specified in a \l QOpcUaHistoryReadRawRequest. |
1023 | |
1024 | Returns a \l QOpcUaHistoryReadResponse which contains the state of the request if the asynchronous |
1025 | request has been successfully dispatched. The results are returned in the |
1026 | \l QOpcUaHistoryReadResponse::readHistoryDataFinished(const QList<QOpcUaHistoryData> &results, QOpcUa::UaStatusCode serviceResult) |
1027 | signal. |
1028 | |
1029 | In the following example, the historic data from the last two days of two nodes are requested and printed. |
1030 | The result is limited to ten values per node. |
1031 | |
1032 | \code |
1033 | QOpcUaHistoryReadRawRequest request( |
1034 | { QOpcUaReadItem("ns=1;s=myValue1"), QOpcUaReadItem("ns=1;s=myValue2") }, |
1035 | QDateTime::currentDateTime(), |
1036 | QDateTime::currentDateTime().addDays(-2), |
1037 | 10, |
1038 | true); |
1039 | |
1040 | QOpcUaHistoryReadResponse *response = m_client->readHistoryData(request); |
1041 | if (response) { |
1042 | QObject::connect(response, &QOpcUaHistoryReadResponse::readHistoryDataFinished, |
1043 | [] (QList<QOpcUaHistoryData> results, QOpcUa::UaStatusCode serviceResult) { |
1044 | if (serviceResult != QOpcUa::UaStatusCode::Good) { |
1045 | qWarning() << "Fetching historical data failed with:" << serviceResult; |
1046 | } else { |
1047 | for (const auto& result : results) { |
1048 | qInfo() << "NodeId:" << result.nodeId(); |
1049 | for (const auto &dataValue : result.result()) |
1050 | qInfo() << "Value:" << dataValue.value(); |
1051 | } |
1052 | } |
1053 | }); |
1054 | } |
1055 | \endcode |
1056 | */ |
1057 | QOpcUaHistoryReadResponse *QOpcUaClient::readHistoryData(const QOpcUaHistoryReadRawRequest &request) |
1058 | { |
1059 | Q_D(const QOpcUaClient); |
1060 | return d->m_impl->readHistoryData(request); |
1061 | } |
1062 | |
1063 | /*! |
1064 | \since 6.7 |
1065 | |
1066 | Registers the node ids in \a nodesToRegister on the server and returns \c true if the request |
1067 | has been successfully dispatched. |
1068 | The results are returned in the \l registerNodesFinished() signal. |
1069 | |
1070 | The node registration service is used to let the server know that a node will be accessed frequently |
1071 | so it may perform operations like keeping the connection to an external resource open. |
1072 | The server may also return an alias node id which is recommended to be numeric. This might come in |
1073 | handy if a node with a long string identifier node id is used in many requests. |
1074 | The real performance gain (if any) depends on the server's implementation. |
1075 | |
1076 | The registered node ids are only guaranteed to be valid for the current session. |
1077 | Any registrations that are no longer needed should be unregistered as soon as possible so the |
1078 | server may free the associated resources. |
1079 | |
1080 | \sa unregisterNodes() |
1081 | */ |
1082 | bool QOpcUaClient::registerNodes(const QStringList &nodesToRegister) |
1083 | { |
1084 | Q_D(const QOpcUaClient); |
1085 | return d->m_impl->registerNodes(nodesToRegister); |
1086 | } |
1087 | |
1088 | /*! |
1089 | \since 6.7 |
1090 | |
1091 | Unregisters the node ids in \a nodesToUnregister on the server and returns \c true if the request |
1092 | has been successfully dispatched. |
1093 | The results are returned in the \l unregisterNodesFinished() signal. |
1094 | |
1095 | The node ids to pass in \a nodesToUnregister must have been obtained via \l registerNodes(). |
1096 | |
1097 | \sa registerNodes() |
1098 | */ |
1099 | bool QOpcUaClient::unregisterNodes(const QStringList &nodesToUnregister) |
1100 | { |
1101 | Q_D(const QOpcUaClient); |
1102 | return d->m_impl->unregisterNodes(nodesToUnregister); |
1103 | } |
1104 | |
1105 | /*! |
1106 | \since 6.7 |
1107 | |
1108 | Starts a read event history request for one or multiple node ids with the parameters in \a request. |
1109 | |
1110 | Returns a \l QOpcUaHistoryReadResponse which contains the state of the request if the asynchronous |
1111 | request has been successfully dispatched. The results are returned in the |
1112 | \l QOpcUaHistoryReadResponse::readHistoryEventsFinished(const QList<QOpcUaHistoryEvent> &results, QOpcUa::UaStatusCode serviceResult) |
1113 | signal. |
1114 | |
1115 | The following example retrieves historic events for the last two days for two nodes. Up to 10 events per node are returned at a time. |
1116 | While there are more events matching the filter and the provided time range, \c hasMoreData() will be true and more events can be |
1117 | fetched via \b readMoreData(). |
1118 | |
1119 | \code |
1120 | QOpcUaMonitoringParameters::EventFilter filter; |
1121 | filter << QOpcUaSimpleAttributeOperand("Message"); |
1122 | filter << QOpcUaSimpleAttributeOperand("Time"); |
1123 | |
1124 | const QOpcUaHistoryReadEventRequest request({ QOpcUaReadItem("ns=2;s=EventHistorian"), QOpcUaReadItem("ns=2;s=EventHistorian2") }, |
1125 | QDateTime::currentDateTime().addDays(-2), QDateTime::currentDateTime(), |
1126 | filter, 10); |
1127 | |
1128 | // The response object must be freed by the user after all wanted data has been retrieved |
1129 | const auto response = opcuaClient->readHistoryEvents(request); |
1130 | |
1131 | QObject::connect(response, &QOpcUaHistoryReadResponse::readHistoryEventsFinished, this, |
1132 | [response](const QList<QOpcUaHistoryEvent> &results, QOpcUa::UaStatusCode serviceResult) { |
1133 | if (serviceResult != QOpcUa::UaStatusCode::Good) { |
1134 | qDebug() << "Service call failed with" << serviceResult; |
1135 | return; |
1136 | } |
1137 | |
1138 | // Print what we got so far |
1139 | for (const auto &result : response->events()) { |
1140 | qDebug() << "Results for" << result.nodeId() << result.statusCode(); |
1141 | for (const auto &event : result.events()) |
1142 | qDebug() << " Event:" << event; |
1143 | } |
1144 | |
1145 | if (response->hasMoreData()) |
1146 | response->readMoreData(); |
1147 | }); |
1148 | \endcode |
1149 | */ |
1150 | QOpcUaHistoryReadResponse *QOpcUaClient::readHistoryEvents(const QOpcUaHistoryReadEventRequest &request) |
1151 | { |
1152 | Q_D(const QOpcUaClient); |
1153 | return d->m_impl->readHistoryEvents(request); |
1154 | } |
1155 | |
1156 | QT_END_NAMESPACE |
1157 |
Definitions
- QOpcUaClient
- ~QOpcUaClient
- setApplicationIdentity
- applicationIdentity
- setPkiConfiguration
- pkiConfiguration
- connectToEndpoint
- disconnectFromEndpoint
- endpoint
- state
- error
- node
- node
- updateNamespaceArray
- namespaceArray
- resolveExpandedNodeId
- qualifiedNameFromNamespaceUri
- addNode
- deleteNode
- addReference
- deleteReference
- requestEndpoints
- findServers
- readNodeAttributes
- writeNodeAttributes
- backend
- setNamespaceAutoupdate
- isNamespaceAutoupdateEnabled
- setNamespaceAutoupdateInterval
- namespaceAutoupdateInterval
- setAuthenticationInformation
- authenticationInformation
- setConnectionSettings
- connectionSettings
- supportedSecurityPolicies
- supportedUserTokenTypes
- readHistoryData
- registerNodes
- unregisterNodes
Learn to use CMake with our Intro Training
Find out more