1// Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qopcuaclient.h"
5#include "qopcuanode.h"
6#include <private/qopcuaclient_p.h>
7#include <private/qopcuaclientimpl_p.h>
8#include <private/qopcuanode_p.h>
9#include <private/qopcuanodeimpl_p.h>
10
11#include "qopcuarelativepathelement.h"
12
13QT_BEGIN_NAMESPACE
14
15/*!
16 \class QOpcUaNode
17 \inmodule QtOpcUa
18
19 \brief QOpcUaNode allows interaction with an OPC UA node.
20
21
22 The node is the basic building block of the OPC UA address space.
23 It has attributes like browse name, value, associated properties and can have
24 references to other nodes in the address space.
25 Nodes are organized in namespaces and have IDs which can e.g. be numeric,
26 a string, a namespace-specific format (opaque) or a globally unique identifier.
27 A node is identified by the namespace ID and the node ID.
28 This identifier is usually given as a string:
29 The identifier of a node residing in namespace 0 and having the numeric
30 identifier 42 results in the string \c ns=0;i=42. A node with a string
31 identifier can be addressed via \c ns=0;s=myStringIdentifier.
32
33 Objects of this type are owned by the user and must be deleted when they are
34 no longer needed. They are valid as long as the \l QOpcUaClient which created them exists.
35
36 \section1 Reading and writing of attributes
37
38 The node attributes are read from the server when \l readAttributes() or \l readAttributeRange()
39 is called. The results are cached locally and can be retrieved using \l attribute()
40 after the \l attributeRead signal has been received.
41
42 Attributes can be written using \l writeAttribute(), \l writeAttributes() and \l writeAttributeRange()
43 if the user has the necessary rights.
44 Success of the write operation is reported using the \l attributeWritten signal.
45
46 \l attributeError() contains a status code associated with the last read or write operation
47 on the attribute. This is the low level status code returned by the OPC UA service.
48 This status code can be simplified by converting it to a \l QOpcUa::ErrorCategory using
49 \l QOpcUa::errorCategory().
50
51 \section1 Subscriptions and monitored items
52 Subscriptions are a concept in OPC UA which allows receiving of notifications for changes in data
53 or in case of events instead of continuously polling a node for changes.
54 Monitored items define how attributes of a node are watched for changes. They are added to a
55 subscription and any notifications they generate are forwarded to the user via the subscription.
56 The interval of the updates as well as many other options of the monitored items and subscriptions
57 can be configured by the user.
58
59 \l QOpcUaNode offers an abstraction to interact with subscriptions and monitored items.
60 \l enableMonitoring() enables data change notifications for one or more attributes.
61 The \l dataChangeOccurred signal contains new values and the local cache is updated.
62 \l disableMonitoring() disables the data change notifications.
63 The \l monitoringStatusChanged signal notifies about changes of the monitoring status, e. g. after
64 manual enable and disable or a status change on the server.
65
66 Event monitoring uses the same API for setup and life cycle management.
67 The \l {QOpcUa::NodeAttribute} {EventNotifier} attribute
68 must be monitored using an \l {QOpcUaMonitoringParameters::EventFilter} {EventFilter} which selects
69 the required event fields and filters the reported events by user defined criteria. The events are
70 reported in the \l eventOccurred() signal as a \l QVariantList which contains the values of the selected
71 event fields.
72
73 Settings of the subscription and monitored item can be modified at runtime using \l modifyMonitoring().
74
75 \section1 Browsing the address space
76 The OPC UA address space consists of nodes connected by references.
77 \l browseChildren follows these references in forward direction and returns attributes from all
78 nodes connected to the node behind an instance of \l QOpcUaNode in the \l browseFinished signal.
79 \l browse() is similar to \l browseChildren() but offers more options to configure the browse call.
80
81 \section1 Method calls
82 OPC UA specifies methods on the server which can be called by the user.
83 \l QOpcUaNode supports this via \l callMethod which takes parameters and returns the results of
84 the call in the \l methodCallFinished signal.
85
86 \section1 Resolving browse paths
87 To support programming against a type description, OPC UA supports the resolution of a path of browse names
88 starting from a certain node to obtain the node id of the target node. The \l resolveBrowsePath() method
89 follows a path starting from the node it was called on and returns the result in the
90 \l resolveBrowsePathFinished() signal.
91
92 \section1 Example
93 For connecting the client to a server and getting a \l QOpcUaNode object, see \l QOpcUaClient.
94
95 After the node has been successfully created, the BrowseName of the root node is read from the server:
96
97 \code
98 QOpcUaNode *rootNode; // Created before, see QOpcUaClient documentation.
99 // Connect to the attributeRead signal. Compatible slots of QObjects can be used instead of a lambda.
100 QObject::connect(rootNode, &QOpcUaNode::attributeRead, [rootNode, client](QOpcUa::NodeAttributes attr) {
101 qDebug() << "Signal for attributes:" << attr;
102 if (rootNode->attributeError(QOpcUa::NodeAttribute::BrowseName) != QOpcUa::UaStatusCode::Good) {
103 qDebug() << "Failed to read attribute:" << rootNode->attributeError(QOpcUa::NodeAttribute::BrowseName);
104 client->disconnectFromEndpoint();
105 }
106 qDebug() << "Browse name:" << rootNode->attribute(QOpcUa::NodeAttribute::BrowseName).value<QOpcUaQualifiedName>().name();
107 });
108 rootNode->readAttributes(QOpcUa::NodeAttribute::BrowseName); // Start a read operation for the node's BrowseName attribute.
109 \endcode
110*/
111
112/*!
113 \typedef QOpcUaNode::AttributeMap
114
115 This type is used by \l writeAttributes() to write more than one attribute at a time.
116 QVariant values must be assigned to the attributes to be written.
117*/
118
119/*!
120 \fn void QOpcUaNode::attributeRead(QOpcUa::NodeAttributes attributes)
121
122 This signal is emitted after a \l readAttributes() or \l readAttributeRange() operation has finished.
123 The receiver has to check the status code for the attributes contained in \a attributes.
124*/
125
126/*!
127 \fn void QOpcUaNode::attributeWritten(QOpcUa::NodeAttribute attribute, QOpcUa::UaStatusCode statusCode)
128
129 This signal is emitted after a \l writeAttribute(), \l writeAttributes() or \l writeAttributeRange()
130 operation has finished.
131
132 Before this signal is emitted, the attribute cache is updated in case of a successful write.
133 For \l writeAttributes() a signal is emitted for each attribute in the write call.
134 \a statusCode contains the success information for the write operation on \a attribute.
135*/
136
137/*!
138 \fn void QOpcUaNode::dataChangeOccurred(QOpcUa::NodeAttribute attr, QVariant value)
139
140 This signal is emitted after a data change notification has been received. \a value contains the
141 new value for the node attribute \a attr.
142
143 \sa attribute() serverTimestamp() sourceTimestamp()
144*/
145
146/*!
147 \fn void QOpcUaNode::attributeUpdated(QOpcUa::NodeAttribute attr, QVariant value)
148
149 This signal is emitted after the value in the attribute cache has been updated by a
150 data change notification from the server, a read or a write operation. \a value contains the
151 new value for the node attribute \a attr.
152
153 \sa attribute() attributeError() serverTimestamp() sourceTimestamp()
154*/
155
156/*!
157 \fn void QOpcUaNode::enableMonitoringFinished(QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode)
158
159 This signal is emitted after an asynchronous call to \l enableMonitoring() has finished.
160 After this signal has been emitted, \l monitoringStatus() returns valid information for \a attr.
161 \a statusCode contains the status code for the operation.
162*/
163
164/*!
165 \fn void QOpcUaNode::disableMonitoringFinished(QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode)
166
167 This signal is emitted after an asynchronous call to \l disableMonitoring() has finished. \a statusCode contains
168 the status code generated by the operation.
169 After this signal has been emitted, monitoringStatus returns a default constructed value with
170 status code BadMonitoredItemIdIinvalid for \a attr.
171*/
172
173/*!
174 \fn void QOpcUaNode::monitoringStatusChanged(QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items, QOpcUa::UaStatusCode statusCode);
175
176 This signal is emitted after an asynchronous call to \l modifyMonitoring() has finished.
177 The node attribute for which the operation was requested is returned in \a attr. \a items contains the parameters that have been modified.
178 \a statusCode contains the result of the modify operation on the server.
179*/
180
181/*!
182 \fn void QOpcUaNode::methodCallFinished(QString methodNodeId, QVariant result, QOpcUa::UaStatusCode statusCode)
183
184 This signal is emitted after a method call for \a methodNodeId has finished on the server.
185 \a statusCode contains the status code from the method call, \a result contains the output
186 arguments of the method. \a result is empty if the method has no output arguments or \a statusCode
187 is not \l {QOpcUa::UaStatusCode} {Good}.
188 The \a result variant is either a single value if there is only one output argument or it contains a list of
189 variants in case the called function returned multiple output arguments.
190 \code
191 if (result.canConvert<QVariantList>()) {
192 // handle list type
193 } else {
194 // handle value type
195 }
196 \endcode
197*/
198
199/*!
200 \fn void QOpcUaNode::browseFinished(QList<QOpcUaReferenceDescription> children, QOpcUa::UaStatusCode statusCode)
201
202 This signal is emitted after a \l browseChildren() or \l browse() operation has finished.
203
204 \a children contains information about all nodes which matched the criteria in \l browseChildren().
205 \a statusCode contains the service result of the browse operation. If \a statusCode is not \l {QOpcUa::UaStatusCode} {Good},
206 the passed \a children vector is empty.
207 \sa QOpcUaReferenceDescription
208*/
209
210/*!
211 \fn void QOpcUaNode::resolveBrowsePathFinished(QList<QOpcUaBrowsePathTarget> targets, QList<QOpcUaRelativePathElement> path, QOpcUa::UaStatusCode statusCode)
212
213 This signal is emitted after a \l resolveBrowsePath() call has finished.
214
215 \l QOpcUaBrowsePathTarget \a targets contains the matches, \a statusCode is the status code of the operation.
216 If \a statusCode is not \l {QOpcUa::UaStatusCode} {Good}, \a targets is empty.
217 The browse path \a path is the browse path from the request. It can be used to associate results with requests.
218*/
219
220/*!
221 \fn void QOpcUaNode::eventOccurred(QVariantList eventFields)
222
223 This signal is emitted after a new event has been received.
224
225 \a eventFields contains the values of the event fields in the order specified in the \c select clause of the event filter.
226*/
227
228/*!
229 \fn QOpcUa::NodeAttributes QOpcUaNode::mandatoryBaseAttributes()
230
231 Contains all mandatory attributes of the OPC UA base node class.
232*/
233
234/*!
235 \fn QOpcUa::NodeAttributes QOpcUaNode::allBaseAttributes()
236
237 Contains all attributes of the OPC UA base node class.
238*/
239
240/*!
241 \internal QOpcUaNodeImpl is an opaque type (as seen from the public API).
242 This prevents users of the public API to use this constructor (even though
243 it is public).
244*/
245QOpcUaNode::QOpcUaNode(QOpcUaNodeImpl *impl, QOpcUaClient *client, QObject *parent)
246 : QObject(*new QOpcUaNodePrivate(impl, client), parent)
247{
248}
249
250QOpcUaNode::~QOpcUaNode()
251{
252}
253
254/*!
255 Starts an asynchronous read operation for the node attribute \a attribute.
256 \a indexRange is a string which can be used to select a part of an array. It is defined in OPC-UA part 4, 7.22.
257 The first element in an array is 0, "1" returns the second element, "0:9" returns the first 10 elements,
258 "0,1" returns the second element of the first row in a two-dimensional array.
259
260 Returns \c true if the asynchronous call has been successfully dispatched.
261
262 Attribute values only contain valid information after the \l attributeRead signal has been emitted.
263*/
264bool QOpcUaNode::readAttributeRange(QOpcUa::NodeAttribute attribute, const QString &indexRange)
265{
266 Q_D(QOpcUaNode);
267 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
268 return false;
269
270 return d->m_impl->readAttributes(attr: QOpcUa::NodeAttributes() | attribute, indexRange);
271}
272
273/*!
274 Starts an asynchronous read operation for the node's Value attribute.
275
276 Returns \c true if the asynchronous call has been successfully dispatched.
277
278 \sa readAttributes()
279*/
280bool QOpcUaNode::readValueAttribute()
281{
282 return readAttributes(attributes: QOpcUa::NodeAttribute::Value);
283}
284
285/*!
286 Starts an asynchronous read operation for the node attributes in \a attributes.
287
288 Returns \c true if the asynchronous call has been successfully dispatched.
289
290 Attribute values only contain valid information after the \l attributeRead signal has been emitted.
291*/
292bool QOpcUaNode::readAttributes(QOpcUa::NodeAttributes attributes)
293{
294 Q_D(QOpcUaNode);
295 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
296 return false;
297
298 return d->m_impl->readAttributes(attr: attributes, indexRange: QString());
299}
300
301/*!
302 Returns the value of the attribute given in \a attribute.
303
304 The value is only valid after the \l attributeRead signal has been emitted.
305 An empty QVariant is returned if there is no cached value for the attribute.
306*/
307QVariant QOpcUaNode::attribute(QOpcUa::NodeAttribute attribute) const
308{
309 Q_D(const QOpcUaNode);
310 auto it = d->m_nodeAttributes.constFind(key: attribute);
311 if (it == d->m_nodeAttributes.constEnd())
312 return QVariant();
313
314 return it->value();
315}
316
317/*!
318 Returns the value of the node's Value attribute.
319
320 The returned value is only valid after the Value attribute has been successfully read or written
321 or after a data change from a monitoring has updated the attribute cache.
322 This is indicated by a \l attributeRead() or \l attributeWritten() signal with status code
323 \l {QOpcUa::UaStatusCode} {Good} or a \l dataChangeOccurred() signal for the Value attribute.
324
325 If there is no value in the attribute cache, an invalid \l QVariant is returned.
326
327 \sa readValueAttribute() writeValueAttribute() valueAttributeError()
328*/
329QVariant QOpcUaNode::valueAttribute() const
330{
331 return attribute(attribute: QOpcUa::NodeAttribute::Value);
332}
333
334/*!
335 Returns the error code for the attribute given in \a attribute.
336
337 The error code is only valid after the \l attributeRead or \l attributeWritten signal has been emitted.
338
339 If there is no entry in the attribute cache, \l {QOpcUa::UaStatusCode} {BadNoEntryExists} is returned.
340
341 \sa QOpcUa::errorCategory
342 */
343QOpcUa::UaStatusCode QOpcUaNode::attributeError(QOpcUa::NodeAttribute attribute) const
344{
345 Q_D(const QOpcUaNode);
346 auto it = d->m_nodeAttributes.constFind(key: attribute);
347 if (it == d->m_nodeAttributes.constEnd())
348 return QOpcUa::UaStatusCode::BadNoEntryExists;
349
350 return it->statusCode();
351}
352
353/*!
354 Returns the error code for the node's Value attribute.
355 The status code \l {QOpcUa::UaStatusCode} {Good} indicates a valid return value for \l valueAttribute().
356 If there is no entry in the attribute cache, \l {QOpcUa::UaStatusCode} {BadNoEntryExists} is returned.
357*/
358QOpcUa::UaStatusCode QOpcUaNode::valueAttributeError() const
359{
360 return attributeError(attribute: QOpcUa::NodeAttribute::Value);
361}
362
363/*!
364 Returns the source timestamp from the last read or data change of \a attribute.
365 Before at least one \l attributeRead or \l dataChangeOccurred signal has been emitted,
366 a null datetime is returned.
367
368*/
369QDateTime QOpcUaNode::sourceTimestamp(QOpcUa::NodeAttribute attribute) const
370{
371 Q_D(const QOpcUaNode);
372 auto it = d->m_nodeAttributes.constFind(key: attribute);
373 if (it == d->m_nodeAttributes.constEnd())
374 return QDateTime();
375
376 return it->sourceTimestamp();
377}
378
379/*!
380 Returns the server timestamp from the last read or data change of \a attribute.
381 Before at least one \l attributeRead or \l dataChangeOccurred signal has been emitted,
382 a null datetime is returned.
383*/
384QDateTime QOpcUaNode::serverTimestamp(QOpcUa::NodeAttribute attribute) const
385{
386 Q_D(const QOpcUaNode);
387 auto it = d->m_nodeAttributes.constFind(key: attribute);
388 if (it == d->m_nodeAttributes.constEnd())
389 return QDateTime();
390
391 return it->serverTimestamp();
392}
393
394/*!
395 This method creates a monitored item for each of the attributes given in \a attr.
396 The settings from \a settings are used in the creation of the monitored items and the subscription.
397
398 Returns \c true if the asynchronous call has been successfully dispatched.
399
400 On completion of the call, the \l enableMonitoringFinished signal is emitted.
401 There are multiple error cases in which a bad status code is generated: A subscription with the subscription id specified in \a settings does not exist,
402 the node does not exist on the server, the node does not have the requested attribute or the maximum number of monitored items for
403 the server is reached.
404
405 The same method is used to enable event monitoring. Events are special objects in the OPC UA address space which contain information
406 about an event that has occurred. If an event is triggered on the server, an event monitored item collects selected values of
407 node attributes of the event object and its child nodes.
408 Every node that has an event source can be monitored for events.
409 To monitor a node for events, the attribute \l {QOpcUa::NodeAttribute} {EventNotifier} must be monitored using an EventFilter which contains the event fields
410 the user needs and optionally a where clause which is used to filter events by criteria (for more details, see \l QOpcUaMonitoringParameters::EventFilter).
411
412 */
413bool QOpcUaNode::enableMonitoring(QOpcUa::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
414{
415 Q_D(QOpcUaNode);
416 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
417 return false;
418
419 return d->m_impl->enableMonitoring(attr, settings);
420}
421
422/*!
423 This method modifies settings of the monitored item or the subscription.
424 The parameter \a item of the monitored item or subscription associated with \a attr is attempted to set to \a value.
425
426 Returns \c true if the asynchronous call has been successfully dispatched.
427
428 After the call has finished, the \l monitoringStatusChanged signal is emitted. This signal contains the modified parameters and the status code.
429 A bad status code is generated if there is no monitored item associated with the requested attribute, modifying the requested
430 parameter is not implemented or if the server has rejected the requested value.
431*/
432bool QOpcUaNode::modifyMonitoring(QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value)
433{
434 Q_D(QOpcUaNode);
435 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
436 return false;
437
438 return d->m_impl->modifyMonitoring(attr, item, value);
439}
440
441/*!
442 Returns the monitoring parameters associated with the attribute \a attr. This can be used to check the success of \l enableMonitoring()
443 or if parameters have been revised.
444 The returned values are only valid after \l enableMonitoringFinished or \l monitoringStatusChanged have been emitted
445 for \a attr. If the status is queried before a signal has been emitted, \l QOpcUaMonitoringParameters::statusCode()
446 returns \l {QOpcUa::UaStatusCode} {BadNoEntryExists}.
447*/
448QOpcUaMonitoringParameters QOpcUaNode::monitoringStatus(QOpcUa::NodeAttribute attr)
449{
450 Q_D(QOpcUaNode);
451 auto it = d->m_monitoringStatus.constFind(key: attr);
452 if (it == d->m_monitoringStatus.constEnd()) {
453 QOpcUaMonitoringParameters p;
454 p.setStatusCode(QOpcUa::UaStatusCode::BadNoEntryExists);
455 return p;
456 }
457
458 return *it;
459}
460
461/*!
462 Modifies an existing event monitoring to use \a eventFilter as event filter.
463
464 Returns \c true if the filter modification request has been successfully dispatched to the backend.
465
466 \l monitoringStatusChanged for \l {QOpcUa::NodeAttribute} {EventNotifier} is emitted after the operation has finished.
467*/
468bool QOpcUaNode::modifyEventFilter(const QOpcUaMonitoringParameters::EventFilter &eventFilter)
469{
470 return modifyMonitoring(attr: QOpcUa::NodeAttribute::EventNotifier, item: QOpcUaMonitoringParameters::Parameter::Filter, value: QVariant::fromValue(value: eventFilter));
471}
472
473/*!
474 Modifies an existing data change monitoring to use \a filter as data change filter.
475
476 Returns \c true if the filter modification request has been successfully dispatched to the backend.
477
478 \l monitoringStatusChanged for \a attr is emitted after the operation has finished.
479*/
480bool QOpcUaNode::modifyDataChangeFilter(QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::DataChangeFilter &filter)
481{
482 return modifyMonitoring(attr, item: QOpcUaMonitoringParameters::Parameter::Filter, value: QVariant::fromValue(value: filter));
483}
484
485/*!
486 Writes \a value to the attribute given in \a attribute using the type information from \a type.
487 Returns \c true if the asynchronous call has been successfully dispatched.
488
489 If the \a type parameter is omitted, the backend tries to find the correct type. The following default types are assumed:
490 \table
491 \header
492 \li Qt MetaType
493 \li OPC UA type
494 \row
495 \li Bool
496 \li Boolean
497 \row
498 \li UChar
499 \li Byte
500 \row
501 \li Char
502 \li SByte
503 \row
504 \li UShort
505 \li UInt16
506 \row
507 \li Short
508 \li Int16
509 \row
510 \li Int
511 \li Int32
512 \row
513 \li UInt
514 \li UInt32
515 \row
516 \li ULongLong
517 \li UInt64
518 \row
519 \li LongLong
520 \li Int64
521 \row
522 \li Double
523 \li Double
524 \row
525 \li Float
526 \li Float
527 \row
528 \li QString
529 \li String
530 \row
531 \li QDateTime
532 \li DateTime
533 \row
534 \li QByteArray
535 \li ByteString
536 \row
537 \li QUuid
538 \li Guid
539 \endtable
540*/
541
542bool QOpcUaNode::writeAttribute(QOpcUa::NodeAttribute attribute, const QVariant &value, QOpcUa::Types type)
543{
544 Q_D(QOpcUaNode);
545 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
546 return false;
547
548 return d->m_impl->writeAttribute(attribute, value, type, indexRange: QString());
549}
550
551/*!
552 Writes \a value to the attribute given in \a attribute using the type information from \a type.
553 For \a indexRange, see \l readAttributeRange().
554
555 Returns \c true if the asynchronous call has been successfully dispatched.
556*/
557bool QOpcUaNode::writeAttributeRange(QOpcUa::NodeAttribute attribute, const QVariant &value, const QString &indexRange, QOpcUa::Types type)
558{
559 Q_D(QOpcUaNode);
560 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
561 return false;
562
563 return d->m_impl->writeAttribute(attribute, value, type, indexRange);
564}
565
566/*!
567 Executes a write operation for the attributes and values specified in \a toWrite.
568
569 Returns \c true if the asynchronous call has been successfully dispatched.
570
571 The \a valueAttributeType parameter can be used to supply type information for the value attribute.
572 All other attributes have known types.
573 \sa writeAttribute()
574*/
575bool QOpcUaNode::writeAttributes(const AttributeMap &toWrite, QOpcUa::Types valueAttributeType)
576{
577 Q_D(QOpcUaNode);
578 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
579 return false;
580
581 return d->m_impl->writeAttributes(toWrite, valueAttributeType);
582}
583
584/*!
585 Writes \a value to the node's Value attribute using the type information from \a type.
586
587 Returns \c true if the asynchronous call has been successfully dispatched.
588
589 \sa writeAttribute()
590*/
591bool QOpcUaNode::writeValueAttribute(const QVariant &value, QOpcUa::Types type)
592{
593 return writeAttribute(attribute: QOpcUa::NodeAttribute::Value, value, type);
594}
595
596/*!
597 This method disables monitoring for the attributes given in \a attr.
598
599 Returns \c true if the asynchronous call has been successfully dispatched.
600
601 After the call is finished, the \l disableMonitoringFinished signal is emitted and monitoringStatus returns a default constructed value with
602 status code BadMonitoredItemIdIinvalid for \a attr.
603*/
604bool QOpcUaNode::disableMonitoring(QOpcUa::NodeAttributes attr)
605{
606 Q_D(QOpcUaNode);
607 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
608 return false;
609
610 return d->m_impl->disableMonitoring(attr);
611}
612
613/*!
614 Executes a forward browse call starting from the node this method is called on.
615 The browse operation collects information about child nodes connected to the node
616 and delivers the results in the \l browseFinished() signal.
617
618 Returns \c true if the asynchronous call has been successfully dispatched.
619
620 To request only children connected to the node by a certain type of reference, \a referenceType must be set to that reference type.
621 For example, this can be used to get all properties of a node by passing \l {QOpcUa::ReferenceTypeId} {HasProperty} in \a referenceType.
622 The results can be filtered to contain only nodes with certain node classes by setting them in \a nodeClassMask.
623*/
624bool QOpcUaNode::browseChildren(QOpcUa::ReferenceTypeId referenceType, QOpcUa::NodeClasses nodeClassMask)
625{
626 Q_D(QOpcUaNode);
627 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
628 return false;
629
630 QOpcUaBrowseRequest request;
631 request.setReferenceTypeId(referenceType);
632 request.setNodeClassMask(nodeClassMask);
633 request.setBrowseDirection(QOpcUaBrowseRequest::BrowseDirection::Forward);
634 request.setIncludeSubtypes(true);
635 return d->m_impl->browse(request);
636}
637
638/*!
639 Returns the ID of the OPC UA node.
640*/
641QString QOpcUaNode::nodeId() const
642{
643 Q_D(const QOpcUaNode);
644 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
645 return QString();
646
647 return d->m_impl->nodeId();
648}
649
650/*!
651 Returns a pointer to the client that has created this node.
652*/
653QOpcUaClient *QOpcUaNode::client() const
654{
655 Q_D(const QOpcUaNode);
656 return d->m_client.data();
657}
658
659/*!
660 Calls the OPC UA method \a methodNodeId with the parameters given via \a args. The result is
661 returned in the \l methodCallFinished signal.
662
663 Returns \c true if the asynchronous call has been successfully dispatched.
664*/
665bool QOpcUaNode::callMethod(const QString &methodNodeId, const QList<QOpcUa::TypedVariant> &args)
666{
667 Q_D(QOpcUaNode);
668 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
669 return false;
670
671 return d->m_impl->callMethod(methodNodeId, args);
672}
673
674/*!
675 Resolves the browse path \a path to one or more node ids starting from this node
676 using the TranslateBrowsePathsToNodeIds service specified in OPC-UA part 4, 5.8.4.
677
678 Returns \c true if the asynchronous call has been successfully dispatched.
679
680 TranslateBrowsePathsToNodeIds is mainly used to program against type definitions instead of a concrete set of
681 nodes in the OPC UA address space.
682 For example, a type definition for a machine model could consist of a starting node with browse name "Machine"
683 which has a component with browse name "Fan". Fan has a component with browse name "RPM" which is a Variable node
684 holding the current RPM value of the fan. There are multiple machines of that type and each of these machines is
685 mapped into the OPC UA address space as an object of the machine type.
686 For each of these machine objects, the path from the machine node to the "RPM" node is the same. If a client wants
687 to read the current RPM value, it needs to call \l resolveBrowsePath() with the machine node as starting node
688 and the browse path from the machine to the "RPM" node:
689
690 \code
691 QScopedPointer<QOpcUaNode> node(opcuaClient->node("ns=1;s=machine1"));
692
693 QList<QOpcUaRelativePathElement> path;
694 path.append(QOpcUaRelativePathElement(QOpcUaQualifiedName(1, "Fan"), QOpcUa::ReferenceTypeId::HasComponent));
695 path.append(QOpcUaRelativePathElement(QOpcUaQualifiedName(1, "RPM"), QOpcUa::ReferenceTypeId::HasComponent));
696 node->resolveBrowsePath(path);
697 \endcode
698
699 The result returned in \l resolveBrowsePathFinished() contains the node id of the "RPM" node which can be
700 used to access the node's attributes:
701
702 \code
703 if (!results.size()) {
704 qWarning() << "Browse path resolution failed";
705 return;
706 }
707
708 if (results.at(0).isFullyResolved()) {
709 QOpcUaNode *rpmNode = client->node(results.at(0).targetId());
710 if (!rpmNode) {
711 qWarning() << "Failed to create node";
712 return;
713 }
714 // Connect slots, call methods
715 } else {
716 qWarning() << "Browse path could not be fully resolved, the target node is on another server";
717 return;
718 }
719 \endcode
720*/
721bool QOpcUaNode::resolveBrowsePath(const QList<QOpcUaRelativePathElement> &path)
722{
723 Q_D(QOpcUaNode);
724 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
725 return 0;
726
727 return d->m_impl->resolveBrowsePath(path);
728}
729
730/*!
731 Starts a browse call from this node.
732
733 Returns \c true if the asynchronous call has been successfully dispatched.
734
735 All references matching the criteria specified in \a request are returned in the \l browseFinished() signal.
736
737 For example, an inverse browse call can be used to find the parent node of a property node:
738 \code
739 QOpcUaBrowseRequest request;
740 request.setBrowseDirection(QOpcUaBrowseRequest::BrowseDirection::Inverse);
741 request.setReferenceTypeId(QOpcUa::ReferenceTypeId::HasProperty);
742 propertyNode->browse(request);
743 \endcode
744*/
745bool QOpcUaNode::browse(const QOpcUaBrowseRequest &request)
746{
747 Q_D(QOpcUaNode);
748 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
749 return false;
750
751 return d->m_impl->browse(request);
752}
753
754/*!
755 Starts a read history request for this node. This is the Qt OPC UA representation for the OPC UA
756 ReadHistory service for reading raw historical data defined in
757 \l {https://reference.opcfoundation.org/v104/Core/docs/Part4/5.10.3/} {OPC-UA part 4, 5.10.3}.
758 It reads the history based on the parementers, \a startTime, \a endTime,
759 \a numValues, and \a returnBounds.
760
761 Returns a \l QOpcUaHistoryReadResponse which contains the state of the request if the asynchronous
762 request has been successfully dispatched. The results are returned in the
763 \l QOpcUaHistoryReadResponse::readHistoryDataFinished(const QList<QOpcUaHistoryData> &results, QOpcUa::UaStatusCode serviceResult)
764 signal.
765
766 In the following example, the historic data from the last two days of a node are requested and printed.
767 The result is limited to ten values per node.
768
769 \code
770 QOpcUaHistoryReadResponse *response = node->readHistoryRaw(QDateTime::currentDateTime(),
771 QDateTime::currentDateTime().addDays(-2),
772 10,
773 true);
774 if (response) {
775 QObject::connect(response123, &QOpcUaHistoryReadResponse::readHistoryDataFinished,
776 [] (QList<QOpcUaHistoryData> results, QOpcUa::UaStatusCode serviceResult) {
777 if (serviceResult != QOpcUa::UaStatusCode::Good) {
778 qWarning() << "Fetching historical data failed with:" << serviceResult;
779 } else {
780 for (const auto& result : results) {
781 qInfo() << "NodeId:" << result.nodeId();
782 for (const auto &dataValue : result.result())
783 qInfo() << "Value:" << dataValue.value();
784 }
785 }
786 });
787 }
788 \endcode
789
790 \since 6.3
791*/
792QOpcUaHistoryReadResponse *QOpcUaNode::readHistoryRaw(const QDateTime &startTime, const QDateTime &endTime, quint32 numValues, bool returnBounds)
793{
794 Q_D(QOpcUaNode);
795 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
796 return nullptr;
797
798 return d->m_impl->readHistoryRaw(startTime, endTime, numValues, returnBounds);
799}
800
801QDebug operator<<(QDebug dbg, const QOpcUaNode &node)
802{
803 dbg << "QOpcUaNode {"
804 << "DisplayName:" << node.attribute(attribute: QOpcUa::NodeAttribute::DisplayName)
805 << "Id:" << node.attribute(attribute: QOpcUa::NodeAttribute::NodeId)
806 << "Class:" << node.attribute(attribute: QOpcUa::NodeAttribute::NodeClass)
807 << "}";
808 return dbg;
809}
810
811QT_END_NAMESPACE
812

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