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() valueAttributeUpdated()
154*/
155
156/*!
157 \fn void QOpcUaNode::valueAttributeUpdated(const QVariant &value)
158 \since 6.7
159
160 This signal is emitted after the value attribute in the attribute cache has been updated by a
161 data change notification from the server, a read or a write operation. \a value contains the
162 new value for the value attribute.
163
164 \sa attribute() attributeError() serverTimestamp() sourceTimestamp() attributeUpdated()
165*/
166
167/*!
168 \fn void QOpcUaNode::enableMonitoringFinished(QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode)
169
170 This signal is emitted after an asynchronous call to \l enableMonitoring() has finished.
171 After this signal has been emitted, \l monitoringStatus() returns valid information for \a attr.
172 \a statusCode contains the status code for the operation.
173*/
174
175/*!
176 \fn void QOpcUaNode::disableMonitoringFinished(QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode)
177
178 This signal is emitted after an asynchronous call to \l disableMonitoring() has finished. \a statusCode contains
179 the status code generated by the operation.
180 After this signal has been emitted, monitoringStatus returns a default constructed value with
181 status code BadMonitoredItemIdIinvalid for \a attr.
182*/
183
184/*!
185 \fn void QOpcUaNode::monitoringStatusChanged(QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items, QOpcUa::UaStatusCode statusCode);
186
187 This signal is emitted after an asynchronous call to \l modifyMonitoring() has finished.
188 The node attribute for which the operation was requested is returned in \a attr. \a items contains the parameters that have been modified.
189 \a statusCode contains the result of the modify operation on the server.
190*/
191
192/*!
193 \fn void QOpcUaNode::methodCallFinished(QString methodNodeId, QVariant result, QOpcUa::UaStatusCode statusCode)
194
195 This signal is emitted after a method call for \a methodNodeId has finished on the server.
196 \a statusCode contains the status code from the method call, \a result contains the output
197 arguments of the method. \a result is empty if the method has no output arguments or \a statusCode
198 is not \l {QOpcUa::UaStatusCode} {Good}.
199 The \a result variant is either a single value if there is only one output argument or it contains a list of
200 variants in case the called function returned multiple output arguments.
201 \code
202 if (result.canConvert<QVariantList>()) {
203 // handle list type
204 } else {
205 // handle value type
206 }
207 \endcode
208*/
209
210/*!
211 \fn void QOpcUaNode::browseFinished(QList<QOpcUaReferenceDescription> children, QOpcUa::UaStatusCode statusCode)
212
213 This signal is emitted after a \l browseChildren() or \l browse() operation has finished.
214
215 \a children contains information about all nodes which matched the criteria in \l browseChildren().
216 \a statusCode contains the service result of the browse operation. If \a statusCode is not \l {QOpcUa::UaStatusCode} {Good},
217 the passed \a children vector is empty.
218 \sa QOpcUaReferenceDescription
219*/
220
221/*!
222 \fn void QOpcUaNode::resolveBrowsePathFinished(QList<QOpcUaBrowsePathTarget> targets, QList<QOpcUaRelativePathElement> path, QOpcUa::UaStatusCode statusCode)
223
224 This signal is emitted after a \l resolveBrowsePath() call has finished.
225
226 \l QOpcUaBrowsePathTarget \a targets contains the matches, \a statusCode is the status code of the operation.
227 If \a statusCode is not \l {QOpcUa::UaStatusCode} {Good}, \a targets is empty.
228 The browse path \a path is the browse path from the request. It can be used to associate results with requests.
229*/
230
231/*!
232 \fn void QOpcUaNode::eventOccurred(QVariantList eventFields)
233
234 This signal is emitted after a new event has been received.
235
236 \a eventFields contains the values of the event fields in the order specified in the \c select clause of the event filter.
237*/
238
239/*!
240 \fn QOpcUa::NodeAttributes QOpcUaNode::mandatoryBaseAttributes()
241
242 Contains all mandatory attributes of the OPC UA base node class.
243*/
244
245/*!
246 \fn QOpcUa::NodeAttributes QOpcUaNode::allBaseAttributes()
247
248 Contains all attributes of the OPC UA base node class.
249*/
250
251/*!
252 \internal QOpcUaNodeImpl is an opaque type (as seen from the public API).
253 This prevents users of the public API to use this constructor (even though
254 it is public).
255*/
256QOpcUaNode::QOpcUaNode(QOpcUaNodeImpl *impl, QOpcUaClient *client, QObject *parent)
257 : QObject(*new QOpcUaNodePrivate(impl, client), parent)
258{
259 d_func()->createConnections();
260}
261
262QOpcUaNode::~QOpcUaNode()
263{
264}
265
266/*!
267 Starts an asynchronous read operation for the node attribute \a attribute.
268 \a indexRange is a string which can be used to select a part of an array. It is defined in OPC UA 1.05 part 4, 7.27.
269 The first element in an array is 0, "1" returns the second element, "0:9" returns the first 10 elements,
270 "0,1" returns the second element of the first row in a two-dimensional array.
271
272 Returns \c true if the asynchronous call has been successfully dispatched.
273
274 Attribute values only contain valid information after the \l attributeRead signal has been emitted.
275*/
276bool QOpcUaNode::readAttributeRange(QOpcUa::NodeAttribute attribute, const QString &indexRange)
277{
278 Q_D(QOpcUaNode);
279 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
280 return false;
281
282 return d->m_impl->readAttributes(attr: QOpcUa::NodeAttributes() | attribute, indexRange);
283}
284
285/*!
286 Starts an asynchronous read operation for the node's Value attribute.
287
288 Returns \c true if the asynchronous call has been successfully dispatched.
289
290 \sa readAttributes()
291*/
292bool QOpcUaNode::readValueAttribute()
293{
294 return readAttributes(attributes: QOpcUa::NodeAttribute::Value);
295}
296
297/*!
298 Starts an asynchronous read operation for the node attributes in \a attributes.
299
300 Returns \c true if the asynchronous call has been successfully dispatched.
301
302 Attribute values only contain valid information after the \l attributeRead signal has been emitted.
303*/
304bool QOpcUaNode::readAttributes(QOpcUa::NodeAttributes attributes)
305{
306 Q_D(QOpcUaNode);
307 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
308 return false;
309
310 return d->m_impl->readAttributes(attr: attributes, indexRange: QString());
311}
312
313/*!
314 Returns the value of the attribute given in \a attribute.
315
316 The value is only valid after the \l attributeRead signal has been emitted.
317 An empty QVariant is returned if there is no cached value for the attribute.
318*/
319QVariant QOpcUaNode::attribute(QOpcUa::NodeAttribute attribute) const
320{
321 Q_D(const QOpcUaNode);
322 auto it = d->m_nodeAttributes.constFind(key: attribute);
323 if (it == d->m_nodeAttributes.constEnd())
324 return QVariant();
325
326 return it->value();
327}
328
329/*!
330 Returns the value of the node's Value attribute.
331
332 The returned value is only valid after the Value attribute has been successfully read or written
333 or after a data change from a monitoring has updated the attribute cache.
334 This is indicated by a \l attributeRead() or \l attributeWritten() signal with status code
335 \l {QOpcUa::UaStatusCode} {Good} or a \l dataChangeOccurred() signal for the Value attribute.
336
337 If there is no value in the attribute cache, an invalid \l QVariant is returned.
338
339 \sa readValueAttribute() writeValueAttribute() valueAttributeError()
340*/
341QVariant QOpcUaNode::valueAttribute() const
342{
343 return attribute(attribute: QOpcUa::NodeAttribute::Value);
344}
345
346/*!
347 Returns the error code for the attribute given in \a attribute.
348
349 The error code is only valid after the \l attributeRead or \l attributeWritten signal has been emitted.
350
351 If there is no entry in the attribute cache, \l {QOpcUa::UaStatusCode} {BadNoEntryExists} is returned.
352
353 \sa QOpcUa::errorCategory
354 */
355QOpcUa::UaStatusCode QOpcUaNode::attributeError(QOpcUa::NodeAttribute attribute) const
356{
357 Q_D(const QOpcUaNode);
358 auto it = d->m_nodeAttributes.constFind(key: attribute);
359 if (it == d->m_nodeAttributes.constEnd())
360 return QOpcUa::UaStatusCode::BadNoEntryExists;
361
362 return it->statusCode();
363}
364
365/*!
366 Returns the error code for the node's Value attribute.
367 The status code \l {QOpcUa::UaStatusCode} {Good} indicates a valid return value for \l valueAttribute().
368 If there is no entry in the attribute cache, \l {QOpcUa::UaStatusCode} {BadNoEntryExists} is returned.
369*/
370QOpcUa::UaStatusCode QOpcUaNode::valueAttributeError() const
371{
372 return attributeError(attribute: QOpcUa::NodeAttribute::Value);
373}
374
375/*!
376 Returns the source timestamp from the last read or data change of \a attribute.
377 Before at least one \l attributeRead or \l dataChangeOccurred signal has been emitted,
378 a null datetime is returned.
379
380*/
381QDateTime QOpcUaNode::sourceTimestamp(QOpcUa::NodeAttribute attribute) const
382{
383 Q_D(const QOpcUaNode);
384 auto it = d->m_nodeAttributes.constFind(key: attribute);
385 if (it == d->m_nodeAttributes.constEnd())
386 return QDateTime();
387
388 return it->sourceTimestamp();
389}
390
391/*!
392 Returns the server timestamp from the last read or data change of \a attribute.
393 Before at least one \l attributeRead or \l dataChangeOccurred signal has been emitted,
394 a null datetime is returned.
395*/
396QDateTime QOpcUaNode::serverTimestamp(QOpcUa::NodeAttribute attribute) const
397{
398 Q_D(const QOpcUaNode);
399 auto it = d->m_nodeAttributes.constFind(key: attribute);
400 if (it == d->m_nodeAttributes.constEnd())
401 return QDateTime();
402
403 return it->serverTimestamp();
404}
405
406/*!
407 This method creates a monitored item for each of the attributes given in \a attr.
408 The settings from \a settings are used in the creation of the monitored items and the subscription.
409
410 Returns \c true if the asynchronous call has been successfully dispatched.
411
412 On completion of the call, the \l enableMonitoringFinished signal is emitted.
413 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,
414 the node does not exist on the server, the node does not have the requested attribute or the maximum number of monitored items for
415 the server is reached.
416
417 The same method is used to enable event monitoring. Events are special objects in the OPC UA address space which contain information
418 about an event that has occurred. If an event is triggered on the server, an event monitored item collects selected values of
419 node attributes of the event object and its child nodes.
420 Every node that has an event source can be monitored for events.
421 To monitor a node for events, the attribute \l {QOpcUa::NodeAttribute} {EventNotifier} must be monitored using an EventFilter which contains the event fields
422 the user needs and optionally a where clause which is used to filter events by criteria (for more details, see \l QOpcUaMonitoringParameters::EventFilter).
423
424 */
425bool QOpcUaNode::enableMonitoring(QOpcUa::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
426{
427 Q_D(QOpcUaNode);
428 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
429 return false;
430
431 return d->m_impl->enableMonitoring(attr, settings);
432}
433
434/*!
435 This method modifies settings of the monitored item or the subscription.
436 The parameter \a item of the monitored item or subscription associated with \a attr is attempted to set to \a value.
437
438 Returns \c true if the asynchronous call has been successfully dispatched.
439
440 After the call has finished, the \l monitoringStatusChanged signal is emitted. This signal contains the modified parameters and the status code.
441 A bad status code is generated if there is no monitored item associated with the requested attribute, modifying the requested
442 parameter is not implemented or if the server has rejected the requested value.
443*/
444bool QOpcUaNode::modifyMonitoring(QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value)
445{
446 Q_D(QOpcUaNode);
447 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
448 return false;
449
450 return d->m_impl->modifyMonitoring(attr, item, value);
451}
452
453/*!
454 Returns the monitoring parameters associated with the attribute \a attr. This can be used to check the success of \l enableMonitoring()
455 or if parameters have been revised.
456 The returned values are only valid after \l enableMonitoringFinished or \l monitoringStatusChanged have been emitted
457 for \a attr. If the status is queried before a signal has been emitted, \l QOpcUaMonitoringParameters::statusCode()
458 returns \l {QOpcUa::UaStatusCode} {BadNoEntryExists}.
459*/
460QOpcUaMonitoringParameters QOpcUaNode::monitoringStatus(QOpcUa::NodeAttribute attr)
461{
462 Q_D(QOpcUaNode);
463 auto it = d->m_monitoringStatus.constFind(key: attr);
464 if (it == d->m_monitoringStatus.constEnd()) {
465 QOpcUaMonitoringParameters p;
466 p.setStatusCode(QOpcUa::UaStatusCode::BadNoEntryExists);
467 return p;
468 }
469
470 return *it;
471}
472
473/*!
474 Modifies an existing event monitoring to use \a eventFilter as event filter.
475
476 Returns \c true if the filter modification request has been successfully dispatched to the backend.
477
478 \l monitoringStatusChanged for \l {QOpcUa::NodeAttribute} {EventNotifier} is emitted after the operation has finished.
479*/
480bool QOpcUaNode::modifyEventFilter(const QOpcUaMonitoringParameters::EventFilter &eventFilter)
481{
482 return modifyMonitoring(attr: QOpcUa::NodeAttribute::EventNotifier, item: QOpcUaMonitoringParameters::Parameter::Filter, value: QVariant::fromValue(value: eventFilter));
483}
484
485/*!
486 Modifies an existing data change monitoring to use \a filter as data change filter.
487
488 Returns \c true if the filter modification request has been successfully dispatched to the backend.
489
490 \l monitoringStatusChanged for \a attr is emitted after the operation has finished.
491*/
492bool QOpcUaNode::modifyDataChangeFilter(QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::DataChangeFilter &filter)
493{
494 return modifyMonitoring(attr, item: QOpcUaMonitoringParameters::Parameter::Filter, value: QVariant::fromValue(value: filter));
495}
496
497/*!
498 Writes \a value to the attribute given in \a attribute using the type information from \a type.
499 Returns \c true if the asynchronous call has been successfully dispatched.
500
501 If the \a type parameter is omitted, the backend tries to find the correct type. The following default types are assumed:
502 \table
503 \header
504 \li Qt MetaType
505 \li OPC UA type
506 \row
507 \li Bool
508 \li Boolean
509 \row
510 \li UChar
511 \li Byte
512 \row
513 \li Char
514 \li SByte
515 \row
516 \li UShort
517 \li UInt16
518 \row
519 \li Short
520 \li Int16
521 \row
522 \li Int
523 \li Int32
524 \row
525 \li UInt
526 \li UInt32
527 \row
528 \li ULongLong
529 \li UInt64
530 \row
531 \li LongLong
532 \li Int64
533 \row
534 \li Double
535 \li Double
536 \row
537 \li Float
538 \li Float
539 \row
540 \li QString
541 \li String
542 \row
543 \li QDateTime
544 \li DateTime
545 \row
546 \li QByteArray
547 \li ByteString
548 \row
549 \li QUuid
550 \li Guid
551 \endtable
552*/
553
554bool QOpcUaNode::writeAttribute(QOpcUa::NodeAttribute attribute, const QVariant &value, QOpcUa::Types type)
555{
556 Q_D(QOpcUaNode);
557 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
558 return false;
559
560 return d->m_impl->writeAttribute(attribute, value, type, indexRange: QString());
561}
562
563/*!
564 Writes \a value to the attribute given in \a attribute using the type information from \a type.
565 For \a indexRange, see \l readAttributeRange().
566
567 Returns \c true if the asynchronous call has been successfully dispatched.
568*/
569bool QOpcUaNode::writeAttributeRange(QOpcUa::NodeAttribute attribute, const QVariant &value, const QString &indexRange, QOpcUa::Types type)
570{
571 Q_D(QOpcUaNode);
572 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
573 return false;
574
575 return d->m_impl->writeAttribute(attribute, value, type, indexRange);
576}
577
578/*!
579 Executes a write operation for the attributes and values specified in \a toWrite.
580
581 Returns \c true if the asynchronous call has been successfully dispatched.
582
583 The \a valueAttributeType parameter can be used to supply type information for the value attribute.
584 All other attributes have known types.
585 \sa writeAttribute()
586*/
587bool QOpcUaNode::writeAttributes(const AttributeMap &toWrite, QOpcUa::Types valueAttributeType)
588{
589 Q_D(QOpcUaNode);
590 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
591 return false;
592
593 return d->m_impl->writeAttributes(toWrite, valueAttributeType);
594}
595
596/*!
597 Writes \a value to the node's Value attribute using the type information from \a type.
598
599 Returns \c true if the asynchronous call has been successfully dispatched.
600
601 \sa writeAttribute()
602*/
603bool QOpcUaNode::writeValueAttribute(const QVariant &value, QOpcUa::Types type)
604{
605 return writeAttribute(attribute: QOpcUa::NodeAttribute::Value, value, type);
606}
607
608/*!
609 This method disables monitoring for the attributes given in \a attr.
610
611 Returns \c true if the asynchronous call has been successfully dispatched.
612
613 After the call is finished, the \l disableMonitoringFinished signal is emitted and monitoringStatus returns a default constructed value with
614 status code BadMonitoredItemIdIinvalid for \a attr.
615*/
616bool QOpcUaNode::disableMonitoring(QOpcUa::NodeAttributes attr)
617{
618 Q_D(QOpcUaNode);
619 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
620 return false;
621
622 return d->m_impl->disableMonitoring(attr);
623}
624
625/*!
626 Executes a forward browse call starting from the node this method is called on.
627 The browse operation collects information about child nodes connected to the node
628 and delivers the results in the \l browseFinished() signal.
629
630 Returns \c true if the asynchronous call has been successfully dispatched.
631
632 To request only children connected to the node by a certain type of reference, \a referenceType must be set to that reference type.
633 For example, this can be used to get all properties of a node by passing \l {QOpcUa::ReferenceTypeId} {HasProperty} in \a referenceType.
634 The results can be filtered to contain only nodes with certain node classes by setting them in \a nodeClassMask.
635*/
636bool QOpcUaNode::browseChildren(QOpcUa::ReferenceTypeId referenceType, QOpcUa::NodeClasses nodeClassMask)
637{
638 Q_D(QOpcUaNode);
639 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
640 return false;
641
642 QOpcUaBrowseRequest request;
643 request.setReferenceTypeId(referenceType);
644 request.setNodeClassMask(nodeClassMask);
645 request.setBrowseDirection(QOpcUaBrowseRequest::BrowseDirection::Forward);
646 request.setIncludeSubtypes(true);
647 return d->m_impl->browse(request);
648}
649
650/*!
651 Returns the ID of the OPC UA node.
652*/
653QString QOpcUaNode::nodeId() const
654{
655 Q_D(const QOpcUaNode);
656 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
657 return QString();
658
659 return d->m_impl->nodeId();
660}
661
662/*!
663 Returns a pointer to the client that has created this node.
664*/
665QOpcUaClient *QOpcUaNode::client() const
666{
667 Q_D(const QOpcUaNode);
668 return d->m_client.data();
669}
670
671/*!
672 Calls the OPC UA method \a methodNodeId with the parameters given via \a args. The result is
673 returned in the \l methodCallFinished signal.
674
675 Returns \c true if the asynchronous call has been successfully dispatched.
676*/
677bool QOpcUaNode::callMethod(const QString &methodNodeId, const QList<QOpcUa::TypedVariant> &args)
678{
679 Q_D(QOpcUaNode);
680 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
681 return false;
682
683 return d->m_impl->callMethod(methodNodeId, args);
684}
685
686/*!
687 Resolves the browse path \a path to one or more node ids starting from this node
688 using the TranslateBrowsePathsToNodeIds service specified in OPC UA 1.05 part 4, 5.8.4.
689
690 Returns \c true if the asynchronous call has been successfully dispatched.
691
692 TranslateBrowsePathsToNodeIds is mainly used to program against type definitions instead of a concrete set of
693 nodes in the OPC UA address space.
694 For example, a type definition for a machine model could consist of a starting node with browse name "Machine"
695 which has a component with browse name "Fan". Fan has a component with browse name "RPM" which is a Variable node
696 holding the current RPM value of the fan. There are multiple machines of that type and each of these machines is
697 mapped into the OPC UA address space as an object of the machine type.
698 For each of these machine objects, the path from the machine node to the "RPM" node is the same. If a client wants
699 to read the current RPM value, it needs to call \l resolveBrowsePath() with the machine node as starting node
700 and the browse path from the machine to the "RPM" node:
701
702 \code
703 QScopedPointer<QOpcUaNode> node(opcuaClient->node("ns=1;s=machine1"));
704
705 QList<QOpcUaRelativePathElement> path;
706 path.append(QOpcUaRelativePathElement(QOpcUaQualifiedName(1, "Fan"), QOpcUa::ReferenceTypeId::HasComponent));
707 path.append(QOpcUaRelativePathElement(QOpcUaQualifiedName(1, "RPM"), QOpcUa::ReferenceTypeId::HasComponent));
708 node->resolveBrowsePath(path);
709 \endcode
710
711 The result returned in \l resolveBrowsePathFinished() contains the node id of the "RPM" node which can be
712 used to access the node's attributes:
713
714 \code
715 if (!results.size()) {
716 qWarning() << "Browse path resolution failed";
717 return;
718 }
719
720 if (results.at(0).isFullyResolved()) {
721 QOpcUaNode *rpmNode = client->node(results.at(0).targetId());
722 if (!rpmNode) {
723 qWarning() << "Failed to create node";
724 return;
725 }
726 // Connect slots, call methods
727 } else {
728 qWarning() << "Browse path could not be fully resolved, the target node is on another server";
729 return;
730 }
731 \endcode
732*/
733bool QOpcUaNode::resolveBrowsePath(const QList<QOpcUaRelativePathElement> &path)
734{
735 Q_D(QOpcUaNode);
736 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
737 return 0;
738
739 return d->m_impl->resolveBrowsePath(path);
740}
741
742/*!
743 Starts a browse call from this node.
744
745 Returns \c true if the asynchronous call has been successfully dispatched.
746
747 All references matching the criteria specified in \a request are returned in the \l browseFinished() signal.
748
749 For example, an inverse browse call can be used to find the parent node of a property node:
750 \code
751 QOpcUaBrowseRequest request;
752 request.setBrowseDirection(QOpcUaBrowseRequest::BrowseDirection::Inverse);
753 request.setReferenceTypeId(QOpcUa::ReferenceTypeId::HasProperty);
754 propertyNode->browse(request);
755 \endcode
756*/
757bool QOpcUaNode::browse(const QOpcUaBrowseRequest &request)
758{
759 Q_D(QOpcUaNode);
760 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
761 return false;
762
763 return d->m_impl->browse(request);
764}
765
766/*!
767 Starts a read history request for this node. This is the Qt OPC UA representation for the OPC UA
768 ReadHistory service for reading raw historical data defined in
769 \l {https://reference.opcfoundation.org/v105/Core/docs/Part4/5.10.3/} {OPC UA 1.05 part 4, 5.10.3}.
770 It reads the history based on the parementers, \a startTime, \a endTime,
771 \a numValues, and \a returnBounds.
772
773 Returns a \l QOpcUaHistoryReadResponse which contains the state of the request if the asynchronous
774 request has been successfully dispatched. The results are returned in the
775 \l QOpcUaHistoryReadResponse::readHistoryDataFinished(const QList<QOpcUaHistoryData> &results, QOpcUa::UaStatusCode serviceResult)
776 signal.
777
778 In the following example, the historic data from the last two days of a node are requested and printed.
779 The result is limited to ten values per node.
780
781 \code
782 QOpcUaHistoryReadResponse *response = node->readHistoryRaw(QDateTime::currentDateTime(),
783 QDateTime::currentDateTime().addDays(-2),
784 10,
785 true);
786 if (response) {
787 QObject::connect(response123, &QOpcUaHistoryReadResponse::readHistoryDataFinished,
788 [] (QList<QOpcUaHistoryData> results, QOpcUa::UaStatusCode serviceResult) {
789 if (serviceResult != QOpcUa::UaStatusCode::Good) {
790 qWarning() << "Fetching historical data failed with:" << serviceResult;
791 } else {
792 for (const auto& result : results) {
793 qInfo() << "NodeId:" << result.nodeId();
794 for (const auto &dataValue : result.result())
795 qInfo() << "Value:" << dataValue.value();
796 }
797 }
798 });
799 }
800 \endcode
801
802 \since 6.3
803*/
804QOpcUaHistoryReadResponse *QOpcUaNode::readHistoryRaw(const QDateTime &startTime, const QDateTime &endTime, quint32 numValues, bool returnBounds)
805{
806 Q_D(QOpcUaNode);
807 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
808 return nullptr;
809
810 return d->m_impl->readHistoryRaw(startTime, endTime, numValues, returnBounds, timestampsToReturn: QOpcUa::TimestampsToReturn::Both);
811}
812
813/*!
814 Starts a read history request for this node.
815 The additional \a timestampsToReturn parameter determines which timestamps will be
816 returned for each value.
817
818 \since 6.7
819*/
820QOpcUaHistoryReadResponse *QOpcUaNode::readHistoryRaw(const QDateTime &startTime, const QDateTime &endTime, quint32 numValues, bool returnBounds,
821 QOpcUa::TimestampsToReturn timestampsToReturn)
822{
823 Q_D(QOpcUaNode);
824 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
825 return nullptr;
826
827 return d->m_impl->readHistoryRaw(startTime, endTime, numValues, returnBounds, timestampsToReturn);
828}
829
830/*!
831 \since 6.7
832
833 Starts a read event history request for this node using the parameters \a startTime, \a endTime, \a filter and \a numValues.
834 The \a filter is used by the server to determine which events and which set of their fields shall be returned.
835
836 Returns a \l QOpcUaHistoryReadResponse which contains the state of the request if the asynchronous
837 request has been successfully dispatched. The results are returned in the
838 \l QOpcUaHistoryReadResponse::readHistoryEventsFinished(const QList<QOpcUaHistoryEvent> &results, QOpcUa::UaStatusCode serviceResult)
839 signal.
840
841 The following example retrieves historic events for the last two days. Up to 10 events are returned at a time. While there are more
842 events matching the filter and the provided time range, \c hasMoreData() will be true and more events can be fetched via \b readMoreData().
843
844 \code
845 QScopedPointer<QOpcUaNode> node(opcuaClient->node("ns=2;s=EventHistorian"));
846 QVERIFY(node != nullptr);
847
848 QOpcUaMonitoringParameters::EventFilter filter;
849 filter << QOpcUaSimpleAttributeOperand("Message");
850 filter << QOpcUaSimpleAttributeOperand("Time");
851
852 const auto response = node->readHistoryEvents(QDateTime::currentDateTime().addDays(-2), QDateTime::currentDateTime(), filter, 10);
853
854 QObject::connect(response, &QOpcUaHistoryReadResponse::readHistoryEventsFinished, this,
855 [response](const QList<QOpcUaHistoryEvent> &results, QOpcUa::UaStatusCode serviceResult) {
856 if (serviceResult != QOpcUa::UaStatusCode::Good) {
857 qDebug() << "Service call failed with" << serviceResult;
858 return;
859 }
860
861 // Print what we got so far
862 for (const auto &result : response->events()) {
863 qDebug() << "Results for" << result.nodeId() << result.statusCode();
864 for (const auto &event : result.events())
865 qDebug() << " Event:" << event;
866 }
867
868 if (response->hasMoreData())
869 response->readMoreData();
870 });
871 \endcode
872*/
873QOpcUaHistoryReadResponse *QOpcUaNode::readHistoryEvents(const QDateTime &startTime, const QDateTime &endTime, QOpcUaMonitoringParameters::EventFilter &filter, quint32 numValues)
874{
875 Q_D(QOpcUaNode);
876 if (d->m_client.isNull() || d->m_client->state() != QOpcUaClient::Connected)
877 return nullptr;
878
879 return d->m_impl->readHistoryEvents(startTime, endTime, filter, numValues);
880}
881
882QDebug operator<<(QDebug dbg, const QOpcUaNode &node)
883{
884 dbg << "QOpcUaNode {"
885 << "DisplayName:" << node.attribute(attribute: QOpcUa::NodeAttribute::DisplayName)
886 << "Id:" << node.attribute(attribute: QOpcUa::NodeAttribute::NodeId)
887 << "Class:" << node.attribute(attribute: QOpcUa::NodeAttribute::NodeClass)
888 << "}";
889 return dbg;
890}
891
892QT_END_NAMESPACE
893

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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