1// Copyright (C) 2018 The Qt Company Ltd.
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 <private/opcuavaluenode_p.h>
5#include <private/opcuaconnection_p.h>
6#include <private/opcuanodeid_p.h>
7#include <private/opcuaattributevalue_p.h>
8
9#include <QLoggingCategory>
10#include <QMetaEnum>
11
12QT_BEGIN_NAMESPACE
13
14/*!
15 \qmltype ValueNode
16 \inqmlmodule QtOpcUa
17 \brief Represents a value node from a server.
18 \inherits Node
19 \since QtOpcUa 5.12
20 \deprecated [6.9]
21
22 \code
23 import QtOpcUa as QtOpcUa
24
25 QtOpcUa.ValueNode {
26 nodeId: QtOpcUa.NodeId {
27 ns: "Test Namespace"
28 identifier: "s=TestName"
29 }
30 connection: myConnection
31 }
32 \endcode
33
34 A subscription will be created on the server in order to track value changes on the server.
35
36 \sa NodeId, Connection, Node
37*/
38
39/*!
40 \qmlproperty variant ValueNode::value
41
42 Value of this node.
43 Reading and writing this property will access the node on the server.
44*/
45
46/*!
47 \qmlproperty variant ValueNode::valueType
48
49 Type type of this node.
50 The initial value will be \l {QOpcUa::Undefined}{QtOpcUa.Constants.Undefined} and be fetched from the server when the first connection is established.
51 Any value will be written to the server as the specified type.
52*/
53
54/*!
55 \qmlproperty Date ValueNode::sourceTimestamp
56 \readonly
57
58 Source timestamp of the value attribute.
59*/
60
61/*!
62 \qmlproperty Date ValueNode::serverTimestamp
63 \readonly
64
65 Server timestamp of the value attribute.
66*/
67
68Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_QML)
69
70OpcUaValueNode::OpcUaValueNode(QObject *parent):
71 OpcUaNode(parent)
72{
73 connect(sender: m_attributeCache.attribute(attribute: QOpcUa::NodeAttribute::Value), signal: &OpcUaAttributeValue::changed, context: this, slot: &OpcUaValueNode::valueChanged);
74 connect(sender: this, signal: &OpcUaValueNode::filterChanged, context: this, slot: &OpcUaValueNode::updateFilters);
75}
76
77OpcUaValueNode::~OpcUaValueNode()
78{
79}
80
81void OpcUaValueNode::setValue(const QVariant &value)
82{
83 if (!m_connection || !m_node)
84 return;
85 m_node->writeAttribute(attribute: QOpcUa::NodeAttribute::Value, value, type: m_valueType);
86}
87
88template<typename T> QString enumToString(T value) {
89 const auto metaEnum = QMetaEnum::fromType<T>();
90 const auto key = metaEnum.valueToKey(static_cast<int>(value));
91 if (key)
92 return QString::fromLatin1(key);
93 else
94 return QString();
95}
96
97void OpcUaValueNode::setupNode(const QString &absolutePath)
98{
99 // Additionally read the value attribute
100 setAttributesToRead(attributesToRead()
101 | QOpcUa::NodeAttribute::Value
102 | QOpcUa::NodeAttribute::DataType);
103
104 OpcUaNode::setupNode(absolutePath);
105 if (!m_node)
106 return;
107
108 connect(sender: m_node, signal: &QOpcUaNode::attributeWritten, context: this, slot: [this](QOpcUa::NodeAttribute attribute, QOpcUa::UaStatusCode statusCode) {
109 if (statusCode != QOpcUa::Good) {
110 QString msg = QStringLiteral("Failed to write attribute ")
111 + enumToString(value: attribute)
112 + QStringLiteral(": ")
113 + enumToString(value: statusCode);
114 setStatus(status: Status::FailedToWriteAttribute, message: msg);
115 qCWarning(QT_OPCUA_PLUGINS_QML) << msg;
116 }
117 });
118
119
120 connect(sender: m_node, signal: &QOpcUaNode::attributeUpdated, context: this, slot: [this](QOpcUa::NodeAttribute attr, QVariant value) {
121 if (attr == QOpcUa::NodeAttribute::DataType && m_valueType == QOpcUa::Types::Undefined) {
122 const auto valueType = QOpcUa::opcUaDataTypeToQOpcUaType(type: value.toString());
123 m_valueType = valueType;
124 }
125 });
126
127 connect(sender: m_node, signal: &QOpcUaNode::enableMonitoringFinished, context: this, slot: [this](QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode){
128 if (attr != QOpcUa::NodeAttribute::Value)
129 return;
130 if (statusCode == QOpcUa::Good) {
131 m_monitoredState = true;
132 emit monitoredChanged(monitored: m_monitoredState);
133 qCDebug(QT_OPCUA_PLUGINS_QML) << "Monitoring was enabled for node" << resolvedNode().fullNodeId();
134 updateFilters();
135 } else {
136 qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to enable monitoring for node" << resolvedNode().fullNodeId();
137 setStatus(status: Status::FailedToSetupMonitoring);
138 }
139 });
140 connect(sender: m_node, signal: &QOpcUaNode::disableMonitoringFinished, context: this, slot: [this](QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode){
141 if (attr != QOpcUa::NodeAttribute::Value)
142 return;
143 // The monitoring is gone in this case, regardless of the status code
144 if (statusCode == QOpcUa::Good ||
145 node()->monitoringStatus(attr: QOpcUa::NodeAttribute::Value).statusCode()== QOpcUa::UaStatusCode::BadNoEntryExists) {
146 m_monitoredState = false;
147 emit monitoredChanged(monitored: m_monitoredState);
148 qCDebug(QT_OPCUA_PLUGINS_QML) << "Monitoring was disabled for node "<< resolvedNode().fullNodeId();
149 } else {
150 qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to disable monitoring for node "<< resolvedNode().fullNodeId();
151 setStatus(status: Status::FailedToDisableMonitoring);
152 }
153 });
154 connect(sender: m_node, signal: &QOpcUaNode::monitoringStatusChanged, context: this, slot: [this](QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items,
155 QOpcUa::UaStatusCode statusCode) {
156 if (attr != QOpcUa::NodeAttribute::Value && attr != QOpcUa::NodeAttribute::EventNotifier)
157 return;
158 if (statusCode != QOpcUa::Good) {
159 setStatus(status: Status::FailedToModifyMonitoring);
160 qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to modify monitoring";
161 } else {
162 if (items & QOpcUaMonitoringParameters::Parameter::PublishingInterval) {
163 if (m_publishingInterval != m_node->monitoringStatus(attr: QOpcUa::NodeAttribute::Value).publishingInterval()) {
164 m_publishingInterval = m_node->monitoringStatus(attr: QOpcUa::NodeAttribute::Value).publishingInterval();
165 emit publishingIntervalChanged(publishingInterval: m_publishingInterval);
166 }
167 }
168 }
169 });
170
171 updateSubscription();
172}
173
174void OpcUaValueNode::updateFilters() const
175{
176 if (!m_connection || !m_node || !m_filter || !m_monitoredState)
177 return;
178
179 m_node->modifyDataChangeFilter(attr: QOpcUa::NodeAttribute::Value, filter: m_filter->filter());
180}
181
182bool OpcUaValueNode::checkValidity()
183{
184 if (!m_connection || !m_node)
185 return false;
186
187 if (m_node->attribute(attribute: QOpcUa::NodeAttribute::NodeClass).value<QOpcUa::NodeClass>() != QOpcUa::NodeClass::Variable) {
188 setStatus(status: Status::InvalidNodeType);
189 return false;
190 }
191
192 setStatus(status: Status::Valid);
193 return true;
194}
195
196QVariant OpcUaValueNode::value() const
197{
198 if (!m_connection || !m_node)
199 return QVariant();
200 return m_node->attribute(attribute: QOpcUa::NodeAttribute::Value);
201}
202
203QDateTime OpcUaValueNode::serverTimestamp() const
204{
205 return getServerTimestamp(QOpcUa::NodeAttribute::Value);
206}
207
208QDateTime OpcUaValueNode::sourceTimestamp() const
209{
210 return getSourceTimestamp(QOpcUa::NodeAttribute::Value);
211}
212
213bool OpcUaValueNode::monitored() const
214{
215 if (!m_connection || !m_node)
216 return m_monitored;
217
218 return m_monitoredState;
219}
220
221void OpcUaValueNode::updateSubscription()
222{
223 if (!m_connection || !m_node)
224 return;
225
226 QOpcUaMonitoringParameters parameters;
227 parameters.setPublishingInterval(m_publishingInterval);
228
229 if (m_filter)
230 parameters.setFilter(m_filter->filter());
231
232 if (m_monitoredState != m_monitored) {
233 if (m_monitored) {
234 m_node->enableMonitoring(attr: QOpcUa::NodeAttribute::Value, settings: parameters);
235 } else {
236 m_node->disableMonitoring(attr: QOpcUa::NodeAttribute::Value);
237 }
238 }
239}
240void OpcUaValueNode::setMonitored(bool monitored)
241{
242 m_monitored = monitored;
243 updateSubscription();
244}
245
246double OpcUaValueNode::publishingInterval() const
247{
248 if (!m_connection || !m_node)
249 return 0.0;
250
251 auto monitoringStatus = node()->monitoringStatus(attr: QOpcUa::NodeAttribute::Value);
252 if (monitoringStatus.statusCode() == QOpcUa::BadNoEntryExists)
253 return 0.0;
254 return monitoringStatus.publishingInterval();
255}
256
257OpcUaDataChangeFilter *OpcUaValueNode::filter() const
258{
259 return m_filter;
260}
261
262void OpcUaValueNode::setFilter(OpcUaDataChangeFilter *filter)
263{
264 bool changed = false;
265
266 if (m_filter) {
267 disconnect(sender: m_filter, signal: &OpcUaDataChangeFilter::filterChanged, receiver: this, slot: &OpcUaValueNode::updateFilters);
268 changed = !(*m_filter == *filter);
269 } else {
270 changed = true;
271 }
272
273 m_filter = filter;
274 connect(sender: m_filter, signal: &OpcUaDataChangeFilter::filterChanged, context: this, slot: &OpcUaValueNode::updateFilters);
275
276 if (changed)
277 emit filterChanged();
278}
279
280void OpcUaValueNode::setPublishingInterval(double publishingInterval)
281{
282 if (!m_connection || !m_node)
283 return;
284 if (qFuzzyCompare(p1: m_publishingInterval, p2: publishingInterval))
285 return;
286
287 m_node->modifyMonitoring(attr: QOpcUa::NodeAttribute::Value, item: QOpcUaMonitoringParameters::Parameter::PublishingInterval, value: publishingInterval);
288}
289
290QOpcUa::Types OpcUaValueNode::valueType() const
291{
292 return m_valueType;
293}
294
295void OpcUaValueNode::setValueType(QOpcUa::Types valueType)
296{
297 m_valueType = valueType;
298}
299
300QT_END_NAMESPACE
301

source code of qtopcua/src/declarative_opcua/opcuavaluenode.cpp