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