Revision control
++ // Copyright (C) 2018 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include <private/opcuavaluenode_p.h>
++ #include <private/opcuaconnection_p.h>
++ #include <private/opcuanodeid_p.h>
++ #include <private/opcuaattributevalue_p.h>
++
++ #include <QLoggingCategory>
++ #include <QMetaEnum>
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ \qmltype ValueNode
++ \inqmlmodule QtOpcUa
++ \brief Represents a value node from a server.
++ \inherits Node
++ \since QtOpcUa 5.12
++ \deprecated [6.9]
++
++ \code
++ import QtOpcUa as QtOpcUa
++
++ QtOpcUa.ValueNode {
++ nodeId: QtOpcUa.NodeId {
++ ns: "Test Namespace"
++ identifier: "s=TestName"
++ }
++ connection: myConnection
++ }
++ \endcode
++
++ A subscription will be created on the server in order to track value changes on the server.
++
++ \sa NodeId, Connection, Node
++ */
++
++ /*!
++ \qmlproperty variant ValueNode::value
++
++ Value of this node.
++ Reading and writing this property will access the node on the server.
++ */
++
++ /*!
++ \qmlproperty variant ValueNode::valueType
++
++ Type type of this node.
++ The initial value will be \l {QOpcUa::Undefined}{QtOpcUa.Constants.Undefined} and be fetched from the server when the first connection is established.
++ Any value will be written to the server as the specified type.
++ */
++
++ /*!
++ \qmlproperty Date ValueNode::sourceTimestamp
++ \readonly
++
++ Source timestamp of the value attribute.
++ */
++
++ /*!
++ \qmlproperty Date ValueNode::serverTimestamp
++ \readonly
++
++ Server timestamp of the value attribute.
++ */
++
++ Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_QML)
++
++ OpcUaValueNode::OpcUaValueNode(QObject *parent):
++ OpcUaNode(parent)
++ {
++ connect(m_attributeCache.attribute(QOpcUa::NodeAttribute::Value), &OpcUaAttributeValue::changed, this, &OpcUaValueNode::valueChanged);
++ connect(this, &OpcUaValueNode::filterChanged, this, &OpcUaValueNode::updateFilters);
++ }
++
++ OpcUaValueNode::~OpcUaValueNode()
++ {
++ }
++
++ void OpcUaValueNode::setValue(const QVariant &value)
++ {
++ if (!m_connection || !m_node)
++ return;
++ m_node->writeAttribute(QOpcUa::NodeAttribute::Value, value, m_valueType);
++ }
++
++ template<typename T> QString enumToString(T value) {
++ const auto metaEnum = QMetaEnum::fromType<T>();
++ const auto key = metaEnum.valueToKey(static_cast<int>(value));
++ if (key)
++ return QString::fromLatin1(key);
++ else
++ return QString();
++ }
++
++ void OpcUaValueNode::setupNode(const QString &absolutePath)
++ {
++ // Additionally read the value attribute
++ setAttributesToRead(attributesToRead()
++ | QOpcUa::NodeAttribute::Value
++ | QOpcUa::NodeAttribute::DataType);
++
++ OpcUaNode::setupNode(absolutePath);
++ if (!m_node)
++ return;
++
++ connect(m_node, &QOpcUaNode::attributeWritten, this, [this](QOpcUa::NodeAttribute attribute, QOpcUa::UaStatusCode statusCode) {
++ if (statusCode != QOpcUa::Good) {
++ QString msg = QStringLiteral("Failed to write attribute ")
++ + enumToString(attribute)
++ + QStringLiteral(": ")
++ + enumToString(statusCode);
++ setStatus(Status::FailedToWriteAttribute, msg);
++ qCWarning(QT_OPCUA_PLUGINS_QML) << msg;
++ }
++ });
++
++
++ connect(m_node, &QOpcUaNode::attributeUpdated, this, [this](QOpcUa::NodeAttribute attr, QVariant value) {
++ if (attr == QOpcUa::NodeAttribute::DataType && m_valueType == QOpcUa::Types::Undefined) {
++ const auto valueType = QOpcUa::opcUaDataTypeToQOpcUaType(value.toString());
++ m_valueType = valueType;
++ }
++ });
++
++ connect(m_node, &QOpcUaNode::enableMonitoringFinished, this, [this](QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode){
++ if (attr != QOpcUa::NodeAttribute::Value)
++ return;
++ if (statusCode == QOpcUa::Good) {
++ m_monitoredState = true;
++ emit monitoredChanged(m_monitoredState);
++ qCDebug(QT_OPCUA_PLUGINS_QML) << "Monitoring was enabled for node" << resolvedNode().fullNodeId();
++ updateFilters();
++ } else {
++ qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to enable monitoring for node" << resolvedNode().fullNodeId();
++ setStatus(Status::FailedToSetupMonitoring);
++ }
++ });
++ connect(m_node, &QOpcUaNode::disableMonitoringFinished, this, [this](QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode){
++ if (attr != QOpcUa::NodeAttribute::Value)
++ return;
++ // The monitoring is gone in this case, regardless of the status code
++ if (statusCode == QOpcUa::Good ||
++ node()->monitoringStatus(QOpcUa::NodeAttribute::Value).statusCode()== QOpcUa::UaStatusCode::BadNoEntryExists) {
++ m_monitoredState = false;
++ emit monitoredChanged(m_monitoredState);
++ qCDebug(QT_OPCUA_PLUGINS_QML) << "Monitoring was disabled for node "<< resolvedNode().fullNodeId();
++ } else {
++ qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to disable monitoring for node "<< resolvedNode().fullNodeId();
++ setStatus(Status::FailedToDisableMonitoring);
++ }
++ });
++ connect(m_node, &QOpcUaNode::monitoringStatusChanged, this, [this](QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items,
++ QOpcUa::UaStatusCode statusCode) {
++ if (attr != QOpcUa::NodeAttribute::Value && attr != QOpcUa::NodeAttribute::EventNotifier)
++ return;
++ if (statusCode != QOpcUa::Good) {
++ setStatus(Status::FailedToModifyMonitoring);
++ qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to modify monitoring";
++ } else {
++ if (items & QOpcUaMonitoringParameters::Parameter::PublishingInterval) {
++ if (m_publishingInterval != m_node->monitoringStatus(QOpcUa::NodeAttribute::Value).publishingInterval()) {
++ m_publishingInterval = m_node->monitoringStatus(QOpcUa::NodeAttribute::Value).publishingInterval();
++ emit publishingIntervalChanged(m_publishingInterval);
++ }
++ }
++ }
++ });
++
++ updateSubscription();
++ }
++
++ void OpcUaValueNode::updateFilters() const
++ {
++ if (!m_connection || !m_node || !m_filter || !m_monitoredState)
++ return;
++
++ m_node->modifyDataChangeFilter(QOpcUa::NodeAttribute::Value, m_filter->filter());
++ }
++
++ bool OpcUaValueNode::checkValidity()
++ {
++ if (!m_connection || !m_node)
++ return false;
++
++ if (m_node->attribute(QOpcUa::NodeAttribute::NodeClass).value<QOpcUa::NodeClass>() != QOpcUa::NodeClass::Variable) {
++ setStatus(Status::InvalidNodeType);
++ return false;
++ }
++
++ setStatus(Status::Valid);
++ return true;
++ }
++
++ QVariant OpcUaValueNode::value() const
++ {
++ if (!m_connection || !m_node)
++ return QVariant();
++ return m_node->attribute(QOpcUa::NodeAttribute::Value);
++ }
++
++ QDateTime OpcUaValueNode::serverTimestamp() const
++ {
++ return getServerTimestamp(QOpcUa::NodeAttribute::Value);
++ }
++
++ QDateTime OpcUaValueNode::sourceTimestamp() const
++ {
++ return getSourceTimestamp(QOpcUa::NodeAttribute::Value);
++ }
++
++ bool OpcUaValueNode::monitored() const
++ {
++ if (!m_connection || !m_node)
++ return m_monitored;
++
++ return m_monitoredState;
++ }
++
++ void OpcUaValueNode::updateSubscription()
++ {
++ if (!m_connection || !m_node)
++ return;
++
++ QOpcUaMonitoringParameters parameters;
++ parameters.setPublishingInterval(m_publishingInterval);
++
++ if (m_filter)
++ parameters.setFilter(m_filter->filter());
++
++ if (m_monitoredState != m_monitored) {
++ if (m_monitored) {
++ m_node->enableMonitoring(QOpcUa::NodeAttribute::Value, parameters);
++ } else {
++ m_node->disableMonitoring(QOpcUa::NodeAttribute::Value);
++ }
++ }
++ }
++ void OpcUaValueNode::setMonitored(bool monitored)
++ {
++ m_monitored = monitored;
++ updateSubscription();
++ }
++
++ double OpcUaValueNode::publishingInterval() const
++ {
++ if (!m_connection || !m_node)
++ return 0.0;
++
++ auto monitoringStatus = node()->monitoringStatus(QOpcUa::NodeAttribute::Value);
++ if (monitoringStatus.statusCode() == QOpcUa::BadNoEntryExists)
++ return 0.0;
++ return monitoringStatus.publishingInterval();
++ }
++
++ OpcUaDataChangeFilter *OpcUaValueNode::filter() const
++ {
++ return m_filter;
++ }
++
++ void OpcUaValueNode::setFilter(OpcUaDataChangeFilter *filter)
++ {
++ bool changed = false;
++
++ if (m_filter) {
++ disconnect(m_filter, &OpcUaDataChangeFilter::filterChanged, this, &OpcUaValueNode::updateFilters);
++ changed = !(*m_filter == *filter);
++ } else {
++ changed = true;
++ }
++
++ m_filter = filter;
++ connect(m_filter, &OpcUaDataChangeFilter::filterChanged, this, &OpcUaValueNode::updateFilters);
++
++ if (changed)
++ emit filterChanged();
++ }
++
++ void OpcUaValueNode::setPublishingInterval(double publishingInterval)
++ {
++ if (!m_connection || !m_node)
++ return;
++ if (QtPrivate::fuzzyCompare(m_publishingInterval, publishingInterval))
++ return;
++
++ m_node->modifyMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters::Parameter::PublishingInterval, publishingInterval);
++ }
++
++ QOpcUa::Types OpcUaValueNode::valueType() const
++ {
++ return m_valueType;
++ }
++
++ void OpcUaValueNode::setValueType(QOpcUa::Types valueType)
++ {
++ m_valueType = valueType;
++ }
++
++ QT_END_NAMESPACE