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
14QT_BEGIN_NAMESPACE
15
16Q_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 \internal QOpcUaClientImpl is an opaque type (as seen from the public API).
266 This prevents users of the public API to use this constructor (even though
267 it is public).
268*/
269QOpcUaClient::QOpcUaClient(QOpcUaClientImpl *impl, QObject *parent)
270 : QObject(*(new QOpcUaClientPrivate(impl)), parent)
271{
272 impl->m_client = this;
273}
274
275/*!
276 Destroys the \l QOpcUaClient instance.
277*/
278QOpcUaClient::~QOpcUaClient()
279{
280}
281
282/*!
283 Sets the application identity for this \l QOpcUaClient instance to \a identity.
284 \since QtOpcUa 5.13
285*/
286void QOpcUaClient::setApplicationIdentity(const QOpcUaApplicationIdentity &identity)
287{
288 Q_D(QOpcUaClient);
289 d->setApplicationIdentity(identity);
290}
291
292/*!
293 Returns the application identity of this \l QOpcUaClient instance.
294 \since QtOpcUa 5.13
295*/
296QOpcUaApplicationIdentity QOpcUaClient::applicationIdentity() const
297{
298 Q_D(const QOpcUaClient);
299 return d->applicationIdentity();
300}
301
302/*!
303 Sets the application PKI configuration for this \l QOpcUaClient instance to \a config.
304 \since QtOpcUa 5.13
305*/
306void QOpcUaClient::setPkiConfiguration(const QOpcUaPkiConfiguration &config)
307{
308 Q_D(QOpcUaClient);
309 d->setPkiConfiguration(config);
310}
311
312/*!
313 Returns the application's PKI configuration of this \l QOpcUaClient instance.
314 \since QtOpcUa 5.13
315*/
316QOpcUaPkiConfiguration QOpcUaClient::pkiConfiguration() const
317{
318 Q_D(const QOpcUaClient);
319 return d->pkiConfiguration();
320}
321
322/*!
323 Connects to the OPC UA endpoint given in \a endpoint.
324 \since QtOpcUa 5.13
325
326 \code
327 QEndpointDescription endpointDescription;
328 ...
329 client->connectToEndpoint(endpointDescription);
330 \endcode
331
332 A list of available endpoints is usually obtained by calling \l QOpcUaClient::requestEndpoints().
333
334 If the endpoint requires username authentication, at least a user name must be set in \l QOpcUaAuthenticationInformation.
335 Calling this function before setting an authentication information will use the anonymous authentication.
336
337 \code
338 QOpcUaAuthenticationInformation authInfo;
339 authInfo.setUsernameAuthentication("user", "password");
340
341 client->setAuthenticationInformation(authInfo);
342 \endcode
343
344 \sa connected(), stateChanged(), setAuthenticationInformation(), QOpcUaEndpointDescription
345*/
346void QOpcUaClient::connectToEndpoint(const QOpcUaEndpointDescription &endpoint)
347{
348 Q_D(QOpcUaClient);
349 d->connectToEndpoint(endpoint);
350}
351
352/*!
353 Disconnects from the server.
354 \sa disconnected(), connectToEndpoint()
355*/
356void QOpcUaClient::disconnectFromEndpoint()
357{
358 Q_D(QOpcUaClient);
359 d->disconnectFromEndpoint();
360}
361
362/*!
363 Returns the description of the endpoint the client is currently connected to
364 or was last connected to.
365*/
366QOpcUaEndpointDescription QOpcUaClient::endpoint() const
367{
368 Q_D(const QOpcUaClient);
369 return d->m_endpoint;
370}
371
372QOpcUaClient::ClientState QOpcUaClient::state() const
373{
374 Q_D(const QOpcUaClient);
375 return d->m_state;
376}
377
378/*!
379 Returns the current error state of the client.
380*/
381QOpcUaClient::ClientError QOpcUaClient::error() const
382{
383 Q_D(const QOpcUaClient);
384 return d->m_error;
385}
386
387/*!
388 Returns a \l QOpcUaNode object associated with the OPC UA node identified
389 by \a nodeId. The caller becomes owner of the node object.
390
391 If the client is not connected, \c nullptr is returned. The backends may also
392 return \c nullptr for other error cases (for example for a malformed node id).
393*/
394QOpcUaNode *QOpcUaClient::node(const QString &nodeId)
395{
396 if (state() != QOpcUaClient::Connected)
397 return nullptr;
398
399 Q_D(QOpcUaClient);
400 return d->m_impl->node(nodeId);
401}
402
403/*!
404 Returns a \l QOpcUaNode object associated with the OPC UA node identified
405 by \a expandedNodeId. The caller becomes owner of the node object.
406
407 If the node is not on the currently connected server, the namespace can't be resolved,
408 the node id is malformed or the client is not connected, \c nullptr is returned.
409
410 \sa updateNamespaceArray()
411*/
412QOpcUaNode *QOpcUaClient::node(const QOpcUaExpandedNodeId &expandedNodeId)
413{
414 if (expandedNodeId.serverIndex()) {
415 qCWarning(QT_OPCUA) << "Can't create a QOpcuaNode for a node on a different server.";
416 return nullptr;
417 }
418
419 const QString nodeId = resolveExpandedNodeId(expandedNodeId);
420
421 if (!nodeId.isEmpty())
422 return node(nodeId);
423 else
424 return nullptr;
425}
426
427/*!
428 Requests an update of the namespace array from the server.
429 Returns \c true if the operation has been successfully dispatched.
430
431 The \l namespaceArrayUpdated() signal is emitted after the operation is finished.
432
433 \sa namespaceArray() namespaceArrayUpdated()
434*/
435bool QOpcUaClient::updateNamespaceArray()
436{
437 if (state() != QOpcUaClient::Connected)
438 return false;
439
440 Q_D(QOpcUaClient);
441 return d->updateNamespaceArray();
442}
443
444/*!
445 Returns the cached value of the namespace array.
446
447 The value is only valid after the \l namespaceArrayUpdated() signal has been emitted.
448
449 \sa updateNamespaceArray() namespaceArrayUpdated()
450*/
451QStringList QOpcUaClient::namespaceArray() const
452{
453 Q_D(const QOpcUaClient);
454 return d->namespaceArray();
455}
456
457/*!
458 Attempts to resolve \a expandedNodeId to a node id string with numeric namespace index.
459 Returns the node id string if the conversion was successful.
460
461 An empty string is returned if the namespace index can't be resolved or if the identifier part
462 of the expanded node id is malformed. \a ok will be set to \c true if the conversion has been successful.
463 If the expanded node id could not be resolved, \a ok will be set to \c false.
464*/
465QString QOpcUaClient::resolveExpandedNodeId(const QOpcUaExpandedNodeId &expandedNodeId, bool *ok) const
466{
467 if (expandedNodeId.serverIndex() && !expandedNodeId.namespaceUri().isEmpty()) {
468 qCWarning(QT_OPCUA) << "Can't resolve a namespace index on a different server.";
469 if (ok)
470 *ok = false;
471 return QString();
472 }
473
474 if (expandedNodeId.namespaceUri().isEmpty()) {
475 if (ok)
476 *ok = true;
477 return expandedNodeId.nodeId();
478 } else {
479 if (!namespaceArray().size()) {
480 qCWarning(QT_OPCUA) << "Namespaces table missing, unable to resolve namespace URI.";
481 if (ok)
482 *ok = false;
483 return QString();
484 }
485
486 int index = namespaceArray().indexOf(str: expandedNodeId.namespaceUri());
487
488 if (index < 0) {
489 qCWarning(QT_OPCUA) << "Failed to resolve namespace" << expandedNodeId.namespaceUri();
490 if (ok)
491 *ok = false;
492 return QString();
493 }
494
495 QStringList splitId = expandedNodeId.nodeId().split(sep: QLatin1String(";"));
496 if (splitId.size() != 2) {
497 qCWarning(QT_OPCUA) << "Failed to split node id" << expandedNodeId.nodeId();
498 if (ok)
499 *ok = false;
500 return QString();
501 }
502
503 if (ok)
504 *ok = true;
505 return QStringLiteral("ns=%1;").arg(a: index).append(s: splitId.at(i: 1));
506 }
507}
508
509/*!
510 Attempts to create a qualified name from \a namespaceUri and the name string \a name.
511 Returns the resulting qualified name. An empty qualified name is returned if
512 \a namespaceUri can't be resolved.
513
514 \a ok will be set to \c true if the namespace URI resolution has been successful.
515 If the namespace URI could not be resolved, \a ok will be set to \c false.
516*/
517QOpcUaQualifiedName QOpcUaClient::qualifiedNameFromNamespaceUri(const QString &namespaceUri, const QString &name, bool *ok) const
518{
519 if (namespaceArray().isEmpty()) {
520 qCWarning(QT_OPCUA) << "Namespaces table missing, unable to resolve namespace URI.";
521 if (ok)
522 *ok = false;
523 return QOpcUaQualifiedName();
524 }
525
526 int index = namespaceArray().indexOf(str: namespaceUri);
527
528 if (index < 0) {
529 qCWarning(QT_OPCUA) << "Failed to resolve namespace" << namespaceUri;
530 if (ok)
531 *ok = false;
532 return QOpcUaQualifiedName();
533 }
534
535 if (ok)
536 *ok = true;
537
538 return QOpcUaQualifiedName(index, name);
539};
540
541/*!
542 Adds the node described by \a nodeToAdd on the server.
543
544 Returns \c true if the asynchronous call has been successfully dispatched.
545
546 The success of the operation is returned in the \l addNodeFinished() signal.
547
548 The following example code adds new a Variable node on the server:
549
550 \code
551 QOpcUaNodeCreationAttributes attributes;
552 attributes.setDisplayName(QOpcUaLocalizedText("en", "My new Variable node"));
553 attributes.setDescription(QOpcUaLocalizedText("en", "A node which has been added at runtime"));
554 attributes.setValue(23.0, QOpcUa::Types::Double);
555 attributes.setDataTypeId(QOpcUa::ns0ID(QOpcUa::NodeIds::Namespace0::Double));
556 attributes.setValueRank(-2); // Scalar or array
557 attributes.setAccessLevel(QOpcUa::AccessLevelBit::CurrentRead);
558 attributes.setUserAccessLevel(QOpcUa::AccessLevelBit::CurrentRead);
559
560 QOpcUaAddNodeItem item;
561 item.setParentNodeId(QOpcUaExpandedNodeId("ns=3;s=TestFolder"));
562 item.setReferenceTypeId(QOpcUa::nodeIdFromReferenceType(QOpcUa::ReferenceTypeId::Organizes));
563 item.setRequestedNewNodeId(QOpcUaExpandedNodeId("ns=3;s=MyNewVariableNode"));
564 item.setBrowseName(QOpcUaQualifiedName(3, "MyNewVariableNode"));
565 item.setNodeClass(QOpcUa::NodeClass::Variable);
566 item.setNodeAttributes(attributes);
567
568 m_client->addNode(item);
569 \endcode
570
571 \sa deleteNode() addNodeFinished() QOpcUaAddNodeItem
572*/
573bool QOpcUaClient::addNode(const QOpcUaAddNodeItem &nodeToAdd)
574{
575 if (state() != QOpcUaClient::Connected)
576 return false;
577
578 Q_D(QOpcUaClient);
579 return d->m_impl->addNode(nodeToAdd);
580}
581
582/*!
583 Deletes the node with node id \a nodeId from the server.
584 If \a deleteTargetReferences is \c false, only the references with source node \a nodeId are deleted.
585 If \a deleteTargetReferences is \c true, references with \a nodeId as target are deleted too.
586
587 Returns \c true if the asynchronous call has been successfully dispatched.
588
589 The success of the operation is returned in the \l deleteNodeFinished() signal.
590
591 The following example code deletes a node and all references to it from the server:
592
593 \code
594 m_client->deleteNode(QOpcUaExpandedNodeId("ns=3;s=MyNewVariableNode"), true);
595 \endcode
596
597 \sa addNode() deleteNodeFinished()
598*/
599bool QOpcUaClient::deleteNode(const QString &nodeId, bool deleteTargetReferences)
600{
601 if (state() != QOpcUaClient::Connected)
602 return false;
603
604 Q_D(QOpcUaClient);
605 return d->m_impl->deleteNode(nodeId, deleteTargetReferences);
606}
607
608/*!
609 Adds the reference described by \a referenceToAdd to the server.
610
611 Returns \c true if the asynchronous call has been successfully dispatched.
612
613 The success of the operation is returned in the \l addReferenceFinished() signal.
614
615 The following example code adds a reference to a node to the "Objects" folder:
616
617 \code
618 QOpcUaAddReferenceItem item;
619 item.setSourceNodeId(QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ObjectsFolder));
620 item.setReferenceTypeId(QOpcUa::nodeIdFromInteger(0, static_cast<quint32>(QOpcUa::ReferenceTypeId::Organizes)));
621 item.setIsForwardReference(true);
622 item.setTargetNodeId(QOpcUaExpandedNodeId("ns=3;s=MyNewVariableNode"));
623 item.setTargetNodeClass(QOpcUa::NodeClass::Variable);
624
625 m_client->addReference(item);
626 \endcode
627
628 \sa deleteReference() addReferenceFinished() QOpcUaAddReferenceItem
629*/
630bool QOpcUaClient::addReference(const QOpcUaAddReferenceItem &referenceToAdd)
631{
632 if (state() != QOpcUaClient::Connected)
633 return false;
634
635 Q_D(QOpcUaClient);
636 return d->m_impl->addReference(referenceToAdd);
637}
638
639/*!
640 Deletes the reference described by \a referenceToDelete from the server.
641
642 Returns \c true if the asynchronous call has been successfully dispatched.
643
644 The success of the operation is returned in the \l deleteReferenceFinished() signal.
645
646 The following example code deletes a reference to a node from the "Objects" folder:
647
648 \code
649 QOpcUaDeleteReferenceItem item;
650 item.setSourceNodeId(QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ObjectsFolder));
651 item.setReferenceTypeId(QOpcUa::nodeIdFromInteger(0, static_cast<quint32>(QOpcUa::ReferenceTypeId::Organizes)));
652 item.setIsForwardReference(true);
653 item.setTargetNodeId(QOpcUaExpandedNodeId("ns=3;s=MyNewVariableNode"));
654 item.setDeleteBidirectional(true);
655
656 m_client->deleteReference(item);
657 \endcode
658
659 \sa addReference() deleteReferenceFinished() QOpcUaDeleteReferenceItem
660*/
661bool QOpcUaClient::deleteReference(const QOpcUaDeleteReferenceItem &referenceToDelete)
662{
663 if (state() != QOpcUaClient::Connected)
664 return false;
665
666 Q_D(QOpcUaClient);
667 return d->m_impl->deleteReference(referenceToDelete);
668}
669
670/*!
671 Starts an asynchronous \c GetEndpoints request to read a list of available endpoints
672 from the server at \a url.
673 Returns \c true if the asynchronous call has been successfully dispatched.
674
675 The endpoint information is returned in the \l endpointsRequestFinished() signal.
676*/
677bool QOpcUaClient::requestEndpoints(const QUrl &url)
678{
679 Q_D(QOpcUaClient);
680 return d->m_impl->requestEndpoints(url);
681}
682
683/*!
684 Starts an asynchronous FindServers request to read a list of known servers from a server or
685 discovery server at \a url.
686 Returns \c true if the asynchronous call has been successfully dispatched.
687
688 \a localeIds can be used to select the language of the application names returned by the request.
689 The format is specified in OPC-UA part 3, 8.4, for example "en" for English, or "de-DE" for
690 German (Germany). If more than one locale ID is specified, the server uses the first match. If there
691 is no match or \a localeIds is empty, a default locale is chosen by the server.
692
693 \a serverUris may be used to restrict the results to servers with a matching applicationUri in their
694 application description. For example, finding the current URL of the server with the applicationUri
695 "MyPLC", the following call can be used:
696
697 \code
698 client->findServers(discoveryServerUrl, QStringList(), QStringList({"MyPLC"}));
699 \endcode
700
701 The results are returned in the \l findServersFinished() signal.
702*/
703bool QOpcUaClient::findServers(const QUrl &url, const QStringList &localeIds, const QStringList &serverUris)
704{
705 Q_D(QOpcUaClient);
706 return d->m_impl->findServers(url, localeIds, serverUris);
707}
708
709/*!
710 Starts a read of multiple attributes on different nodes.
711 The node id, the attribute and an index range can be specified for every entry in \a nodesToRead.
712
713 Returns true if the asynchronous request has been successfully dispatched.
714 The results are returned in the \l readNodeAttributesFinished() signal.
715
716 This read function offers an alternative way to read attributes of nodes which can be used
717 for scenarios where the values of a large number of node attributes on different nodes must be read
718 without requiring the other features of the \l QOpcUaNode based API like monitoring for value changes.
719 All read items in the request are sent to the server in a single request and are answered in a single
720 response which generates a single \l readNodeAttributesFinished() signal. This reduces the network overhead and
721 the number of signal slot connections if many different nodes are involved.
722
723 In the following example, the display name attribute and the two index ranges "0:2" and "5:7" of the value
724 attribute of the same node and the entire value attribute of a second node are read using a single service call:
725 \code
726 QList<QOpcUaReadItem> request;
727 request.push_back(QOpcUaReadItem("ns=1;s=MyArrayNode",
728 QOpcUa::NodeAttribute::DisplayName));
729 request.push_back(QOpcUaReadItem("ns=1;s=MyArrayNode",
730 QOpcUa::NodeAttribute::Value, "0:2"));
731 request.push_back(QOpcUaReadItem("ns=1;s=MyArrayNode",
732 QOpcUa::NodeAttribute::Value, "5:7"));
733 request.push_back(QOpcUaReadItem("ns=1;s=MyScalarNode));
734 m_client->readNodeAttributes(request);
735 \endcode
736
737 \sa QOpcUaReadItem readNodeAttributesFinished()
738*/
739bool QOpcUaClient::readNodeAttributes(const QList<QOpcUaReadItem> &nodesToRead)
740{
741 if (state() != QOpcUaClient::Connected)
742 return false;
743
744 Q_D(QOpcUaClient);
745 return d->m_impl->readNodeAttributes(nodesToRead);
746}
747
748/*!
749 Starts a write for multiple attributes on different nodes.
750 The node id, the attribute, the value, the value type and an index range can be specified
751 for every entry in \a nodesToWrite.
752
753 Returns \c true if the asynchronous request has been successfully dispatched.
754 The results are returned in the \l writeNodeAttributesFinished() signal.
755
756 This write function offers an alternative way to write attributes of nodes which can be used
757 for scenarios where the values of a large number of node attributes on different nodes must be written
758 without requiring the other features of the \l QOpcUaNode based API like monitoring for value changes.
759 All write items in the request are sent to the server in a single request and are answered in a single
760 response which generates a single \l writeNodeAttributesFinished() signal. This reduces the network overhead and
761 the number of signal slot connections if many different nodes are involved.
762
763 In the following example, the Values attributes of two different nodes are written in one call.
764 The second node has an array value of which only the first two elements are overwritten:
765
766 \code
767 QList<QOpcUaWriteItem> request;
768
769 request.append(QOpcUaWriteItem("ns=2;s=Demo.Static.Scalar.Double", QOpcUa::NodeAttribute::Value,
770 23.0, QOpcUa::Types::Double));
771 request.append(QOpcUaWriteItem("ns=2;s=Demo.Static.Arrays.UInt32", QOpcUa::NodeAttribute::Value,
772 QVariantList({0, 1, 2}), QOpcUa::Types::UInt32, "0:2"));
773
774 m_client->writeNodeAttributes(request);
775 \endcode
776
777 \sa QOpcUaWriteItem writeNodeAttributesFinished()
778*/
779bool QOpcUaClient::writeNodeAttributes(const QList<QOpcUaWriteItem> &nodesToWrite)
780{
781 if (state() != QOpcUaClient::Connected)
782 return false;
783
784 Q_D(QOpcUaClient);
785 return d->m_impl->writeNodeAttributes(nodesToWrite);
786}
787
788/*!
789 Returns the name of the backend used by this instance of QOpcUaClient,
790 e.g. "open62541".
791*/
792QString QOpcUaClient::backend() const
793{
794 Q_D(const QOpcUaClient);
795 return d->m_impl->backend();
796}
797
798/*!
799 Enables automatic update of the namespace table.
800
801 Enabling this will keep the local copy of the namespace table updated automatically.
802 \l namespaceArrayUpdated will be emitted when the array changed.
803 \a isEnabled determines if autoupdate is being enabled or disabled.
804
805 A subscription will be made on the node on the server to keep track of changes.
806 In case a server does not support subscriptions this will not work and
807 \l isNamespaceAutoupdateEnabled returns \c false.
808
809 \sa namespaceArray() namespaceArrayUpdated()
810*/
811void QOpcUaClient::setNamespaceAutoupdate(bool isEnabled)
812{
813 Q_D(QOpcUaClient);
814 d->m_enableNamespaceArrayAutoupdate = isEnabled;
815 d->setupNamespaceArrayMonitoring();
816}
817
818/*!
819 Returns whether autoupdate of the namespace array is enabled.
820*/
821bool QOpcUaClient::isNamespaceAutoupdateEnabled() const
822{
823 Q_D(const QOpcUaClient);
824 return d->m_enableNamespaceArrayAutoupdate;
825}
826
827/*!
828 Sets the interval for the namespace table subscription.
829
830 The subscription may be revised by the server.
831
832 \a interval determines the interval to check for changes in milliseconds. The default is once per second.
833
834 \sa QOpcUaClient::setNamespaceAutoupdate(bool isEnabled)
835*/
836void QOpcUaClient::setNamespaceAutoupdateInterval(int interval)
837{
838 Q_D(QOpcUaClient);
839 d->m_namespaceArrayUpdateInterval = interval;
840 d->setupNamespaceArrayMonitoring();
841}
842
843/*!
844 Returns the current revised update interval of the namespace array.
845
846 \sa setNamespaceAutoupdateInterval(int interval)
847*/
848int QOpcUaClient::namespaceAutoupdateInterval() const
849{
850 Q_D(const QOpcUaClient);
851 return d->m_namespaceArrayUpdateInterval;
852}
853
854/*!
855 Sets the authentication information of this client to \a authenticationInformation.
856
857 \sa connectToEndpoint()
858*/
859void QOpcUaClient::setAuthenticationInformation(const QOpcUaAuthenticationInformation &authenticationInformation)
860{
861 Q_D(QOpcUaClient);
862 d->m_authenticationInformation = authenticationInformation;
863}
864
865/*!
866 Returns the current authentication information.
867*/
868const QOpcUaAuthenticationInformation &QOpcUaClient::authenticationInformation() const
869{
870 Q_D(const QOpcUaClient);
871 return d->m_authenticationInformation;
872}
873
874/*!
875 \since 6.6
876
877 Sets the connection settings for this client to \a connectionSettings.
878
879 Example:
880 \code
881 QOpcUaConnectionSettings settings;
882 // Ask the server to give localized texts in german with french as fallback
883 settings.setSessionLocaleIds({ "de", "fr" });
884 // We need to call some long running methods, increase the request timeout
885 settings.setRequestTimeout(std::chrono::minutes(2));
886 opcuaClient->setConnectionSettings(settings);
887 \endcode
888
889 The values from \a connectionSettings are applied to any new connections after this point.
890
891 \sa connectionSettings()
892 */
893void QOpcUaClient::setConnectionSettings(const QOpcUaConnectionSettings &connectionSettings)
894{
895 Q_D(QOpcUaClient);
896 d->m_connectionSettings = connectionSettings;
897}
898
899/*!
900 \since 6.6
901
902 Returns the connection settings for this client.
903
904 \sa setConnectionSettings()
905 */
906QOpcUaConnectionSettings QOpcUaClient::connectionSettings() const
907{
908 Q_D(const QOpcUaClient);
909 return d->m_connectionSettings;
910}
911
912/*!
913 \since QtOpcUa 5.14
914
915 Returns the security policies supported by the used backend.
916
917 This function is currently available as a Technology Preview, and therefore the API
918 and functionality provided by the function may be subject to change at any time without
919 prior notice.
920*/
921QStringList QOpcUaClient::supportedSecurityPolicies() const
922{
923 Q_D(const QOpcUaClient);
924 return d->m_impl->supportedSecurityPolicies();
925}
926
927/*!
928 \since QtOpcUa 5.14
929
930 Returns the user token types supported by the used backend.
931
932 This function is currently available as a Technology Preview, and therefore the API
933 and functionality provided by the function may be subject to change at any time without
934 prior notice.
935
936 \sa QOpcUaUserTokenPolicy::TokenType
937*/
938QList<QOpcUaUserTokenPolicy::TokenType> QOpcUaClient::supportedUserTokenTypes() const
939{
940 Q_D(const QOpcUaClient);
941 return d->m_impl->supportedUserTokenTypes();
942}
943
944/*!
945 \since 6.3
946
947 Starts a read raw history \a request for one or multiple nodes. This is the Qt OPC UA representation for the OPC UA
948 ReadHistory service for reading raw historical data defined in
949 \l {https://reference.opcfoundation.org/v104/Core/docs/Part4/5.10.3/} {OPC-UA part 4, 5.10.3}.
950
951 The start timestamp, end timestamp, number of values per node, returnBounds and nodes to read
952 can be specified in a \l QOpcUaHistoryReadRawRequest.
953
954 Returns a \l QOpcUaHistoryReadResponse which contains the state of the request if the asynchronous
955 request has been successfully dispatched. The results are returned in the
956 \l QOpcUaHistoryReadResponse::readHistoryDataFinished(const QList<QOpcUaHistoryData> &results, QOpcUa::UaStatusCode serviceResult)
957 signal.
958
959 In the following example, the historic data from the last two days of two nodes are requested and printed.
960 The result is limited to ten values per node.
961
962 \code
963 QOpcUaHistoryReadRawRequest request(
964 { QOpcUaReadItem("ns=1;s=myValue1"), QOpcUaReadItem("ns=1;s=myValue2") },
965 QDateTime::currentDateTime(),
966 QDateTime::currentDateTime().addDays(-2),
967 10,
968 true);
969
970 QOpcUaHistoryReadResponse *response = m_client->readHistoryData(request);
971 if (response) {
972 QObject::connect(response, &QOpcUaHistoryReadResponse::readHistoryDataFinished,
973 [] (QList<QOpcUaHistoryData> results, QOpcUa::UaStatusCode serviceResult) {
974 if (serviceResult != QOpcUa::UaStatusCode::Good) {
975 qWarning() << "Fetching historical data failed with:" << serviceResult;
976 } else {
977 for (const auto& result : results) {
978 qInfo() << "NodeId:" << result.nodeId();
979 for (const auto &dataValue : result.result())
980 qInfo() << "Value:" << dataValue.value();
981 }
982 }
983 });
984 }
985 \endcode
986*/
987QOpcUaHistoryReadResponse *QOpcUaClient::readHistoryData(const QOpcUaHistoryReadRawRequest &request)
988{
989 Q_D(const QOpcUaClient);
990 return d->m_impl->readHistoryData(request);
991}
992
993QT_END_NAMESPACE
994

source code of qtopcua/src/opcua/client/qopcuaclient.cpp