diff options
author | Rainer Keller <Rainer.Keller@qt.io> | 2019-04-29 13:14:14 +0200 |
---|---|---|
committer | Rainer Keller <Rainer.Keller@qt.io> | 2019-05-17 10:15:56 +0000 |
commit | 387309bd295256dd1668fc7fa7de979331763a77 (patch) | |
tree | 58f1afe3b8ab5f1498b3858d9a9832c470c23042 /src | |
parent | 7ce99deb73f854b367139212b6042b4ce4e06b8e (diff) |
qml: Add data change filter support
Change-Id: Ib0e8d10f298a63fb7b3892cd61ac5e4c12b4d56f
Reviewed-by: Jannis Völker <jannis.voelker@basyskom.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/imports/opcua/opcua.pro | 2 | ||||
-rw-r--r-- | src/imports/opcua/opcua_plugin.cpp | 2 | ||||
-rw-r--r-- | src/imports/opcua/opcuadatachangefilter.cpp | 114 | ||||
-rw-r--r-- | src/imports/opcua/opcuadatachangefilter.h | 93 | ||||
-rw-r--r-- | src/imports/opcua/opcuavaluenode.cpp | 46 | ||||
-rw-r--r-- | src/imports/opcua/opcuavaluenode.h | 9 |
6 files changed, 264 insertions, 2 deletions
diff --git a/src/imports/opcua/opcua.pro b/src/imports/opcua/opcua.pro index e53cf6c..dd3cc01 100644 --- a/src/imports/opcua/opcua.pro +++ b/src/imports/opcua/opcua.pro @@ -22,6 +22,7 @@ SOURCES += \ opcuastatus.cpp \ opcuawriteitem.cpp \ opcuawriteresult.cpp \ + opcuadatachangefilter.cpp \ HEADERS += \ opcua_plugin.h \ @@ -45,6 +46,7 @@ HEADERS += \ opcuastatus.h \ opcuawriteitem.h \ opcuawriteresult.h \ + opcuadatachangefilter.h \ load(qml_plugin) diff --git a/src/imports/opcua/opcua_plugin.cpp b/src/imports/opcua/opcua_plugin.cpp index 3fc8ac6..4c2c51b 100644 --- a/src/imports/opcua/opcua_plugin.cpp +++ b/src/imports/opcua/opcua_plugin.cpp @@ -41,6 +41,7 @@ #include "opcuanodeid.h" #include "opcuanodeidtype.h" #include "opcuaconnection.h" +#include "opcuadatachangefilter.h" #include "opcuarelativenodepath.h" #include "opcuarelativenodeid.h" #include "qopcuatype.h" @@ -145,6 +146,7 @@ void OpcUaPlugin::registerTypes(const char *uri) qmlRegisterType<OpcUaEndpointDiscovery>(uri, major, minor, "EndpointDiscovery"); qmlRegisterType<OpcUaServerDiscovery>(uri, major, minor, "ServerDiscovery"); qmlRegisterUncreatableType<QOpcUaUserTokenPolicy>(uri, major, minor, "UserTokenPolicy", "This type can not be created."); + qmlRegisterType<OpcUaDataChangeFilter>(uri, major, minor, "DataChangeFilter"); // insert new versions here diff --git a/src/imports/opcua/opcuadatachangefilter.cpp b/src/imports/opcua/opcuadatachangefilter.cpp new file mode 100644 index 0000000..fb06839 --- /dev/null +++ b/src/imports/opcua/opcuadatachangefilter.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt OPC UA module. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "opcuadatachangefilter.h" + +QT_BEGIN_NAMESPACE + +OpcUaDataChangeFilter::OpcUaDataChangeFilter(QObject *parent) + : QObject(parent) +{ + +} + +OpcUaDataChangeFilter::OpcUaDataChangeFilter(const OpcUaDataChangeFilter &other) + : QObject(nullptr) + , m_filter(other.filter()) +{ +} + +OpcUaDataChangeFilter::~OpcUaDataChangeFilter() = default; + +OpcUaDataChangeFilter &OpcUaDataChangeFilter::operator=(const OpcUaDataChangeFilter &other) +{ + m_filter = other.filter(); + return *this; +} + +bool OpcUaDataChangeFilter::operator==(const OpcUaDataChangeFilter &other) const +{ + return m_filter == other.filter(); +} + +OpcUaDataChangeFilter::DataChangeTrigger OpcUaDataChangeFilter::trigger() const +{ + return static_cast<OpcUaDataChangeFilter::DataChangeTrigger>(m_filter.trigger()); +} + +OpcUaDataChangeFilter::DeadbandType OpcUaDataChangeFilter::deadbandType() const +{ + return static_cast<OpcUaDataChangeFilter::DeadbandType>(m_filter.deadbandType()); +} + +double OpcUaDataChangeFilter::deadbandValue() const +{ + return m_filter.deadbandValue(); +} + +const QOpcUaMonitoringParameters::DataChangeFilter &OpcUaDataChangeFilter::filter() const +{ + return m_filter; +} + +void OpcUaDataChangeFilter::setTrigger(DataChangeTrigger trigger) +{ + const auto newValue = static_cast<QOpcUaMonitoringParameters::DataChangeFilter::DataChangeTrigger>(trigger); + + if (m_filter.trigger() == newValue) + return; + m_filter.setTrigger(newValue); + emit filterChanged(); +} + +void OpcUaDataChangeFilter::setDeadbandType(DeadbandType deadbandType) +{ + const auto newValue = static_cast<QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType>(deadbandType); + + if (m_filter.deadbandType() == newValue) + return; + m_filter.setDeadbandType(newValue); + emit filterChanged(); +} + +void OpcUaDataChangeFilter::setDeadbandValue(double deadbandValue) +{ + if (deadbandValue == m_filter.deadbandValue()) + return; + m_filter.setDeadbandValue(deadbandValue); + emit filterChanged(); +} + +QT_END_NAMESPACE diff --git a/src/imports/opcua/opcuadatachangefilter.h b/src/imports/opcua/opcuadatachangefilter.h new file mode 100644 index 0000000..753c0f6 --- /dev/null +++ b/src/imports/opcua/opcuadatachangefilter.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt OPC UA module. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OPCUADATACHANGEFILTER +#define OPCUADATACHANGEFILTER + +#include <QOpcUaMonitoringParameters> + +QT_BEGIN_NAMESPACE + +class OpcUaDataChangeFilter : public QObject { + Q_OBJECT + Q_PROPERTY(DataChangeTrigger trigger READ trigger WRITE setTrigger) + Q_PROPERTY(DeadbandType deadbandType READ deadbandType WRITE setDeadbandType) + Q_PROPERTY(double deadbandValue READ deadbandValue WRITE setDeadbandValue) + +public: + // Same as in QOpcUaMonitoringParameters::DataChangeFilter::DataChangeTrigger + enum class DataChangeTrigger { + Status = 0, + StatusOrValue = 1, + StatusOrValueOrTimestamp = 2 + }; + Q_ENUM(DataChangeTrigger) + + // Same as in QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType + enum class DeadbandType { + None = 0, + Absolute = 1, + Percent = 2 + }; + Q_ENUM(DeadbandType) + + explicit OpcUaDataChangeFilter(QObject *parent = nullptr); + OpcUaDataChangeFilter(const OpcUaDataChangeFilter &); + OpcUaDataChangeFilter &operator=(const OpcUaDataChangeFilter &); + bool operator==(const OpcUaDataChangeFilter &) const; + ~OpcUaDataChangeFilter(); + + DataChangeTrigger trigger() const; + DeadbandType deadbandType() const; + double deadbandValue() const; + + const QOpcUaMonitoringParameters::DataChangeFilter &filter() const; + +signals: + void filterChanged(); + +public slots: + void setTrigger(DataChangeTrigger trigger); + void setDeadbandType(DeadbandType deadbandType); + void setDeadbandValue(double deadbandValue); + +private: + QOpcUaMonitoringParameters::DataChangeFilter m_filter; +}; + +QT_END_NAMESPACE + +#endif // OPCUADATACHANGEFILTER diff --git a/src/imports/opcua/opcuavaluenode.cpp b/src/imports/opcua/opcuavaluenode.cpp index 8a51247..cd42595 100644 --- a/src/imports/opcua/opcuavaluenode.cpp +++ b/src/imports/opcua/opcuavaluenode.cpp @@ -102,6 +102,7 @@ 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() @@ -161,6 +162,10 @@ void OpcUaValueNode::setupNode(const QString &absolutePath) m_monitoredState = true; emit monitoredChanged(m_monitoredState); qCDebug(QT_OPCUA_PLUGINS_QML) << "Monitoring was enabled for node" << resolvedNode().fullNodeId(); + if (m_connection->backend() != QLatin1String("open62541")) { + // This line triggers a bug in open62541. When it is fixed the call should be unconditional. + updateFilters(); + } } else { qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to enable monitoring for node" << resolvedNode().fullNodeId(); setStatus(Status::FailedToSetupMonitoring); @@ -180,7 +185,7 @@ void OpcUaValueNode::setupNode(const QString &absolutePath) }); connect(m_node, &QOpcUaNode::monitoringStatusChanged, this, [this](QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items, QOpcUa::UaStatusCode statusCode) { - if (attr != QOpcUa::NodeAttribute::Value) + if (attr != QOpcUa::NodeAttribute::Value && attr != QOpcUa::NodeAttribute::EventNotifier) return; if (statusCode != QOpcUa::Good) { setStatus(Status::FailedToModifyMonitoring); @@ -198,6 +203,14 @@ void OpcUaValueNode::setupNode(const QString &absolutePath) 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) @@ -242,9 +255,15 @@ 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, QOpcUaMonitoringParameters(m_publishingInterval)); + m_node->enableMonitoring(QOpcUa::NodeAttribute::Value, parameters); } else { m_node->disableMonitoring(QOpcUa::NodeAttribute::Value); } @@ -267,6 +286,29 @@ double OpcUaValueNode::publishingInterval() const 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) diff --git a/src/imports/opcua/opcuavaluenode.h b/src/imports/opcua/opcuavaluenode.h index cafaf41..ff0a929 100644 --- a/src/imports/opcua/opcuavaluenode.h +++ b/src/imports/opcua/opcuavaluenode.h @@ -38,6 +38,7 @@ #include "opcuanode.h" #include <QDateTime> +#include "opcuadatachangefilter.h" QT_BEGIN_NAMESPACE @@ -51,6 +52,7 @@ class OpcUaValueNode : public OpcUaNode Q_PROPERTY(QDateTime sourceTimestamp READ sourceTimestamp) Q_PROPERTY(bool monitored READ monitored WRITE setMonitored NOTIFY monitoredChanged) Q_PROPERTY(double publishingInterval READ publishingInterval WRITE setPublishingInterval NOTIFY publishingIntervalChanged) + Q_PROPERTY(OpcUaDataChangeFilter *filter READ filter WRITE setFilter NOTIFY filterChanged) public: OpcUaValueNode(QObject *parent = nullptr); @@ -62,6 +64,8 @@ public: bool monitored() const; double publishingInterval() const; QOpcUa::Types valueType() const; + OpcUaDataChangeFilter *filter() const; + void setFilter(OpcUaDataChangeFilter *filter); public slots: void setValue(const QVariant &); @@ -69,14 +73,18 @@ public slots: void setPublishingInterval(double publishingInterval); void setValueType(QOpcUa::Types valueType); + signals: void valueChanged(const QVariant &value); void monitoredChanged(bool monitored); void publishingIntervalChanged(double publishingInterval); + void dataChangeOccurred(const QVariant &value); + void filterChanged(); private slots: void setupNode(const QString &absolutePath) override; void updateSubscription(); + void updateFilters() const; private: bool checkValidity() override; @@ -84,6 +92,7 @@ private: bool m_monitoredState = false; double m_publishingInterval = 100; QOpcUa::Types m_valueType = QOpcUa::Types::Undefined; + OpcUaDataChangeFilter *m_filter = nullptr; }; QT_END_NAMESPACE |