summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRainer Keller <Rainer.Keller@qt.io>2019-04-29 13:14:14 +0200
committerRainer Keller <Rainer.Keller@qt.io>2019-05-17 10:15:56 +0000
commit387309bd295256dd1668fc7fa7de979331763a77 (patch)
tree58f1afe3b8ab5f1498b3858d9a9832c470c23042 /src
parent7ce99deb73f854b367139212b6042b4ce4e06b8e (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.pro2
-rw-r--r--src/imports/opcua/opcua_plugin.cpp2
-rw-r--r--src/imports/opcua/opcuadatachangefilter.cpp114
-rw-r--r--src/imports/opcua/opcuadatachangefilter.h93
-rw-r--r--src/imports/opcua/opcuavaluenode.cpp46
-rw-r--r--src/imports/opcua/opcuavaluenode.h9
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