summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJannis Voelker <jannis.voelker@basyskom.com>2017-11-13 14:34:17 +0100
committerFrank Meerkoetter <frank.meerkoetter@basyskom.com>2017-12-14 10:52:33 +0000
commit28f2c5d6bc152948a3ce7f22e55db9afcaba1d3c (patch)
tree5d7138b25642bfc8f98e1f0432e44cbbad6731c0
parent0e71f5b4a9b6073f527236f5e6ac9d423ebe315b (diff)
Add async API for data change subscriptions
Subscriptions and monitored items are now handled entirely in the backend, no objects are passed to the user. Monitoring of attributes is activated using enableMonitoring() on the node. Different attributes of the node can be monitored with different intervals. Subscriptions are shared by default. Parameters allow requesting the creation of an exclusive subscription or to assign monitored items for attributes to an existing subscription. disableMonitoring() removes a monitored item. The enableMonitoringFinished and disableMonitoringFinished signals inform the user about the success of adding and removing monitored items. Data changes are reported by the attributeUpdated signal, this also updates the attribute cache. Monitoring parameters can be modified using modifyMonitoring(). Changed settings are reported by the monitoringStatusChanged signal, the value can be checked by calling monitoringStatus() for an attribute. Modifying monitoring parameters is currently not supported in freeopcua. Open62541 allows modifying subscription settings, monitored items can't be modified. Change-Id: I3ba5b3f03bd9f0413c98feefa9ad9571f82e8f0d Reviewed-by: Frank Meerkoetter <frank.meerkoetter@basyskom.com>
-rw-r--r--src/opcua/client/client.pri23
-rw-r--r--src/opcua/client/qopcuabackend_p.h6
-rw-r--r--src/opcua/client/qopcuaclient.cpp17
-rw-r--r--src/opcua/client/qopcuaclient.h3
-rw-r--r--src/opcua/client/qopcuaclient_p.h1
-rw-r--r--src/opcua/client/qopcuaclientimpl.cpp25
-rw-r--r--src/opcua/client/qopcuaclientimpl_p.h8
-rw-r--r--src/opcua/client/qopcuamonitoredevent.cpp91
-rw-r--r--src/opcua/client/qopcuamonitoredevent.h72
-rw-r--r--src/opcua/client/qopcuamonitoredevent_p.h78
-rw-r--r--src/opcua/client/qopcuamonitoredeventprivate.cpp62
-rw-r--r--src/opcua/client/qopcuamonitoredvalue.cpp86
-rw-r--r--src/opcua/client/qopcuamonitoredvalue.h70
-rw-r--r--src/opcua/client/qopcuamonitoredvalue_p.h78
-rw-r--r--src/opcua/client/qopcuamonitoredvalueprivate.cpp64
-rw-r--r--src/opcua/client/qopcuamonitoringparameters.cpp390
-rw-r--r--src/opcua/client/qopcuamonitoringparameters.h165
-rw-r--r--src/opcua/client/qopcuamonitoringparameters_p.h (renamed from src/opcua/client/qopcuasubscription_p.h)56
-rw-r--r--src/opcua/client/qopcuanode.cpp115
-rw-r--r--src/opcua/client/qopcuanode.h14
-rw-r--r--src/opcua/client/qopcuanode_p.h59
-rw-r--r--src/opcua/client/qopcuanodeimpl_p.h12
-rw-r--r--src/opcua/client/qopcuasubscription.cpp107
-rw-r--r--src/opcua/client/qopcuasubscription.h71
-rw-r--r--src/opcua/client/qopcuasubscriptionimpl.cpp49
-rw-r--r--src/opcua/client/qopcuasubscriptionimpl_p.h73
-rw-r--r--src/opcua/client/qopcuasubscriptionprivate.cpp53
-rw-r--r--src/opcua/client/qopcuavaluesubscription.cpp77
-rw-r--r--src/opcua/client/qopcuavaluesubscription.h67
-rw-r--r--src/opcua/core/qopcuaprovider.cpp4
-rw-r--r--src/opcua/doc/src/qtopcua.qdoc6
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuaclient.cpp11
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuaclient.h2
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuanode.cpp30
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuanode.h5
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp247
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuasubscription.h59
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp117
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuaworker.h18
-rw-r--r--src/plugins/opcua/open62541/qopen62541backend.cpp203
-rw-r--r--src/plugins/opcua/open62541/qopen62541backend.h31
-rw-r--r--src/plugins/opcua/open62541/qopen62541client.cpp8
-rw-r--r--src/plugins/opcua/open62541/qopen62541client.h1
-rw-r--r--src/plugins/opcua/open62541/qopen62541node.cpp28
-rw-r--r--src/plugins/opcua/open62541/qopen62541node.h3
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.cpp295
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.h56
-rw-r--r--tests/auto/qopcuaclient/tst_client.cpp204
48 files changed, 1776 insertions, 1544 deletions
diff --git a/src/opcua/client/client.pri b/src/opcua/client/client.pri
index c91ffdf..9be8a86 100644
--- a/src/opcua/client/client.pri
+++ b/src/opcua/client/client.pri
@@ -2,35 +2,24 @@
PUBLIC_HEADERS += \
client/qopcuaclient.h \
- client/qopcuasubscription.h \
client/qopcuanode.h \
- client/qopcuatype.h \
- client/qopcuamonitoredevent.h \
- client/qopcuamonitoredvalue.h
+ client/qopcuatype.h
SOURCES += \
client/qopcuaclient.cpp \
- client/qopcuasubscription.cpp \
client/qopcuanode.cpp \
client/qopcuatype.cpp \
- client/qopcuamonitoredevent.cpp \
- client/qopcuamonitoredvalue.cpp \
client/qopcuaclientimpl.cpp \
client/qopcuanodeimpl.cpp \
client/qopcuaclientprivate.cpp \
- client/qopcuamonitoredeventprivate.cpp \
- client/qopcuasubscriptionprivate.cpp \
- client/qopcuamonitoredvalueprivate.cpp \
- client/qopcuasubscriptionimpl.cpp \
- client/qopcuabackend.cpp
+ client/qopcuabackend.cpp \
+ client/qopcuamonitoringparameters.cpp
HEADERS += \
client/qopcuaclient_p.h \
client/qopcuaclientimpl_p.h \
client/qopcuanode_p.h \
client/qopcuanodeimpl_p.h \
- client/qopcuamonitoredevent_p.h \
- client/qopcuamonitoredvalue_p.h \
- client/qopcuasubscription_p.h \
- client/qopcuasubscriptionimpl_p.h \
- client/qopcuabackend_p.h
+ client/qopcuabackend_p.h \
+ client/qopcuamonitoringparameters.h \
+ client/qopcuamonitoringparameters_p.h
diff --git a/src/opcua/client/qopcuabackend_p.h b/src/opcua/client/qopcuabackend_p.h
index da4cf9c..28cbd28 100644
--- a/src/opcua/client/qopcuabackend_p.h
+++ b/src/opcua/client/qopcuabackend_p.h
@@ -58,6 +58,7 @@
QT_BEGIN_NAMESPACE
class QOpcUaNodeImpl;
+class QOpcUaMonitoringParameters;
class Q_OPCUA_EXPORT QOpcUaBackend : public QObject
{
@@ -80,6 +81,11 @@ Q_SIGNALS:
void attributesRead(uintptr_t handle, QVector<QOpcUaReadResult> attributes, QOpcUa::UaStatusCode serviceResult);
void attributeWritten(uintptr_t hande, QOpcUaNode::NodeAttribute attribute, QVariant value, QOpcUa::UaStatusCode statusCode);
+ void attributeUpdated(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QVariant value);
+ void monitoringEnableDisable(uintptr_t handle, QOpcUaNode::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status);
+ void monitoringStatusChanged(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items,
+ QOpcUaMonitoringParameters param);
+
private:
Q_DISABLE_COPY(QOpcUaBackend)
};
diff --git a/src/opcua/client/qopcuaclient.cpp b/src/opcua/client/qopcuaclient.cpp
index 86fd7f9..4306af7 100644
--- a/src/opcua/client/qopcuaclient.cpp
+++ b/src/opcua/client/qopcuaclient.cpp
@@ -241,21 +241,4 @@ QString QOpcUaClient::backend() const
return d_func()->m_impl->backend();
}
-/*!
- Creates a subscription with \a interval milliseconds publishing period
- on the server and returns a QOpcUaSubscription object for it. The
- subscription may be used to monitor nodes for events or value changes.
- The caller becomes the owner of the newly created subscription object.
-
- For this method to work the client needs to be connected to the server.
- A null pointer is returned on error.
-*/
-QOpcUaSubscription *QOpcUaClient::createSubscription(quint32 interval)
-{
- if (state() != QOpcUaClient::Connected)
- return nullptr;
-
- return d_func()->m_impl->createSubscription(interval);
-}
-
QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuaclient.h b/src/opcua/client/qopcuaclient.h
index 604c5f4..15c9ee2 100644
--- a/src/opcua/client/qopcuaclient.h
+++ b/src/opcua/client/qopcuaclient.h
@@ -39,7 +39,6 @@
#include <QtOpcUa/qopcuaglobal.h>
#include <QtOpcUa/qopcuanode.h>
-#include <QtOpcUa/qopcuasubscription.h>
#include <QtCore/qobject.h>
#include <QtCore/qurl.h>
@@ -82,8 +81,6 @@ public:
Q_INVOKABLE void disconnectFromEndpoint();
QOpcUaNode *node(const QString &nodeId);
- QOpcUaSubscription *createSubscription(quint32 interval);
-
QUrl url() const;
ClientState state() const;
diff --git a/src/opcua/client/qopcuaclient_p.h b/src/opcua/client/qopcuaclient_p.h
index a5511fd..8e9d447 100644
--- a/src/opcua/client/qopcuaclient_p.h
+++ b/src/opcua/client/qopcuaclient_p.h
@@ -50,7 +50,6 @@
#include <QtOpcUa/qopcuaclient.h>
#include <QtOpcUa/qopcuaglobal.h>
-#include <QtOpcUa/qopcuavaluesubscription.h>
#include <private/qopcuaclientimpl_p.h>
#include <QtCore/qobject.h>
diff --git a/src/opcua/client/qopcuaclientimpl.cpp b/src/opcua/client/qopcuaclientimpl.cpp
index f44a928..e3e414d 100644
--- a/src/opcua/client/qopcuaclientimpl.cpp
+++ b/src/opcua/client/qopcuaclientimpl.cpp
@@ -36,6 +36,7 @@
#include <private/qopcuabackend_p.h>
#include <private/qopcuaclientimpl_p.h>
+#include <QtOpcUa/qopcuamonitoringparameters.h>
QT_BEGIN_NAMESPACE
@@ -61,6 +62,9 @@ void QOpcUaClientImpl::connectBackendWithClient(QOpcUaBackend *backend)
connect(backend, &QOpcUaBackend::attributesRead, this, &QOpcUaClientImpl::handleAttributesRead);
connect(backend, &QOpcUaBackend::stateAndOrErrorChanged, this, &QOpcUaClientImpl::stateAndOrErrorChanged);
connect(backend, &QOpcUaBackend::attributeWritten, this, &QOpcUaClientImpl::handleAttributeWritten);
+ connect(backend, &QOpcUaBackend::attributeUpdated, this, &QOpcUaClientImpl::handleAttributeUpdated);
+ connect(backend, &QOpcUaBackend::monitoringEnableDisable, this, &QOpcUaClientImpl::handleMonitoringEnableDisable);
+ connect(backend, &QOpcUaBackend::monitoringStatusChanged, this, &QOpcUaClientImpl::handleMonitoringStatusChanged);
}
void QOpcUaClientImpl::handleAttributesRead(uintptr_t handle, QVector<QOpcUaReadResult> attr, QOpcUa::UaStatusCode serviceResult)
@@ -77,4 +81,25 @@ void QOpcUaClientImpl::handleAttributeWritten(uintptr_t handle, QOpcUaNode::Node
emit (*it)->attributeWritten(attr, value, statusCode);
}
+void QOpcUaClientImpl::handleAttributeUpdated(uintptr_t handle, QOpcUaNode::NodeAttribute attr, const QVariant &value)
+{
+ auto it = m_handles.constFind(handle);
+ if (it != m_handles.constEnd() && !it->isNull())
+ emit (*it)->attributeUpdated(attr, value);
+}
+
+void QOpcUaClientImpl::handleMonitoringEnableDisable(uintptr_t handle, QOpcUaNode::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status)
+{
+ auto it = m_handles.constFind(handle);
+ if (it != m_handles.constEnd() && !it->isNull())
+ emit (*it)->monitoringEnableDisable(attr, subscribe, status);
+}
+
+void QOpcUaClientImpl::handleMonitoringStatusChanged(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items, QOpcUaMonitoringParameters param)
+{
+ auto it = m_handles.constFind(handle);
+ if (it != m_handles.constEnd() && !it->isNull())
+ emit (*it)->monitoringStatusChanged(attr, items, param);
+}
+
QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuaclientimpl_p.h b/src/opcua/client/qopcuaclientimpl_p.h
index 7ddbf95..07d3f9c 100644
--- a/src/opcua/client/qopcuaclientimpl_p.h
+++ b/src/opcua/client/qopcuaclientimpl_p.h
@@ -60,8 +60,8 @@ QT_BEGIN_NAMESPACE
class QOpcUaNode;
class QOpcUaClient;
-class QOpcUaSubscription;
class QOpcUaBackend;
+class QOpcUaMonitoringParameters;
class Q_OPCUA_EXPORT QOpcUaClientImpl : public QObject
{
@@ -83,13 +83,15 @@ public:
void connectBackendWithClient(QOpcUaBackend *backend);
- virtual QOpcUaSubscription *createSubscription(quint32 interval) = 0;
-
QOpcUaClient *m_client;
private Q_SLOTS:
void handleAttributesRead(uintptr_t handle, QVector<QOpcUaReadResult> attr, QOpcUa::UaStatusCode serviceResult);
void handleAttributeWritten(uintptr_t handle, QOpcUaNode::NodeAttribute attr, const QVariant &value, QOpcUa::UaStatusCode statusCode);
+ void handleAttributeUpdated(uintptr_t handle, QOpcUaNode::NodeAttribute attr, const QVariant &value);
+ void handleMonitoringEnableDisable(uintptr_t handle, QOpcUaNode::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status);
+ void handleMonitoringStatusChanged(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items,
+ QOpcUaMonitoringParameters param);
signals:
void connected();
diff --git a/src/opcua/client/qopcuamonitoredevent.cpp b/src/opcua/client/qopcuamonitoredevent.cpp
deleted file mode 100644
index 40a7399..0000000
--- a/src/opcua/client/qopcuamonitoredevent.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 "qopcuamonitoredevent.h"
-#include "qopcuasubscription.h"
-#include <private/qopcuamonitoredevent_p.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- * \internal
- */
-QOpcUaMonitoredEvent::QOpcUaMonitoredEvent(QOpcUaNode *node, QOpcUaSubscription *subscription, QObject *parent)
- : QObject(*new QOpcUaMonitoredEventPrivate(node, subscription), parent)
-{}
-
-/*!
- Destroys this event monitor instance. This will automatically
- remove it from its subscription.
- */
-QOpcUaMonitoredEvent::~QOpcUaMonitoredEvent()
-{
- d_func()->m_subscription->removeEvent(this);
-}
-
-/*!
- Returns the node which belongs to this event monitor instance.
-*/
-QOpcUaNode &QOpcUaMonitoredEvent::node()
-{
- return *d_func()->m_node;
-}
-
-/*!
- \class QOpcUaMonitoredEvent
- \inmodule QtOpcUa
-
- \brief Represents a source for events corresponding to a node on a OPC UA server.
-
- \chapter QOpcUaMonitoredEvent
- Every QOpcUaMonitoredEvent emits signals when the corresponding event on
- the server is triggered. They are backed by QOpcUaSubscriptions.
-
- The objects are owned by the user and must be deleted when they are no longer needed.
- Deletion causes the monitored items to be unsubscribed from the server.
-*/
-
-/*!
- \fn void QOpcUaMonitoredEvent::newEvent(QVector<QVariant> val) const
- This signal is emitted when a new event is received by a data change
- subscription.
- The QVector \a val currently contains three QVariants holding the values of the
- first three event fields.
- \warning When using the FreeOPCUA backend, no signal is emitted as the
- internal implementation of events does not call the callback method.
-*/
-
-QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuamonitoredevent.h b/src/opcua/client/qopcuamonitoredevent.h
deleted file mode 100644
index 6bc345b..0000000
--- a/src/opcua/client/qopcuamonitoredevent.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 QOPCUAMONITOREDEVENT_H
-#define QOPCUAMONITOREDEVENT_H
-
-#include <QtOpcUa/qopcuaglobal.h>
-#include <QtOpcUa/qopcuanode.h>
-
-#include <QtCore/qobject.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qvector.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpcUaMonitoredEventPrivate;
-class QOpcUaMonitoredEventImpl;
-class QOpcUaSubscription;
-
-class Q_OPCUA_EXPORT QOpcUaMonitoredEvent : public QObject
-{
- Q_OBJECT
-
-public:
- Q_DECLARE_PRIVATE(QOpcUaMonitoredEvent)
-
- QOpcUaMonitoredEvent(QOpcUaNode *node, QOpcUaSubscription *subscription, QObject *parent = nullptr);
- ~QOpcUaMonitoredEvent() override;
- QOpcUaNode &node();
-
-Q_SIGNALS:
- void newEvent(QVector<QVariant> value) const;
-private:
- Q_DISABLE_COPY(QOpcUaMonitoredEvent)
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPCUAMONITOREDEVENT_H
diff --git a/src/opcua/client/qopcuamonitoredevent_p.h b/src/opcua/client/qopcuamonitoredevent_p.h
deleted file mode 100644
index 1f300ac..0000000
--- a/src/opcua/client/qopcuamonitoredevent_p.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 QOPCUAMONITOREDEVENT_P_H
-#define QOPCUAMONITOREDEVENT_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtOpcUa/qopcuaglobal.h>
-#include <QtOpcUa/qopcuamonitoredevent.h>
-#include <QtOpcUa/qopcuanode.h>
-
-#include <private/qobject_p.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qvector.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpcUaSubscriptionImpl;
-class QOpcUaSubscription;
-
-class Q_OPCUA_EXPORT QOpcUaMonitoredEventPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QOpcUaMonitoredEvent)
-public:
- QOpcUaMonitoredEventPrivate(QOpcUaNode *node, QOpcUaSubscription *subscription);
- ~QOpcUaMonitoredEventPrivate() override;
-
- bool triggerNewEvent(const QVector<QVariant> &val);
- QOpcUaNode *m_node;
- QOpcUaSubscription *m_subscription;
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPCUAMONITOREDEVENT_P_H
diff --git a/src/opcua/client/qopcuamonitoredeventprivate.cpp b/src/opcua/client/qopcuamonitoredeventprivate.cpp
deleted file mode 100644
index d9c59a1..0000000
--- a/src/opcua/client/qopcuamonitoredeventprivate.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 <private/qopcuamonitoredevent_p.h>
-#include <private/qopcuasubscriptionimpl_p.h>
-
-QT_BEGIN_NAMESPACE
-
-QOpcUaMonitoredEventPrivate::QOpcUaMonitoredEventPrivate(QOpcUaNode *node, QOpcUaSubscription *subscription)
- : m_node(node)
- , m_subscription(subscription)
-{
-}
-
-QOpcUaMonitoredEventPrivate::~QOpcUaMonitoredEventPrivate()
-{
-}
-
-bool QOpcUaMonitoredEventPrivate::triggerNewEvent(const QVector<QVariant> &val)
-{
- static const int meta = qRegisterMetaType<QVector<QVariant>>();
- Q_UNUSED(meta);
-
- // explicitly use invoke to force the signal to be emitted on the main thread
- // even if the plugin triggered this from a worker thread
- return QMetaObject::invokeMethod(q_func(), "newEvent", Qt::AutoConnection, Q_ARG(QVector<QVariant>, val));
-}
-
-QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuamonitoredvalue.cpp b/src/opcua/client/qopcuamonitoredvalue.cpp
deleted file mode 100644
index 802ad24..0000000
--- a/src/opcua/client/qopcuamonitoredvalue.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 "qopcuamonitoredvalue.h"
-#include "qopcuasubscription.h"
-#include <private/qopcuamonitoredvalue_p.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QOpcUaMonitoredValue
- \inmodule QtOpcUa
-
- \brief Represents a source for values corresponding to a node on a OPC UA server.
-
- \chapter QOpcUaMonitoredValue
- Every QOpcUaMonitoredValue emits signals when the corresponding value in
- nodes on the server change. They are backed by QOpcUaSubscriptions.
-
- The objects are owned by the user and must be deleted when they are no longer needed.
- Deletion causes the monitored items to be unsubscribed from the server.
-*/
-
-/*!
- \fn void QOpcUaMonitoredValue::valueChanged(QVariant val) const
-
- This signal is emitted when a new update for a data change subscription
- arrives. \a val contains the new value.
- */
-
-QOpcUaMonitoredValue::QOpcUaMonitoredValue(QOpcUaNode *node, QOpcUaSubscription *subscription, QObject *parent)
- : QObject(*new QOpcUaMonitoredValuePrivate(node, subscription), parent)
-{
-}
-
-QOpcUaMonitoredValue::~QOpcUaMonitoredValue()
-{
- Q_D(QOpcUaMonitoredValue);
- if (d->m_subscription)
- d->m_subscription->removeValue(this);
-}
-
-/*!
- \fn QOpcUaNode &QOpcUaMonitoredValue::node()
-
- Returns the node which belongs to this value monitor instance.
-*/
-QOpcUaNode &QOpcUaMonitoredValue::node()
-{
- return *d_func()->m_node;
-}
-
-QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuamonitoredvalue.h b/src/opcua/client/qopcuamonitoredvalue.h
deleted file mode 100644
index 1761a79..0000000
--- a/src/opcua/client/qopcuamonitoredvalue.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 QOPCUAMONITOREDVALUE_H
-#define QOPCUAMONITOREDVALUE_H
-
-#include <QtOpcUa/qopcuaglobal.h>
-#include <QtOpcUa/qopcuanode.h>
-
-#include <QtCore/qvariant.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpcUaMonitoredValuePrivate;
-class QOpcUaMonitoredValueImpl;
-class QOpcUaSubscription;
-
-class Q_OPCUA_EXPORT QOpcUaMonitoredValue : public QObject
-{
- Q_OBJECT
-
-public:
- Q_DECLARE_PRIVATE(QOpcUaMonitoredValue)
-
- QOpcUaMonitoredValue(QOpcUaNode *node, QOpcUaSubscription *subscription, QObject *parent = nullptr);
- ~QOpcUaMonitoredValue() override;
- QOpcUaNode &node();
-
-Q_SIGNALS:
- void valueChanged(QVariant val) const;
-private:
- Q_DISABLE_COPY(QOpcUaMonitoredValue)
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPCUAMONITOREDVALUE_H
diff --git a/src/opcua/client/qopcuamonitoredvalue_p.h b/src/opcua/client/qopcuamonitoredvalue_p.h
deleted file mode 100644
index 49bf31b..0000000
--- a/src/opcua/client/qopcuamonitoredvalue_p.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 QOPCUAMONITOREDVALUE_P_H
-#define QOPCUAMONITOREDVALUE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtOpcUa/qopcuaglobal.h>
-#include <QtOpcUa/qopcuamonitoredvalue.h>
-#include <QtOpcUa/qopcuanode.h>
-
-#include <private/qobject_p.h>
-#include <QtCore/qvariant.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpcUaSubscriptionImpl;
-class QOpcUaSubscription;
-
-class Q_OPCUA_EXPORT QOpcUaMonitoredValuePrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QOpcUaMonitoredValue)
-public:
- QOpcUaMonitoredValuePrivate(QOpcUaNode *node, QOpcUaSubscription *subscription);
- ~QOpcUaMonitoredValuePrivate() override;
-
- void triggerValueChanged(const QVariant &val);
- QOpcUaNode *m_node;
- QOpcUaSubscription *m_subscription;
- QVariant m_currentValue;
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPCUAMONITOREDVALUE_P_H
diff --git a/src/opcua/client/qopcuamonitoredvalueprivate.cpp b/src/opcua/client/qopcuamonitoredvalueprivate.cpp
deleted file mode 100644
index 8cd2297..0000000
--- a/src/opcua/client/qopcuamonitoredvalueprivate.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 <private/qopcuamonitoredvalue_p.h>
-#include <private/qopcuasubscriptionimpl_p.h>
-
-QT_BEGIN_NAMESPACE
-
-QOpcUaMonitoredValuePrivate::QOpcUaMonitoredValuePrivate(QOpcUaNode *node, QOpcUaSubscription *subscription)
- : m_node(node)
- , m_subscription(subscription)
- // TODO: is it useful to initialize a monitored item with a potentially uninitialized value?
- , m_currentValue(node->attribute(QOpcUaNode::NodeAttribute::Value))
-{
-}
-
-QOpcUaMonitoredValuePrivate::~QOpcUaMonitoredValuePrivate()
-{
-}
-
-void QOpcUaMonitoredValuePrivate::triggerValueChanged(const QVariant &val)
-{
- // explicitly use invoke to force the signal to be emitted on the main thread
- // even if the plugin triggered this from a worker thread
- if (val != m_currentValue) {
- m_currentValue = val;
- QMetaObject::invokeMethod(q_func(), "valueChanged", Qt::AutoConnection, Q_ARG(QVariant, val));
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuamonitoringparameters.cpp b/src/opcua/client/qopcuamonitoringparameters.cpp
new file mode 100644
index 0000000..c20d2b8
--- /dev/null
+++ b/src/opcua/client/qopcuamonitoringparameters.cpp
@@ -0,0 +1,390 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 basysKom GmbH, opensource@basyskom.com
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtOpcUa module of the Qt Toolkit.
+**
+** $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 "qopcuamonitoringparameters.h"
+#include "private/qopcuamonitoringparameters_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \enum QOpcUaMonitoringParameters::MonitoringMode
+
+ This enum is used to set the monitoring mode for a monitored item.
+
+ \value Disabled Sampling is disabled and no notifications are being generated.
+ \value Sampling Sampling is enabled and notifications are generated and queued, but reporting is disabled.
+ \value Reporting Sampling is enabled, notifications are generated and queued, reporting is enabled.
+*/
+
+/*!
+ \enum QOpcUaMonitoringParameters::SubscriptionType
+
+ \value Shared Share subscription with other monitored items with the same interval
+ \value Exclusive Request a new subscription for this attribute
+*/
+
+/*!
+ \class QOpcUaMonitoringParameters
+
+ This struct contains parameters for the creation of the subscription and monitored items for
+ a enableMonitoring request.
+
+ The same struct is used as return value for monitoringStatus and contains
+ the revised values from the server.
+*/
+
+/*!
+ \enum QOpcUaMonitoringParameters::Parameter
+
+ Enumerates parameters that can be modified at runtime using \l modifyMonitoring().
+ Not all values are guaranteed to be supported by all plugins. Lack of support will be reported
+ in the \l monitoringStatusChanged signal.
+
+ \value PublishingEnabled
+ \value PublishingInterval
+ \value LifetimeCount
+ \value MaxKeepAliveCount
+ \value MaxNotificationsPerPublish
+ \value Priority
+ \value SamplingInterval
+ \value Filter
+ \value QueueSize
+ \value DiscardOldest
+ \value MonitoringMode
+*/
+
+/*!
+ \class QOpcUaMonitoringParameters::DataChangeFilter
+
+ This struct is used to set up filtering for a DataChange monitored item.
+ It is defined in OPC-UA part 4, 7.12.2.
+*/
+
+/*!
+ \variable QOpcUaMonitoringParameters::DataChangeFilter::trigger
+
+ The trigger for this filter.
+*/
+
+/*!
+ \variable QOpcUaMonitoringParameters::DataChangeFilter::deadbandType
+
+ The deadband type for this filter.
+*/
+
+/*!
+ \variable QOpcUaMonitoringParameters::DataChangeFilter::deadbandValue
+
+ The deadband value for this filter.
+*/
+
+/*!
+ \enum QOpcUaMonitoringParameters::DataChangeFilter::DataChangeTrigger
+
+ Enumerates the possible triggers for a \l DataChangeFilter.
+
+ \value Status Triggers if the status code for the value changes.
+ \value StatusValue Triggers if the status code or the value changes.
+ \value StatusValueTimestamp Triggers if the status code, the value or the source timestamp changes.
+*/
+
+/*!
+ \enum QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType
+
+ Enumerates the possible deadband types for a \l DataChangeFilter.
+
+ \value None No deadband filtering.
+ \value Absolute A notification is generated if the absolute value of the difference between the last cached value
+ and the current value is greater than the deadband value.
+ \value Percent Only valid for AnalogItems with an EURange property. A notification is generated if the absolute value
+ of the difference between the last cached value and the current value is greater than value percent of the EURange.
+*/
+
+QOpcUaMonitoringParameters::QOpcUaMonitoringParameters()
+ : d_ptr(new QOpcUaMonitoringParametersPrivate())
+{}
+
+QOpcUaMonitoringParameters::~QOpcUaMonitoringParameters()
+{}
+
+/*!
+ This is the constructor which covers most use cases for the QtOpcUa user.
+ \a publishingInterval must be supplied, \a shared and \a subscriptionId are optional.
+*/
+QOpcUaMonitoringParameters::QOpcUaMonitoringParameters(double publishingInterval, QOpcUaMonitoringParameters::SubscriptionType shared, quint32 subscriptionId)
+ : d_ptr(new QOpcUaMonitoringParametersPrivate)
+{
+ d_ptr->publishingInterval = publishingInterval;
+ d_ptr->shared = shared;
+ d_ptr->subscriptionId = subscriptionId;
+
+}
+
+QOpcUaMonitoringParameters::QOpcUaMonitoringParameters(const QOpcUaMonitoringParameters &other)
+ : d_ptr(other.d_ptr)
+{}
+
+QOpcUaMonitoringParameters &QOpcUaMonitoringParameters::operator=(const QOpcUaMonitoringParameters &other)
+{
+ d_ptr = other.d_ptr;
+ return *this;
+}
+
+/*!
+ Returns the subscription type.
+*/
+QOpcUaMonitoringParameters::SubscriptionType QOpcUaMonitoringParameters::shared() const
+{
+ return d_ptr->shared;
+}
+
+/*!
+ Request \a shared as subscription type for the subscription.
+*/
+void QOpcUaMonitoringParameters::setShared(SubscriptionType shared)
+{
+ d_ptr->shared = shared;
+}
+
+/*!
+ Returns the status code of the monitored item creation.
+*/
+QOpcUa::UaStatusCode QOpcUaMonitoringParameters::statusCode() const
+{
+ return d_ptr->statusCode;
+}
+
+/*!
+ Set the status code to \a statusCode.
+*/
+void QOpcUaMonitoringParameters::setStatusCode(QOpcUa::UaStatusCode statusCode)
+{
+ d_ptr->statusCode = statusCode;
+}
+
+/*!
+ Returns the publishing mode for the subscription.
+*/
+bool QOpcUaMonitoringParameters::publishingEnabled() const
+{
+ return d_ptr->publishingEnabled;
+}
+
+/*!
+ Set \a publishingEnabled as publishing mode for the subscription.
+*/
+void QOpcUaMonitoringParameters::setPublishingEnabled(bool publishingEnabled)
+{
+ d_ptr->publishingEnabled = publishingEnabled;
+}
+
+/*!
+ Returns the priority of the subscription used for the monitored item.
+*/
+quint8 QOpcUaMonitoringParameters::priority() const
+{
+ return d_ptr->priority;
+}
+
+/*!
+ Set \a priority as priority for the subscription.
+*/
+void QOpcUaMonitoringParameters::setPriority(quint8 priority)
+{
+ d_ptr->priority = priority;
+}
+
+/*!
+ Returns the maximum notifications per publish value of the subscription.
+*/
+quint32 QOpcUaMonitoringParameters::maxNotificationsPerPublish() const
+{
+ return d_ptr->maxNotificationsPerPublish;
+}
+
+/*!
+ Request \a maxNotificationsPerPublish as maximum notifications per publish value for the subscription.
+*/
+void QOpcUaMonitoringParameters::setMaxNotificationsPerPublish(quint32 maxNotificationsPerPublish)
+{
+ d_ptr->maxNotificationsPerPublish = maxNotificationsPerPublish;
+}
+
+/*!
+ Returns the maximum keepalive count of the subscription.
+*/
+quint32 QOpcUaMonitoringParameters::maxKeepAliveCount() const
+{
+ return d_ptr->maxKeepAliveCount;
+}
+
+/*!
+ Request \a maxKeepAliveCount as maximum keepalive count for the subscription.
+*/
+void QOpcUaMonitoringParameters::setMaxKeepAliveCount(quint32 maxKeepAliveCount)
+{
+ d_ptr->maxKeepAliveCount = maxKeepAliveCount;
+}
+
+/*!
+ Returns the lifetime count of the subscription.
+*/
+quint32 QOpcUaMonitoringParameters::lifetimeCount() const
+{
+ return d_ptr->lifetimeCount;
+}
+
+/*!
+ Request \a lifetimeCount as lifetime count for the subscription.
+*/
+void QOpcUaMonitoringParameters::setLifetimeCount(quint32 lifetimeCount)
+{
+ d_ptr->lifetimeCount = lifetimeCount;
+}
+
+/*!
+ Returns the publishing interval of the subscription.
+*/
+double QOpcUaMonitoringParameters::publishingInterval() const
+{
+ return d_ptr->publishingInterval;
+}
+
+/*!
+ Request \a publishingInterval as publishing interval for the subscription.
+*/
+void QOpcUaMonitoringParameters::setPublishingInterval(double publishingInterval)
+{
+ d_ptr->publishingInterval = publishingInterval;
+}
+
+/*!
+ Returns the assigned subscription id.
+*/
+quint32 QOpcUaMonitoringParameters::subscriptionId() const
+{
+ return d_ptr->subscriptionId;
+}
+
+/*!
+ Request the monitored items to be created on a known subscription with subscriptionId \a value.
+*/
+void QOpcUaMonitoringParameters::setSubscriptionId(quint32 subscriptionId)
+{
+ d_ptr->subscriptionId = subscriptionId;
+}
+
+/*!
+ Returns the monitoring mode for the monitored item.
+*/
+QOpcUaMonitoringParameters::MonitoringMode QOpcUaMonitoringParameters::monitoringMode() const
+{
+ return d_ptr->monitoringMode;
+}
+
+/*!
+ Request \a monitoringMode as monitoring mode for the monitored item.
+*/
+void QOpcUaMonitoringParameters::setMonitoringMode(MonitoringMode monitoringMode)
+{
+ d_ptr->monitoringMode = monitoringMode;
+}
+
+/*!
+ Returns the discardOldest setting of the monitored item.
+*/
+bool QOpcUaMonitoringParameters::discardOldest() const
+{
+ return d_ptr->discardOldest;
+}
+
+/*!
+ Request \a discardOldest as discardOldest setting for the monitored item.
+*/
+void QOpcUaMonitoringParameters::setDiscardOldest(bool discardOldest)
+{
+ d_ptr->discardOldest = discardOldest;
+}
+
+/*!
+ Returns the queue size of the monitored item.
+*/
+quint32 QOpcUaMonitoringParameters::queueSize() const
+{
+ return d_ptr->queueSize;
+}
+
+/*!
+ Request \a queueSize as queue size for the monitored item.
+*/
+void QOpcUaMonitoringParameters::setQueueSize(quint32 queueSize)
+{
+ d_ptr->queueSize = queueSize;
+}
+
+/*!
+ Returns the filter result. Empty for DataChangeFilter.
+*/
+QVariant QOpcUaMonitoringParameters::filter() const
+{
+ return d_ptr->filter;
+}
+
+/*!
+ Request \a filter as filter for for the monitored item.
+*/
+void QOpcUaMonitoringParameters::setFilter(const QVariant &filter)
+{
+ d_ptr->filter = filter;
+}
+
+/*!
+ Returns the revised sampling interval of the monitored item.
+*/
+double QOpcUaMonitoringParameters::samplingInterval() const
+{
+ return d_ptr->samplingInterval;
+}
+
+/*!
+ Request \a samplingInterval as sampling interval for the monitored item.
+*/
+void QOpcUaMonitoringParameters::setSamplingInterval(double samplingInterval)
+{
+ d_ptr->samplingInterval = samplingInterval;
+}
+
+QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuamonitoringparameters.h b/src/opcua/client/qopcuamonitoringparameters.h
new file mode 100644
index 0000000..422726a
--- /dev/null
+++ b/src/opcua/client/qopcuamonitoringparameters.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 basysKom GmbH, opensource@basyskom.com
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtOpcUa module of the Qt Toolkit.
+**
+** $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 QOPCUAMONITORINGPARAMETERS_H
+#define QOPCUAMONITORINGPARAMETERS_H
+
+#include <QtOpcUa/qopcuatype.h>
+
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOpcUaMonitoringParametersPrivate;
+
+class Q_OPCUA_EXPORT QOpcUaMonitoringParameters
+{
+ Q_GADGET
+
+public:
+
+ enum class MonitoringMode {
+ Disabled = 0,
+ Sampling = 1,
+ Reporting = 2
+ };
+
+ enum class SubscriptionType {
+ Shared,
+ Exclusive
+ };
+
+ enum class Parameter {
+ PublishingEnabled = (1 << 0),
+ PublishingInterval = (1 << 1),
+ LifetimeCount = (1 << 2),
+ MaxKeepAliveCount = (1 << 3),
+ MaxNotificationsPerPublish = (1 << 4),
+ Priority = (1 << 5),
+ SamplingInterval = (1 << 6),
+ Filter = (1 << 7),
+ QueueSize = (1 << 8),
+ DiscardOldest = (1 << 9),
+ MonitoringMode = (1 << 10)
+ };
+ Q_ENUM(Parameter)
+ Q_DECLARE_FLAGS(Parameters, Parameter)
+
+ // This type and the enums are defined in OPC-UA part 4, 7.12.2
+ struct DataChangeFilter {
+ enum class DataChangeTrigger {
+ Status = 0,
+ StatusValue = 1,
+ StatusValueTimestamp = 3
+ };
+
+ enum class DeadbandType {
+ None = 0,
+ Absolute = 1,
+ Percent = 2
+ };
+
+ DataChangeTrigger trigger;
+ DeadbandType deadbandType;
+ double deadbandValue;
+
+ DataChangeFilter()
+ : trigger(DataChangeTrigger::Status)
+ , deadbandType(DeadbandType::None)
+ , deadbandValue(0)
+ {}
+ DataChangeFilter(DataChangeTrigger p_trigger, DeadbandType p_deadbandType, double p_deadbandValue)
+ : trigger(p_trigger)
+ , deadbandType(p_deadbandType)
+ , deadbandValue(p_deadbandValue)
+ {}
+ };
+
+ QOpcUaMonitoringParameters();
+ ~QOpcUaMonitoringParameters();
+ QOpcUaMonitoringParameters(double publishingInterval, SubscriptionType shared = SubscriptionType::Shared, quint32 subscriptionId = 0);
+ QOpcUaMonitoringParameters(const QOpcUaMonitoringParameters &other);
+ QOpcUaMonitoringParameters &operator=(const QOpcUaMonitoringParameters &other);
+
+ double samplingInterval() const;
+ void setSamplingInterval(double samplingInterval);
+ QVariant filter() const;
+ void setFilter(const QVariant &filter);
+ quint32 queueSize() const;
+ void setQueueSize(quint32 queueSize);
+ bool discardOldest() const;
+ void setDiscardOldest(bool discardOldest);
+ QOpcUaMonitoringParameters::MonitoringMode monitoringMode() const;
+ void setMonitoringMode(MonitoringMode monitoringMode);
+ quint32 subscriptionId() const;
+ void setSubscriptionId(quint32 subscriptionId);
+ double publishingInterval() const;
+ void setPublishingInterval(double publishingInterval);
+ quint32 lifetimeCount() const;
+ void setLifetimeCount(quint32 lifetimeCount);
+ quint32 maxKeepAliveCount() const;
+ void setMaxKeepAliveCount(quint32 maxKeepAliveCount);
+ quint32 maxNotificationsPerPublish() const;
+ void setMaxNotificationsPerPublish(quint32 maxNotificationsPerPublish);
+ quint8 priority() const;
+ void setPriority(quint8 priority);
+ bool publishingEnabled() const;
+ void setPublishingEnabled(bool publishingEnabled);
+ QOpcUa::UaStatusCode statusCode() const;
+ void setStatusCode(QOpcUa::UaStatusCode statusCode);
+ QOpcUaMonitoringParameters::SubscriptionType shared() const;
+ void setShared(SubscriptionType subscriptionType);
+
+private:
+ QSharedDataPointer<QOpcUaMonitoringParametersPrivate> d_ptr;
+};
+
+Q_DECLARE_TYPEINFO(QOpcUaMonitoringParameters::SubscriptionType, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QOpcUaMonitoringParameters::DataChangeFilter::DataChangeTrigger, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType, Q_PRIMITIVE_TYPE);
+Q_DECLARE_OPERATORS_FOR_FLAGS(QOpcUaMonitoringParameters::Parameters)
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QOpcUaMonitoringParameters)
+Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::SubscriptionType)
+Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::DataChangeFilter)
+Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::DataChangeFilter::DataChangeTrigger)
+Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType)
+Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::Parameter)
+Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::Parameters)
+
+#endif // QOPCUAMONITORINGPARAMETERS_H
diff --git a/src/opcua/client/qopcuasubscription_p.h b/src/opcua/client/qopcuamonitoringparameters_p.h
index f509c10..5b6e824 100644
--- a/src/opcua/client/qopcuasubscription_p.h
+++ b/src/opcua/client/qopcuamonitoringparameters_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com
+** Copyright (C) 2017 basysKom GmbH, opensource@basyskom.com
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtOpcUa module of the Qt Toolkit.
@@ -34,8 +34,8 @@
**
****************************************************************************/
-#ifndef QOPCUASUBSCRIPTION_P_H
-#define QOPCUASUBSCRIPTION_P_H
+#ifndef QOPCUAMONITORINGPARAMETERS_P_H
+#define QOPCUAMONITORINGPARAMETERS_P_H
//
// W A R N I N G
@@ -48,27 +48,53 @@
// We mean it.
//
-#include <QtOpcUa/qopcuaglobal.h>
-#include <QtOpcUa/qopcuasubscription.h>
-#include <private/qopcuasubscriptionimpl_p.h>
+#include <QtOpcUa/qopcuamonitoringparameters.h>
-#include <private/qobject_p.h>
-#include <QtCore/qscopedpointer.h>
+#include <QtCore/qshareddata.h>
QT_BEGIN_NAMESPACE
-class Q_OPCUA_EXPORT QOpcUaSubscriptionPrivate : public QObjectPrivate
+class Q_OPCUA_EXPORT QOpcUaMonitoringParametersPrivate : public QSharedData
{
public:
- Q_DECLARE_PUBLIC(QOpcUaSubscription)
- QOpcUaSubscriptionPrivate(QOpcUaSubscriptionImpl *impl, quint32 interval);
- ~QOpcUaSubscriptionPrivate();
+ QOpcUaMonitoringParametersPrivate()
+ : samplingInterval(0)
+ , queueSize(0)
+ , discardOldest(true)
+ , monitoringMode(QOpcUaMonitoringParameters::MonitoringMode::Reporting)
+ , subscriptionId(0)
+ , publishingInterval(0)
+ , lifetimeCount(0)
+ , maxKeepAliveCount(0)
+ , maxNotificationsPerPublish(0)
+ , priority(0)
+ , publishingEnabled(true)
+ , statusCode(QOpcUa::UaStatusCode::BadAttributeIdInvalid)
+ , shared(QOpcUaMonitoringParameters::SubscriptionType::Shared)
+ {}
- QScopedPointer<QOpcUaSubscriptionImpl> m_impl;
- quint32 m_interval;
+ // MonitoredItem
+ double samplingInterval;
+ QVariant filter;
+ quint32 queueSize;
+ bool discardOldest;
+ QOpcUaMonitoringParameters::MonitoringMode monitoringMode;
+
+ // Subscription
+ quint32 subscriptionId;
+ double publishingInterval;
+ quint32 lifetimeCount;
+ quint32 maxKeepAliveCount;
+ quint32 maxNotificationsPerPublish;
+ quint8 priority;
+ bool publishingEnabled;
+
+ // QtOpcUa specific
+ QOpcUa::UaStatusCode statusCode;
+ QOpcUaMonitoringParameters::SubscriptionType shared;
};
QT_END_NAMESPACE
-#endif // QOPCUASUBSCRIPTION_P_H
+#endif // QOPCUAMONITORINGPARAMETERS_P_H
diff --git a/src/opcua/client/qopcuanode.cpp b/src/opcua/client/qopcuanode.cpp
index d948365..e02d0aa 100644
--- a/src/opcua/client/qopcuanode.cpp
+++ b/src/opcua/client/qopcuanode.cpp
@@ -35,12 +35,9 @@
****************************************************************************/
#include "qopcuaclient.h"
-#include "qopcuamonitoredvalue.h"
#include "qopcuanode.h"
#include <private/qopcuaclient_p.h>
#include <private/qopcuaclientimpl_p.h>
-#include <private/qopcuamonitoredevent_p.h>
-#include <private/qopcuamonitoredvalue_p.h>
#include <private/qopcuanode_p.h>
#include <private/qopcuanodeimpl_p.h>
@@ -80,11 +77,6 @@ QT_BEGIN_NAMESPACE
event we want to keep up with to the subscription.
\image subscriptions.png
-
- After calling valueMonitor() or eventMonitor(), a QOpcUaMonitoredValue or
- QOpcUaMonitoredEvent is returned.
- These objects emit a signal on a data change or event,
- depending on which type of subscription has been requested from the server.
*/
/*!
@@ -121,7 +113,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QOpcUaNode::attributeWritten(QOpcUaNode::NodeAttribute attribute, quint32 statusCode)
+ \fn QOpcUaNode::attributeWritten(QOpcUaNode::NodeAttribute attribute, QOpcUa::UaStatusCode statusCode)
This signal is emitted after a \l writeAttribute() or \l writeAttributes() operation has finished.
@@ -130,6 +122,29 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn void enableMonitoringFinished(QOpcUaNode::NodeAttribute attr, QOpcUa::UaStatusCode statusCode)
+
+ This signal is emitted after an asynchronous call to \l enableMonitoring() has finished.
+ After this signal has been emitted, \l monitoringStatus() returns valid information for \a attr.
+*/
+
+/*!
+ \fn void disableMonitoringFinished(QOpcUaNode::NodeAttribute attr, QOpcUa::UaStatusCode statusCode)
+
+ This signal is emitted after an asynchronous call to \l disableMonitoring() has finished.
+ After this signal has been emitted, monitoringStatus returns a default constructed value with
+ status code BadMonitoredItemIdIinvalid for \a attr.
+*/
+
+/*!
+ \fn void QOpcUaNode::monitoringStatusChanged(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value, QOpcUa::UaStatusCode statusCode);
+
+ This signal is emitted after an asynchronous call to \l modifyMonitoring() has finished.
+ The node attribute for which the operation was requested is returned in \a attr. \a item contains the parameters that have been modified.
+ \a statusCode contains the result of the modify operation on the server.
+*/
+
+/*!
\internal QOpcUaNodeImpl is an opaque type (as seen from the public API).
This prevents users of the public API to use this constructor (eventhough
it is public).
@@ -244,6 +259,72 @@ QOpcUa::UaStatusCode QOpcUaNode::attributeError(QOpcUaNode::NodeAttribute attrib
\li Guid
\endtable
*/
+
+/*!
+ This method creates a monitored item for each of the attributes given in \a attr.
+ The settings from \a settings are used in the creation of the monitored items and the subscription.
+
+ The \a shared field of \a settings controls how the monitored items are assigned to a subscription. If shared in \a settings is set to Shared,
+ the monitored item will be added to a subscription with a matching \a interval. If shared in \a settings is set to Exclusive,
+ either a new subscription is created, or if a subscription is provided via the subscriptionId field in \a settings, an existing subscription is used.
+ This can be used to group monitored items on a subscription manually by creating the first monitored item with \a shared=false and
+ using the subscriptionId returned in \a subscriptionId of monitoringStatus(\a attr) in the following requests.
+
+ Returns true if the asynchronous call has been successfully dispatched.
+
+ On the completion of the call, the \l enableMonitoringFinished signal is emitted.
+ There are multiple error cases in which a Bad status code is generated: A subscription with \a subscriptionId does not exist,
+ the node does not exist on the server, the node does not have the requested attribute or the maximum number of monitored items for
+ the server is reached.
+ */
+bool QOpcUaNode::enableMonitoring(QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
+{
+ if (d_func()->m_client.isNull() || d_func()->m_client->state() != QOpcUaClient::Connected)
+ return false;
+
+ return d_func()->m_impl->enableMonitoring(attr, settings);
+}
+
+/*!
+ This method modifies settings of the monitored item or the subscription.
+ The parameter \a item of the monitored item or subscription associated with \a attr is attempted to set to \a value.
+
+ Returns true if the asynchronous call has been successfully dispatched.
+
+ After the call has finished, the monitoringStatusChanged signal is emitted. This signal contains the modified parameters and the status code.
+ A Bad status code is generated if there is no monitored item associated with the requested attribute, revising the requested
+ parameter is not implemented or if the server has rejected the requested value.
+*/
+bool QOpcUaNode::modifyMonitoring(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value)
+{
+ if (d_func()->m_client.isNull() || d_func()->m_client->state() != QOpcUaClient::Connected)
+ return false;
+
+ return d_func()->m_impl->modifyMonitoring(attr, item, value);
+}
+
+/*!
+ Returns the monitoring parameters associated with the attribute \a attr. This can be used to check the success of \l enableMonitoring()
+ or if parameters have been revised.
+ The returned values are only valid after \l enableMonitoringFinished or \l monitoringStatusChanged have been emitted for \a attr.
+*/
+QOpcUaMonitoringParameters QOpcUaNode::monitoringStatus(QOpcUaNode::NodeAttribute attr)
+{
+ auto it = d_func()->m_monitoringStatus.constFind(attr);
+ if (it == d_func()->m_monitoringStatus.constEnd()) {
+ QOpcUaMonitoringParameters p;
+ p.setStatusCode(QOpcUa::UaStatusCode::BadAttributeIdInvalid);
+ return p;
+ }
+
+ return *it;
+}
+
+/*!
+ Writes \value to the attribute given in \a attribute using the type information from \a type.
+
+ Returns true if the write operation has been dispatched successfully.
+ */
bool QOpcUaNode::writeAttribute(QOpcUaNode::NodeAttribute attribute, const QVariant &value, QOpcUa::Types type)
{
if (d_func()->m_client.isNull() || d_func()->m_client->state() != QOpcUaClient::Connected)
@@ -269,6 +350,22 @@ bool QOpcUaNode::writeAttributes(const AttributeMap &toWrite, QOpcUa::Types valu
}
/*!
+ This method disables monitoring for the attributes given in \a attr.
+
+ Returns true if the asynchronous call has been successfully dispatched.
+
+ After the call is finished, the \l disableMonitoringFinished signal is emitted and monitoringStatus returns a default constructed value with
+ status code BadMonitoredItemIdIinvalid for \a attr.
+*/
+bool QOpcUaNode::disableMonitoring(QOpcUaNode::NodeAttributes attr)
+{
+ if (d_func()->m_client.isNull() || d_func()->m_client->state() != QOpcUaClient::Connected)
+ return false;
+
+ return d_func()->m_impl->disableMonitoring(attr);
+}
+
+/*!
QStringList filled with the node IDs of all child nodes of the OPC UA node.
*/
QStringList QOpcUaNode::childrenIds() const
diff --git a/src/opcua/client/qopcuanode.h b/src/opcua/client/qopcuanode.h
index b2e3d13..cc0f889 100644
--- a/src/opcua/client/qopcuanode.h
+++ b/src/opcua/client/qopcuanode.h
@@ -38,6 +38,7 @@
#define QOPCUANODE_H
#include <QtOpcUa/qopcuaglobal.h>
+#include <QtOpcUa/qopcuamonitoringparameters.h>
#include <QtOpcUa/qopcuatype.h>
#include <QtCore/qdatetime.h>
@@ -50,8 +51,6 @@ QT_BEGIN_NAMESPACE
class QOpcUaNodePrivate;
class QOpcUaNodeImpl;
class QOpcUaClient;
-class QOpcUaMonitoredEvent;
-class QOpcUaMonitoredValue;
class Q_OPCUA_EXPORT QOpcUaNode : public QObject
{
@@ -119,6 +118,11 @@ public:
bool writeAttribute(QOpcUaNode::NodeAttribute attribute, const QVariant &value, QOpcUa::Types type = QOpcUa::Types::Undefined);
bool writeAttributes(const AttributeMap &toWrite, QOpcUa::Types valueAttributeType = QOpcUa::Types::Undefined);
+ bool enableMonitoring(QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings);
+ bool disableMonitoring(QOpcUaNode::NodeAttributes attr);
+ bool modifyMonitoring(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value);
+ QOpcUaMonitoringParameters monitoringStatus(QOpcUaNode::NodeAttribute attr);
+
QStringList childrenIds() const;
QString nodeId() const;
@@ -131,6 +135,12 @@ public:
Q_SIGNALS:
void readFinished(QOpcUaNode::NodeAttributes attributes);
void attributeWritten(QOpcUaNode::NodeAttribute attribute, QOpcUa::UaStatusCode statusCode);
+ void attributeUpdated(QOpcUaNode::NodeAttribute attr, QVariant value);
+
+ void monitoringStatusChanged(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items,
+ QOpcUa::UaStatusCode statusCode);
+ void enableMonitoringFinished(QOpcUaNode::NodeAttribute attr, QOpcUa::UaStatusCode statusCode);
+ void disableMonitoringFinished(QOpcUaNode::NodeAttribute attr, QOpcUa::UaStatusCode statusCode);
private:
Q_DISABLE_COPY(QOpcUaNode)
diff --git a/src/opcua/client/qopcuanode_p.h b/src/opcua/client/qopcuanode_p.h
index bd57fb5..53f2996 100644
--- a/src/opcua/client/qopcuanode_p.h
+++ b/src/opcua/client/qopcuanode_p.h
@@ -94,12 +94,67 @@ public:
emit q_func()->attributeWritten(attr, statusCode);
});
+
+ m_attributeUpdatedConnection = QObject::connect(impl, &QOpcUaNodeImpl::attributeUpdated,
+ [this](QOpcUaNode::NodeAttribute attr, QVariant value)
+ {
+ this->m_nodeAttributes[attr] = {value, QOpcUa::UaStatusCode::Good};
+ emit q_func()->attributeUpdated(attr, value);
+ });
+
+ m_monitoringEnableDisableConnection = QObject::connect(impl, &QOpcUaNodeImpl::monitoringEnableDisable,
+ [this](QOpcUaNode::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status)
+ {
+ if (subscribe == true) {
+ m_monitoringStatus[attr] = status;
+ emit q_func()->enableMonitoringFinished(attr, status.statusCode());
+ }
+ else {
+ m_monitoringStatus.remove(attr);
+ emit q_func()->disableMonitoringFinished(attr, status.statusCode());
+ }
+ });
+
+ m_monitoringStatusChangedConnection = QObject::connect(impl, &QOpcUaNodeImpl::monitoringStatusChanged,
+ [this](QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items, QOpcUaMonitoringParameters param)
+ {
+ auto it = m_monitoringStatus.find(attr);
+ if (param.statusCode() == QOpcUa::UaStatusCode::Good && it != m_monitoringStatus.end()) {
+ if (items & QOpcUaMonitoringParameters::Parameter::PublishingEnabled)
+ it->setPublishingEnabled(param.publishingEnabled());
+ if (items & QOpcUaMonitoringParameters::Parameter::PublishingInterval)
+ it->setPublishingInterval(param.publishingInterval());
+ if (items & QOpcUaMonitoringParameters::Parameter::LifetimeCount)
+ it->setLifetimeCount(param.lifetimeCount());
+ if (items & QOpcUaMonitoringParameters::Parameter::MaxKeepAliveCount)
+ it->setMaxKeepAliveCount(param.maxKeepAliveCount());
+ if (items & QOpcUaMonitoringParameters::Parameter::MaxNotificationsPerPublish)
+ it->setMaxNotificationsPerPublish(param.maxNotificationsPerPublish());
+ if (items & QOpcUaMonitoringParameters::Parameter::Priority)
+ it->setPriority(param.priority());
+ if (items & QOpcUaMonitoringParameters::Parameter::SamplingInterval)
+ it->setSamplingInterval(param.samplingInterval());
+ if (items & QOpcUaMonitoringParameters::Parameter::Filter)
+ it->setFilter(param.filter());
+ if (items & QOpcUaMonitoringParameters::Parameter::QueueSize)
+ it->setQueueSize(param.queueSize());
+ if (items & QOpcUaMonitoringParameters::Parameter::DiscardOldest)
+ it->setDiscardOldest(param.discardOldest());
+ if (items & QOpcUaMonitoringParameters::Parameter::MonitoringMode)
+ it->setMonitoringMode(param.monitoringMode());
+ }
+
+ emit q_func()->monitoringStatusChanged(attr, items, param.statusCode());
+ });
}
~QOpcUaNodePrivate()
{
QObject::disconnect(m_attributesReadConnection);
QObject::disconnect(m_attributeWrittenConnection);
+ QObject::disconnect(m_attributeUpdatedConnection);
+ QObject::disconnect(m_monitoringEnableDisableConnection);
+ QObject::disconnect(m_monitoringStatusChangedConnection);
}
QScopedPointer<QOpcUaNodeImpl> m_impl;
@@ -110,9 +165,13 @@ public:
QOpcUa::UaStatusCode statusCode;
};
QHash<QOpcUaNode::NodeAttribute, AttributeWithStatus> m_nodeAttributes;
+ QHash<QOpcUaNode::NodeAttribute, QOpcUaMonitoringParameters> m_monitoringStatus;
QMetaObject::Connection m_attributesReadConnection;
QMetaObject::Connection m_attributeWrittenConnection;
+ QMetaObject::Connection m_attributeUpdatedConnection;
+ QMetaObject::Connection m_monitoringEnableDisableConnection;
+ QMetaObject::Connection m_monitoringStatusChangedConnection;
};
QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuanodeimpl_p.h b/src/opcua/client/qopcuanodeimpl_p.h
index 585a025..ff49dc3 100644
--- a/src/opcua/client/qopcuanodeimpl_p.h
+++ b/src/opcua/client/qopcuanodeimpl_p.h
@@ -49,6 +49,7 @@
//
#include <QtOpcUa/qopcuaglobal.h>
+#include <QtOpcUa/qopcuamonitoringparameters.h>
#include <QtOpcUa/qopcuanode.h>
#include <QtOpcUa/qopcuatype.h>
@@ -56,9 +57,6 @@
QT_BEGIN_NAMESPACE
-class QOpcUaMonitoredEvent;
-class QOpcUaMonitoredValue;
-
struct QOpcUaReadResult {
QOpcUaNode::NodeAttribute attributeId;
QOpcUa::UaStatusCode statusCode;
@@ -73,11 +71,15 @@ public:
virtual ~QOpcUaNodeImpl();
virtual bool readAttributes(QOpcUaNode::NodeAttributes attr) = 0;
+ virtual bool enableMonitoring(QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings) = 0;
+ virtual bool disableMonitoring(QOpcUaNode::NodeAttributes attr) = 0;
virtual QStringList childrenIds() const = 0;
virtual QString nodeId() const = 0;
virtual bool writeAttribute(QOpcUaNode::NodeAttribute attribute, const QVariant &value, QOpcUa::Types type) = 0;
virtual bool writeAttributes(const QOpcUaNode::AttributeMap &toWrite, QOpcUa::Types valueAttributeType) = 0;
+ virtual bool modifyMonitoring(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item,
+ const QVariant &value) = 0;
virtual QPair<double, double> readEuRange() const = 0;
virtual QPair<QString, QString> readEui() const = 0;
@@ -89,6 +91,10 @@ Q_SIGNALS:
void attributesRead(QVector<QOpcUaReadResult> attr, QOpcUa::UaStatusCode serviceResult);
void attributeWritten(QOpcUaNode::NodeAttribute attr, QVariant value, QOpcUa::UaStatusCode statusCode);
+ void attributeUpdated(QOpcUaNode::NodeAttribute attr, QVariant value);
+ void monitoringEnableDisable(QOpcUaNode::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status);
+ void monitoringStatusChanged(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items,
+ QOpcUaMonitoringParameters param);
};
QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuasubscription.cpp b/src/opcua/client/qopcuasubscription.cpp
deleted file mode 100644
index f09a4a2..0000000
--- a/src/opcua/client/qopcuasubscription.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 "qopcuasubscription.h"
-#include <private/qopcuasubscription_p.h>
-#include <private/qopcuasubscriptionimpl_p.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QOpcUaSubscription
- \inmodule QtOpcUa
-
- \brief Enables the user to utilize the
- subscription mechanism in OPC UA for event subscriptions.
-*/
-
-/*!
- \internal
- */
-QOpcUaSubscription::QOpcUaSubscription(QOpcUaSubscriptionImpl *impl, quint32 interval, QObject *parent)
- : QObject(*new QOpcUaSubscriptionPrivate(impl, interval), parent)
-{
-}
-
-/*!
- \fn QOpcUaSubscription::~QOpcUaSubscription()
-
- Destroys this subscription instance. It will also destroy the subscrption on
- the server.
- */
-QOpcUaSubscription::~QOpcUaSubscription()
-{
-}
-
-/*!
- Create an event monitor for \a node by adding it to this subscription object.
-
- Returns a QOpcUaMonitoredEvent which can be used to receive a signal when an
- event occcurs.
-*/
-QOpcUaMonitoredEvent *QOpcUaSubscription::addEvent(QOpcUaNode *node)
-{
- return d_func()->m_impl->addEvent(node);
-}
-
-/*!
- Create a value monitor for \a node by adding it to this subscription object.
-
- Return a QOpcUaMonitoredEvent which can be used to receive a signal when
- the value changes.
- */
-QOpcUaMonitoredValue *QOpcUaSubscription::addValue(QOpcUaNode *node)
-{
- return d_func()->m_impl->addValue(node);
-}
-
-/*!
- Remove the monitored event represented by \a event from this subscription.
- */
-void QOpcUaSubscription::removeEvent(QOpcUaMonitoredEvent *e)
-{
- d_func()->m_impl->removeEvent(e);
-}
-
-/*!
- Remove the monitored value represented by \a value from this subscription.
- */
-void QOpcUaSubscription::removeValue(QOpcUaMonitoredValue *value)
-{
- d_func()->m_impl->removeValue(value);
-}
-
-QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuasubscription.h b/src/opcua/client/qopcuasubscription.h
deleted file mode 100644
index 2559a74..0000000
--- a/src/opcua/client/qopcuasubscription.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 QOPCUASUBSCRIPTION_H
-#define QOPCUASUBSCRIPTION_H
-
-#include <QtOpcUa/qopcuaglobal.h>
-
-#include <QtCore/qobject.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpcUaMonitoredEvent;
-class QOpcUaMonitoredValue;
-class QOpcUaNode;
-class QOpcUaSubscriptionImpl;
-class QOpcUaSubscriptionPrivate;
-
-class Q_OPCUA_EXPORT QOpcUaSubscription : public QObject
-{
- Q_OBJECT
- Q_DECLARE_PRIVATE(QOpcUaSubscription)
-public:
- QOpcUaSubscription(QOpcUaSubscriptionImpl *impl, quint32 interval, QObject *parent = nullptr);
- ~QOpcUaSubscription() override;
-
- QOpcUaMonitoredEvent *addEvent(QOpcUaNode *node);
- void removeEvent(QOpcUaMonitoredEvent *e);
-
- QOpcUaMonitoredValue *addValue(QOpcUaNode *node);
- void removeValue(QOpcUaMonitoredValue *value);
-private:
- Q_DISABLE_COPY(QOpcUaSubscription)
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPCUASUBSCRIPTION_H
diff --git a/src/opcua/client/qopcuasubscriptionimpl.cpp b/src/opcua/client/qopcuasubscriptionimpl.cpp
deleted file mode 100644
index 08b7eab..0000000
--- a/src/opcua/client/qopcuasubscriptionimpl.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 <private/qopcuasubscriptionimpl_p.h>
-
-QT_BEGIN_NAMESPACE
-
-QOpcUaSubscriptionImpl::QOpcUaSubscriptionImpl()
-{
-}
-
-QOpcUaSubscriptionImpl::~QOpcUaSubscriptionImpl()
-{
-}
-
-QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuasubscriptionimpl_p.h b/src/opcua/client/qopcuasubscriptionimpl_p.h
deleted file mode 100644
index 8c0a138..0000000
--- a/src/opcua/client/qopcuasubscriptionimpl_p.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 QOPCUASUBSCRIPTIONIMPL_P_H
-#define QOPCUASUBSCRIPTIONIMPL_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtOpcUa/qopcuaglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpcUaMonitoredEvent;
-class QOpcUaMonitoredValue;
-class QOpcUaNode;
-
-class Q_OPCUA_EXPORT QOpcUaSubscriptionImpl
-{
-public:
- QOpcUaSubscriptionImpl();
- virtual ~QOpcUaSubscriptionImpl();
-
- virtual QOpcUaMonitoredEvent *addEvent(QOpcUaNode *node) = 0;
- virtual void removeEvent(QOpcUaMonitoredEvent *event) = 0;
- virtual QOpcUaMonitoredValue *addValue(QOpcUaNode *node) = 0;
- virtual void removeValue(QOpcUaMonitoredValue *value) = 0;
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPCUASUBSCRIPTIONIMPL_P_H
diff --git a/src/opcua/client/qopcuasubscriptionprivate.cpp b/src/opcua/client/qopcuasubscriptionprivate.cpp
deleted file mode 100644
index 762d413..0000000
--- a/src/opcua/client/qopcuasubscriptionprivate.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 <private/qopcuasubscription_p.h>
-
-QT_BEGIN_NAMESPACE
-
-QOpcUaSubscriptionPrivate::QOpcUaSubscriptionPrivate(QOpcUaSubscriptionImpl *impl, quint32 interval)
- : m_impl(impl)
- , m_interval(interval)
-{
-
-}
-
-QOpcUaSubscriptionPrivate::~QOpcUaSubscriptionPrivate()
-{
-
-}
-
-QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuavaluesubscription.cpp b/src/opcua/client/qopcuavaluesubscription.cpp
deleted file mode 100644
index d48f772..0000000
--- a/src/opcua/client/qopcuavaluesubscription.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 "qopcuavaluesubscription.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \class QOpcUaValueSubscription
- \inmodule QtOpcUa
-
- \brief Enables the user to utilize the
- subscription mechanism in OPC UA for data change subscriptions.
-
- \note This class is not exposed to application developers.
-*/
-
-/*!
- Creates an empty QOpcUaValueSubscription object with interval \a interval
- */
-QOpcUaValueSubscription::QOpcUaValueSubscription(uint interval)
- : QOpcUaSubscription()
- , d_ptr(0) // new QOpcUaValueSubscriptionPrivate(this))
-{
- m_interval = interval;
-}
-
-/*!
- Return the interval of the subscription.
- */
-uint QOpcUaValueSubscription::interval() const
-{
- return m_interval;
-}
-
-/*!
- \fn QOpcUaMonitoredValue QOpcUaValueSubscription::addValue(const QString &nodeId)
-
- Adds the node with \a nodeId to this subscription.
-
- Must be reimplemented in the concrete plugin implementation.
-*/
-
-QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuavaluesubscription.h b/src/opcua/client/qopcuavaluesubscription.h
deleted file mode 100644
index 95d2873..0000000
--- a/src/opcua/client/qopcuavaluesubscription.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the QtOpcUa module of the Qt Toolkit.
-**
-** $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 QOPCUAVALUESUBSCRIPTION_H
-#define QOPCUAVALUESUBSCRIPTION_H
-
-#include <QtOpcUa/qopcuasubscription.h>
-
-QT_BEGIN_NAMESPACE
-
-class QOpcUaMonitoredValue;
-class QOpcUaValueSubscriptionPrivate;
-
-class Q_OPCUA_EXPORT QOpcUaValueSubscription : public QOpcUaSubscription
-{
-public:
- ~QOpcUaValueSubscription() override = default;
-
- virtual QOpcUaMonitoredValue *addValue(const QString &nodeId) = 0;
-
- uint interval() const;
-
-protected:
- explicit QOpcUaValueSubscription(uint interval);
-
- uint m_interval;
-
-private:
- Q_DECLARE_PRIVATE(QOpcUaValueSubscription)
-};
-
-QT_END_NAMESPACE
-
-#endif // QOPCUAVALUESUBSCRIPTION_H
diff --git a/src/opcua/core/qopcuaprovider.cpp b/src/opcua/core/qopcuaprovider.cpp
index da33774..c0c95fc 100644
--- a/src/opcua/core/qopcuaprovider.cpp
+++ b/src/opcua/core/qopcuaprovider.cpp
@@ -116,6 +116,10 @@ QOpcUaProvider::QOpcUaProvider(QObject *parent)
qRegisterMetaType<QOpcUaClient::ClientState>();
qRegisterMetaType<QOpcUaClient::ClientError>();
qRegisterMetaType<uintptr_t>("uintptr_t");
+ qRegisterMetaType<QOpcUaMonitoringParameters::SubscriptionType>();
+ qRegisterMetaType<QOpcUaMonitoringParameters::Parameter>();
+ qRegisterMetaType<QOpcUaMonitoringParameters::Parameters>();
+ qRegisterMetaType<QOpcUaMonitoringParameters>();
}
QOpcUaProvider::~QOpcUaProvider()
diff --git a/src/opcua/doc/src/qtopcua.qdoc b/src/opcua/doc/src/qtopcua.qdoc
index 171b1e7..7f53361 100644
--- a/src/opcua/doc/src/qtopcua.qdoc
+++ b/src/opcua/doc/src/qtopcua.qdoc
@@ -187,11 +187,9 @@
\endtable
\section1 Classes and ownership
- Four important classes are exposed to the user: QOpcUaClient, QOpcUaNode,
- QOpcUaMonitoredEvent and QOpcUaMonitoredValue.
+ Two important classes are exposed to the user: QOpcUaClient and QOpcUaNode.
- Objects of the types QOpcUaNode, QOpcUaMonitoredEvent and QOpcUaMonitoredValue
- are owned by the user and must be deleted when they are no longer needed.
+ Objects of the type QOpcUaNode are owned by the user and must be deleted when they are no longer needed.
\section1 Related Information
diff --git a/src/plugins/opcua/freeopcua/qfreeopcuaclient.cpp b/src/plugins/opcua/freeopcua/qfreeopcuaclient.cpp
index c0cc2ff..31b9134 100644
--- a/src/plugins/opcua/freeopcua/qfreeopcuaclient.cpp
+++ b/src/plugins/opcua/freeopcua/qfreeopcuaclient.cpp
@@ -37,7 +37,6 @@
#include "qfreeopcuaclient.h"
#include "qfreeopcuanode.h"
#include "qfreeopcuaworker.h"
-#include <QtOpcUa/qopcuasubscription.h>
#include <private/qopcuaclient_p.h>
#include <QtCore/qloggingcategory.h>
@@ -95,14 +94,4 @@ QOpcUaNode *QFreeOpcUaClientImpl::node(const QString &nodeId)
}
}
-QOpcUaSubscription *QFreeOpcUaClientImpl::createSubscription(quint32 interval)
-{
- QOpcUaSubscription *result;
- QMetaObject::invokeMethod(m_opcuaWorker, "createSubscription",
- Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(QOpcUaSubscription *, result),
- Q_ARG(quint32, interval));
- return result;
-}
-
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/freeopcua/qfreeopcuaclient.h b/src/plugins/opcua/freeopcua/qfreeopcuaclient.h
index f477dde..47423f1 100644
--- a/src/plugins/opcua/freeopcua/qfreeopcuaclient.h
+++ b/src/plugins/opcua/freeopcua/qfreeopcuaclient.h
@@ -63,8 +63,6 @@ public:
bool isSecureConnectionSupported() const override { return false; }
QString backend() const override { return QStringLiteral("freeopcua"); }
- QOpcUaSubscription *createSubscription(quint32 interval) override;
-
QFreeOpcUaWorker *m_opcuaWorker{};
private:
diff --git a/src/plugins/opcua/freeopcua/qfreeopcuanode.cpp b/src/plugins/opcua/freeopcua/qfreeopcuanode.cpp
index 82046fc..f4a07cf 100644
--- a/src/plugins/opcua/freeopcua/qfreeopcuanode.cpp
+++ b/src/plugins/opcua/freeopcua/qfreeopcuanode.cpp
@@ -39,8 +39,6 @@
#include "qfreeopcuasubscription.h"
#include "qfreeopcuavalueconverter.h"
#include "qfreeopcuaworker.h"
-#include <QtOpcUa/qopcuamonitoredevent.h>
-#include <QtOpcUa/qopcuamonitoredvalue.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qloggingcategory.h>
@@ -73,6 +71,34 @@ bool QFreeOpcUaNode::readAttributes(QOpcUaNode::NodeAttributes attr)
Q_ARG(QOpcUaNode::NodeAttributes, attr));
}
+bool QFreeOpcUaNode::enableMonitoring(QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
+{
+ return QMetaObject::invokeMethod(m_client->m_opcuaWorker, "enableMonitoring",
+ Qt::QueuedConnection,
+ Q_ARG(uintptr_t, reinterpret_cast<uintptr_t>(this)),
+ Q_ARG(OpcUa::Node, m_node),
+ Q_ARG(QOpcUaNode::NodeAttributes, attr),
+ Q_ARG(QOpcUaMonitoringParameters, settings));
+}
+
+bool QFreeOpcUaNode::disableMonitoring(QOpcUaNode::NodeAttributes attr)
+{
+ return QMetaObject::invokeMethod(m_client->m_opcuaWorker, "disableMonitoring",
+ Qt::QueuedConnection,
+ Q_ARG(uintptr_t, reinterpret_cast<uintptr_t>(this)),
+ Q_ARG(QOpcUaNode::NodeAttributes, attr));
+}
+
+bool QFreeOpcUaNode::modifyMonitoring(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value)
+{
+ return QMetaObject::invokeMethod(m_client->m_opcuaWorker, "modifyMonitoring",
+ Qt::QueuedConnection,
+ Q_ARG(uintptr_t, reinterpret_cast<uintptr_t>(this)),
+ Q_ARG(QOpcUaNode::NodeAttribute, attr),
+ Q_ARG(QOpcUaMonitoringParameters::Parameter, item),
+ Q_ARG(QVariant, value));
+}
+
QStringList QFreeOpcUaNode::childrenIds() const
{
QStringList result;
diff --git a/src/plugins/opcua/freeopcua/qfreeopcuanode.h b/src/plugins/opcua/freeopcua/qfreeopcuanode.h
index 52fef68..46f2ebd 100644
--- a/src/plugins/opcua/freeopcua/qfreeopcuanode.h
+++ b/src/plugins/opcua/freeopcua/qfreeopcuanode.h
@@ -43,8 +43,6 @@
#include <opc/ua/node.h>
-class QFreeOpcUaWorker;
-
namespace OpcUa
{
class UaClient;
@@ -61,6 +59,9 @@ public:
~QFreeOpcUaNode() override;
bool readAttributes(QOpcUaNode::NodeAttributes attr) override;
+ bool enableMonitoring(QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings);
+ bool disableMonitoring(QOpcUaNode::NodeAttributes attr);
+ bool modifyMonitoring(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value);
QStringList childrenIds() const override;
QString nodeId() const override;
diff --git a/src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp b/src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp
index 487b3ed..f89ae57 100644
--- a/src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp
+++ b/src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp
@@ -35,13 +35,9 @@
****************************************************************************/
#include "qfreeopcuaclient.h"
-#include "qfreeopcuanode.h"
#include "qfreeopcuasubscription.h"
#include "qfreeopcuavalueconverter.h"
-#include <QtOpcUa/qopcuamonitoredvalue.h>
-#include <QtOpcUa/qopcuasubscription.h>
-#include <private/qopcuamonitoredevent_p.h>
-#include <private/qopcuamonitoredvalue_p.h>
+#include "qfreeopcuaworker.h"
#include <private/qopcuanode_p.h>
#include <QtCore/qloggingcategory.h>
@@ -50,157 +46,194 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_FREEOPCUA)
-QFreeOpcUaSubscription::QFreeOpcUaSubscription(OpcUa::UaClient *client, quint32 interval)
- : m_client(client)
+QFreeOpcUaSubscription::QFreeOpcUaSubscription(QFreeOpcUaWorker *backend, const QOpcUaMonitoringParameters &settings)
+ : m_interval(settings.publishingInterval())
+ , m_shared(settings.shared())
+ , m_backend(backend)
{
- Q_ASSERT(m_client);
+ Q_ASSERT(m_backend);
+}
+
+QFreeOpcUaSubscription::~QFreeOpcUaSubscription()
+{
+ removeOnServer();
+}
+
+quint32 QFreeOpcUaSubscription::createOnServer()
+{
+ if (m_subscription)
+ return 0;
try {
- m_subscription = m_client->CreateSubscription(interval, *this);
+ m_subscription = m_backend->CreateSubscription(m_interval, *this);
+ m_interval = m_subscription->GetData().RevisedPublishingInterval;
} catch (const std::exception &ex) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Caught: %s", ex.what());
+ qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "CreateOnServer caught: %s", ex.what());
+ return 0;
}
+ return m_subscription->GetId();
}
-QFreeOpcUaSubscription::~QFreeOpcUaSubscription()
+bool QFreeOpcUaSubscription::removeOnServer()
{
+ bool success = false;
try {
if (m_subscription) {
- for (int32_t handle : m_dataChangeHandles.keys())
- m_subscription->UnSubscribe(handle);
-
- for (int32_t handle : m_eventHandles.keys())
- m_subscription->UnSubscribe(handle);
-
m_subscription->Delete();
+ m_subscription.reset();
+ success = true;
}
} catch (const std::exception &ex) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Caught: %s", ex.what());
+ qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "RemoveOnServer caught: %s", ex.what());
}
+
+ qDeleteAll(m_itemIdToItemMapping);
+
+ m_itemIdToItemMapping.clear();
+ m_handleToItemMapping.clear();
+
+ return success;
}
-void QFreeOpcUaSubscription::DataChange(quint32 handle, const OpcUa::Node &node,
- const OpcUa::Variant &val,
- OpcUa::AttributeId attr)
+void QFreeOpcUaSubscription::modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value)
{
- OPCUA_UNUSED(node);
- OPCUA_UNUSED(attr);
-
- auto it = m_dataChangeHandles.find(handle);
- if (it == m_dataChangeHandles.end()) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Received event for unknown handle: %ul", handle);
+ Q_UNUSED(item);
+ Q_UNUSED(value);
+
+ if (!getItemForAttribute(handle, attr)) {
+ qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Could not modify parameter for %lu, there are no monitored items", handle);
+ QOpcUaMonitoringParameters p;
+ p.setStatusCode(QOpcUa::UaStatusCode::BadAttributeIdInvalid);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
return;
}
- try {
- (*it)->d_func()->triggerValueChanged(QFreeOpcUaValueConverter::toQVariant(val));
- } catch (const std::exception &ex) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Caught: %s", ex.what());
- }
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadNotImplemented);
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
}
-QOpcUaMonitoredValue *QFreeOpcUaSubscription::addValue(QOpcUaNode *node)
+bool QFreeOpcUaSubscription::addAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr, const OpcUa::Node &node, QOpcUaMonitoringParameters settings)
{
- if (!m_subscription)
- return nullptr;
+ Q_UNUSED(settings); // Setting these options is not yet supported
- try {
- // Only add a monitored item if the node has a value attribute
- QFreeOpcUaNode *nnode = static_cast<QFreeOpcUaNode *>(node->d_func()->m_impl.data());
- if (nnode->m_node.GetAttribute(OpcUa::AttributeId::Value).Status != OpcUa::StatusCode::Good)
- return nullptr;
+ quint32 monitoredItemId;
+ try {
if (m_subscription) {
- uint32_t handle = m_subscription->SubscribeDataChange(m_client->GetNode(node->nodeId().toStdString()));
- QOpcUaMonitoredValue *monitoredValue = new QOpcUaMonitoredValue(node, m_qsubscription);
- m_dataChangeHandles[handle] = monitoredValue;
- return monitoredValue;
+ monitoredItemId = m_subscription->SubscribeDataChange(node, QFreeOpcUaValueConverter::toUaAttributeId(attr));
+ }
+ else {
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadSubscriptionIdInvalid);
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
+ return false;
}
} catch (const std::exception &ex) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Caught: %s", ex.what());
+ qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "addAttributeMonitoredItem caught: %s", ex.what());
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QFreeOpcUaValueConverter::exceptionToStatusCode(ex));
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
+ return false;
}
- return nullptr;
+
+ MonitoredItem *temp = new MonitoredItem(handle, attr, monitoredItemId);
+ m_handleToItemMapping[handle][attr] = temp;
+ m_itemIdToItemMapping[monitoredItemId] = temp;
+
+ QOpcUaMonitoringParameters s;
+ s.setSubscriptionId(m_subscription->GetId());
+ s.setPublishingInterval(m_interval);
+ s.setMaxKeepAliveCount(m_subscription->GetData().RevisedMaxKeepAliveCount);
+ s.setLifetimeCount(m_subscription->GetData().RevisedLifetimeCount);
+ s.setStatusCode(QOpcUa::UaStatusCode::Good);
+ s.setSamplingInterval(m_interval);
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
+
+ return true;
}
-void QFreeOpcUaSubscription::Event(quint32 handle, const OpcUa::Event &event)
+bool QFreeOpcUaSubscription::removeAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr)
{
- auto it = m_eventHandles.find(handle);
- if (it == m_eventHandles.end()) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA) << "Received event for unknown handle:" << handle;
- return;
+ QScopedPointer<MonitoredItem> item(getItemForAttribute(handle, attr));
+
+ if (!item) {
+ qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "There is no monitored item for this attribute");
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadMonitoredItemIdInvalid);
+ emit m_backend->monitoringEnableDisable(handle, attr, false, s);
+ return false;
}
- try {
- QVector<QVariant> val;
- val.reserve(3);
-
- val.push_back(QVariant(QString::fromStdString(event.Message.Text)));
- val.push_back(QVariant(QString::fromStdString(event.SourceName)));
- val.push_back(QVariant(event.Severity));
+ QOpcUaMonitoringParameters s;
- QOpcUaMonitoredEvent *me = *it;
- me->d_func()->triggerNewEvent(val);
+ try {
+ m_subscription->UnSubscribe(item->monitoredItemId);
+ s.setStatusCode(QOpcUa::UaStatusCode::Good);
} catch (const std::exception &ex) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Caught: %s", ex.what());
+ qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "removeAttributeMonitoredItem caught: %s", ex.what());
+ s.setStatusCode(QFreeOpcUaValueConverter::exceptionToStatusCode(ex));
}
+
+ m_itemIdToItemMapping.remove(item->monitoredItemId);
+ auto it = m_handleToItemMapping.find(handle);
+ it->remove(attr);
+ if (it->empty())
+ m_handleToItemMapping.erase(it);
+
+ emit m_backend->monitoringEnableDisable(handle, attr, false, s);
+
+ return true;
}
-QOpcUaMonitoredEvent *QFreeOpcUaSubscription::addEvent(QOpcUaNode *node)
+double QFreeOpcUaSubscription::interval() const
{
- // Note: Callback is not called due to some error in the event implementation in freeopcua
- if (!m_subscription)
- return nullptr;
+ return m_interval;
+}
- try {
- QFreeOpcUaNode *nnode = static_cast<QFreeOpcUaNode *>(node->d_func()->m_impl.data());
- if (nnode->m_node.GetAttribute(OpcUa::AttributeId::EventNotifier).Status != OpcUa::StatusCode::Good)
- return nullptr;
+QOpcUaMonitoringParameters::SubscriptionType QFreeOpcUaSubscription::shared() const
+{
+ return m_shared;
+}
- OpcUa::Node serverNode = m_client->GetNode(OpcUa::ObjectId::Server);
- OpcUa::Node typeNode = m_client->GetNode(node->nodeId().toStdString());
+quint32 QFreeOpcUaSubscription::subscriptionId() const
+{
+ if (m_subscription)
+ return m_subscription->GetId();
+ else
+ return 0;
+}
- uint32_t handle = m_subscription->SubscribeEvents(serverNode, typeNode);
- QOpcUaMonitoredEvent *monitoredEvent = new QOpcUaMonitoredEvent(node, m_qsubscription);
- m_eventHandles[handle] = monitoredEvent;
- return monitoredEvent;
- } catch (const std::exception &ex) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Caught: %s", ex.what());
- }
- return nullptr;
+int QFreeOpcUaSubscription::monitoredItemsCount() const
+{
+ return m_itemIdToItemMapping.size();
}
-void QFreeOpcUaSubscription::removeEvent(QOpcUaMonitoredEvent *event)
+QFreeOpcUaSubscription::MonitoredItem *QFreeOpcUaSubscription::getItemForAttribute(uintptr_t handle, QOpcUaNode::NodeAttribute attr)
{
- try {
- auto it = m_eventHandles.begin();
- while (it != m_eventHandles.end()) {
- if (it.value() == event) {
- m_subscription->UnSubscribe(it.key());
- m_eventHandles.erase(it);
- break;
- }
- ++it;
- }
- } catch (const std::exception &ex) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Caught: %s", ex.what());
- }
+ auto nodeEntry = m_handleToItemMapping.find(handle);
+
+ if (nodeEntry == m_handleToItemMapping.end())
+ return nullptr;
+
+ auto item = nodeEntry->find(attr);
+ if (item == nodeEntry->end())
+ return nullptr;
+
+ return item.value();
}
-void QFreeOpcUaSubscription::removeValue(QOpcUaMonitoredValue *value)
+void QFreeOpcUaSubscription::DataChange(quint32 handle, const OpcUa::Node &node,
+ const OpcUa::Variant &val,
+ OpcUa::AttributeId attr)
{
- try {
- auto it = m_dataChangeHandles.begin();
- while (it != m_dataChangeHandles.end()) {
- if (it.value() == value) {
- m_subscription->UnSubscribe(it.key());
- m_dataChangeHandles.erase(it);
- break;
- }
- ++it;
- }
- } catch (const std::exception &ex) {
- qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Caught: %s", ex.what());
- }
+ OPCUA_UNUSED(node);
+ OPCUA_UNUSED(attr);
+
+ auto item = m_itemIdToItemMapping.find(handle);
+ if (item == m_itemIdToItemMapping.end())
+ return;
+ emit m_backend->attributeUpdated(item.value()->handle, item.value()->attr, QFreeOpcUaValueConverter::toQVariant(val));
}
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/freeopcua/qfreeopcuasubscription.h b/src/plugins/opcua/freeopcua/qfreeopcuasubscription.h
index bcc4d1e..8d36bfe 100644
--- a/src/plugins/opcua/freeopcua/qfreeopcuasubscription.h
+++ b/src/plugins/opcua/freeopcua/qfreeopcuasubscription.h
@@ -37,9 +37,9 @@
#ifndef QFREEOPCUASUBSCRIPTION_H
#define QFREEOPCUASUBSCRIPTION_H
-#include <QtOpcUa/qopcuamonitoredevent.h>
-#include <QtOpcUa/qopcuamonitoredvalue.h>
-#include <private/qopcuasubscriptionimpl_p.h>
+#include "qfreeopcuanode.h"
+
+#include <QtCore/qhash.h>
#include <opc/ua/subscription.h>
@@ -51,30 +51,57 @@ namespace OpcUa {
QT_BEGIN_NAMESPACE
class QOpcUaNode;
-class QFreeOpcUaMonitoredValue;
-class QOpcUaSubscription;
+class QFreeOpcUaWorker;
-class QFreeOpcUaSubscription : public QOpcUaSubscriptionImpl, public OpcUa::SubscriptionHandler
+class QFreeOpcUaSubscription : public OpcUa::SubscriptionHandler
{
public:
- explicit QFreeOpcUaSubscription(OpcUa::UaClient *client, quint32 interval);
+ QFreeOpcUaSubscription(QFreeOpcUaWorker *backend, const QOpcUaMonitoringParameters &settings);
~QFreeOpcUaSubscription() override;
// FreeOPC-UA callbacks
void DataChange(uint32_t handle, const OpcUa::Node &node, const OpcUa::Variant &val,
OpcUa::AttributeId attr) override;
- void Event(quint32 handle, const OpcUa::Event &event) override;
- QOpcUaMonitoredEvent *addEvent(QOpcUaNode *node) override;
- void removeEvent(QOpcUaMonitoredEvent *event) override;
- QOpcUaMonitoredValue *addValue(QOpcUaNode *node) override;
- void removeValue(QOpcUaMonitoredValue *value) override;
+ quint32 createOnServer();
+ bool removeOnServer();
+
+ void modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value);
+
+ bool addAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr, const OpcUa::Node &node, QOpcUaMonitoringParameters settings);
+ bool removeAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr);
+
+ double interval() const;
+ QOpcUaMonitoringParameters::SubscriptionType shared() const;
+ quint32 subscriptionId() const;
+ int monitoredItemsCount() const;
+
+ struct MonitoredItem {
+ uintptr_t handle;
+ QOpcUaNode::NodeAttribute attr;
+ quint32 monitoredItemId;
+ MonitoredItem(uintptr_t h, QOpcUaNode::NodeAttribute a, quint32 id)
+ : handle(h)
+ , attr(a)
+ , monitoredItemId(id)
+ {}
+ MonitoredItem()
+ : handle(0)
+ , monitoredItemId(0)
+ {}
+ };
+
+private:
+ MonitoredItem *getItemForAttribute(uintptr_t handle, QOpcUaNode::NodeAttribute attr);
+
+ double m_interval;
+ QOpcUaMonitoringParameters::SubscriptionType m_shared;
- OpcUa::UaClient *m_client;
- QOpcUaSubscription *m_qsubscription;
OpcUa::Subscription::SharedPtr m_subscription;
- QMap<int32_t, QOpcUaMonitoredValue *> m_dataChangeHandles;
- QMap<int32_t, QOpcUaMonitoredEvent *> m_eventHandles;
+ QFreeOpcUaWorker *m_backend;
+
+ QHash<quint32, MonitoredItem *> m_itemIdToItemMapping;
+ QHash<uintptr_t, QHash<QOpcUaNode::NodeAttribute, MonitoredItem *>> m_handleToItemMapping;
};
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp b/src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp
index 6e3f95e..8656987 100644
--- a/src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp
+++ b/src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp
@@ -39,7 +39,6 @@
#include "qfreeopcuasubscription.h"
#include "qfreeopcuavalueconverter.h"
#include "qfreeopcuaworker.h"
-#include <QtOpcUa/qopcuasubscription.h>
#include <QtNetwork/qhostinfo.h>
#include <QtCore/qloggingcategory.h>
@@ -55,6 +54,11 @@ QFreeOpcUaWorker::QFreeOpcUaWorker(QFreeOpcUaClientImpl *client)
, m_client(client)
{}
+QFreeOpcUaWorker::~QFreeOpcUaWorker()
+{
+ qDeleteAll(m_subscriptions);
+}
+
void QFreeOpcUaWorker::asyncConnectToEndpoint(const QUrl &url)
{
try {
@@ -197,12 +201,113 @@ void QFreeOpcUaWorker::writeAttributes(uintptr_t handle, OpcUa::Node node, QOpcU
}
}
-QOpcUaSubscription *QFreeOpcUaWorker::createSubscription(quint32 interval)
+QFreeOpcUaSubscription *QFreeOpcUaWorker::getSubscription(const QOpcUaMonitoringParameters &settings)
+{
+ if (settings.shared() == QOpcUaMonitoringParameters::SubscriptionType::Shared) {
+ for (auto entry : m_subscriptions)
+ if (entry->interval() == settings.publishingInterval() && entry->shared() == QOpcUaMonitoringParameters::SubscriptionType::Shared)
+ return entry;
+ }
+
+ QFreeOpcUaSubscription *sub = new QFreeOpcUaSubscription(this, settings);
+ quint32 id = sub->createOnServer();
+ if (!id) {
+ delete sub;
+ return nullptr;
+ }
+ m_subscriptions[id] = sub;
+ return sub;
+}
+
+bool QFreeOpcUaWorker::removeSubscription(quint32 subscriptionId)
{
- QFreeOpcUaSubscription *backendSubscription = new QFreeOpcUaSubscription(this, interval);
- QOpcUaSubscription *subscription = new QOpcUaSubscription(backendSubscription, interval);
- backendSubscription->m_qsubscription = subscription;
- return subscription;
+ auto sub = m_subscriptions.find(subscriptionId);
+ if (sub != m_subscriptions.end()) {
+ sub.value()->removeOnServer();
+ delete sub.value();
+ m_subscriptions.remove(subscriptionId);
+ return true;
+ }
+ return false;
+}
+
+void QFreeOpcUaWorker::enableMonitoring(uintptr_t handle, OpcUa::Node node, QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
+{
+ QFreeOpcUaSubscription *usedSubscription = nullptr;
+
+ // Create a new subscription if necessary
+ if (settings.subscriptionId()) {
+ if (!m_subscriptions.contains(settings.subscriptionId())) {
+ qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "There is no subscription with id %u", settings.subscriptionId());
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attr) {
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadSubscriptionIdInvalid);
+ emit monitoringEnableDisable(handle, attr, true, s);
+ });
+ return;
+ }
+ usedSubscription = m_subscriptions[settings.subscriptionId()]; // Ignore interval != subscription.interval
+ } else {
+ usedSubscription = getSubscription(settings);
+ }
+
+ if (!usedSubscription) {
+ qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Could not create subscription with interval %f", settings.publishingInterval());
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attr) {
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadSubscriptionIdInvalid);
+ emit monitoringEnableDisable(handle, attr, true, s);
+ });
+ return;
+ }
+
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attr) {
+ bool success = usedSubscription->addAttributeMonitoredItem(handle, attr, node, settings);
+ if (success)
+ m_attributeMapping[handle][attr] = usedSubscription;
+ });
+
+ if (usedSubscription->monitoredItemsCount() == 0)
+ removeSubscription(usedSubscription->subscriptionId()); // No items were added
+}
+
+void QFreeOpcUaWorker::disableMonitoring(uintptr_t handle, QOpcUaNode::NodeAttributes attr)
+{
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attr) {
+ QFreeOpcUaSubscription *sub = getSubscriptionForItem(handle, attr);
+ if (sub) {
+ sub->removeAttributeMonitoredItem(handle, attr);
+ if (sub->monitoredItemsCount() == 0)
+ removeSubscription(sub->subscriptionId());
+ }
+ });
+}
+
+void QFreeOpcUaWorker::modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value)
+{
+ QFreeOpcUaSubscription *subscription = getSubscriptionForItem(handle, attr);
+ if (!subscription) {
+ qCWarning(QT_OPCUA_PLUGINS_FREEOPCUA, "Could not modify parameter for %lu, the monitored item does not exist", handle);
+ QOpcUaMonitoringParameters p;
+ p.setStatusCode(QOpcUa::UaStatusCode::BadSubscriptionIdInvalid);
+ emit monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+
+ subscription->modifyMonitoring(handle, attr, item, value);
+}
+
+QFreeOpcUaSubscription *QFreeOpcUaWorker::getSubscriptionForItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr)
+{
+ auto nodeEntry = m_attributeMapping.find(handle);
+ if (nodeEntry == m_attributeMapping.end())
+ return nullptr;
+
+ auto subscription = nodeEntry->find(attr);
+ if (subscription == nodeEntry->end())
+ return nullptr;
+
+ return subscription.value();
}
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/freeopcua/qfreeopcuaworker.h b/src/plugins/opcua/freeopcua/qfreeopcuaworker.h
index d4ede6b..65d2c23 100644
--- a/src/plugins/opcua/freeopcua/qfreeopcuaworker.h
+++ b/src/plugins/opcua/freeopcua/qfreeopcuaworker.h
@@ -37,8 +37,8 @@
#ifndef QFREEOPCUAWORKER_H
#define QFREEOPCUAWORKER_H
+#include "qfreeopcuasubscription.h"
#include <QtOpcUa/qopcuanode.h>
-#include <QtOpcUa/qopcuasubscription.h>
#include <private/qopcuabackend_p.h>
#include <QtCore/qobject.h>
@@ -49,7 +49,6 @@
QT_BEGIN_NAMESPACE
class QFreeOpcUaNode;
-class QOpcUaSubscription;
class QOpcUaNode;
class QFreeOpcUaClientImpl;
@@ -58,8 +57,7 @@ class QFreeOpcUaWorker : public QOpcUaBackend, public OpcUa::UaClient
Q_OBJECT
public:
QFreeOpcUaWorker(QFreeOpcUaClientImpl *client);
-
- Q_INVOKABLE QOpcUaSubscription *createSubscription(quint32 interval);
+ ~QFreeOpcUaWorker();
public slots:
void asyncConnectToEndpoint(const QUrl &url);
@@ -69,8 +67,20 @@ public slots:
void writeAttribute(uintptr_t handle, OpcUa::Node node, QOpcUaNode::NodeAttribute attr, QVariant value, QOpcUa::Types type);
void writeAttributes(uintptr_t handle, OpcUa::Node node, QOpcUaNode::AttributeMap toWrite, QOpcUa::Types valueAttributeType);
+ QFreeOpcUaSubscription *getSubscription(const QOpcUaMonitoringParameters &settings);
+ bool removeSubscription(quint32 subscriptionId);
+
+ void enableMonitoring(uintptr_t handle, OpcUa::Node node, QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings);
+ void disableMonitoring(uintptr_t handle, QOpcUaNode::NodeAttributes attr);
+ void modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value);
+
private:
+ QFreeOpcUaSubscription *getSubscriptionForItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr);
+
QFreeOpcUaClientImpl *m_client;
+
+ QHash<quint32, QFreeOpcUaSubscription *> m_subscriptions;
+ QHash<uintptr_t, QHash<QOpcUaNode::NodeAttribute, QFreeOpcUaSubscription *>> m_attributeMapping; // Handle -> Attribute -> Subscription
};
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/open62541/qopen62541backend.cpp b/src/plugins/opcua/open62541/qopen62541backend.cpp
index 4411a04..7c5226f 100644
--- a/src/plugins/opcua/open62541/qopen62541backend.cpp
+++ b/src/plugins/opcua/open62541/qopen62541backend.cpp
@@ -60,10 +60,20 @@ struct UaLocalizedTextMemberDeleter
Open62541AsyncBackend::Open62541AsyncBackend(QOpen62541Client *parent)
: QOpcUaBackend()
- , m_clientImpl(parent)
, m_uaclient(nullptr)
- , m_subscriptionTimer(nullptr)
+ , m_clientImpl(parent)
+ , m_subscriptionTimer(this)
+ , m_sendPublishRequests(false)
+ , m_shortestInterval(std::numeric_limits<qint32>::max())
+{
+ m_subscriptionTimer.setSingleShot(true);
+ QObject::connect(&m_subscriptionTimer, &QTimer::timeout,
+ this, &Open62541AsyncBackend::sendPublishRequest);
+}
+
+Open62541AsyncBackend::~Open62541AsyncBackend()
{
+ qDeleteAll(m_subscriptions);
}
void Open62541AsyncBackend::readAttributes(uintptr_t handle, UA_NodeId id, QOpcUaNode::NodeAttributes attr)
@@ -155,6 +165,109 @@ void Open62541AsyncBackend::writeAttributes(uintptr_t handle, UA_NodeId id, QOpc
UA_NodeId_deleteMembers(&id);
}
+void Open62541AsyncBackend::enableMonitoring(uintptr_t handle, UA_NodeId id, QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
+{
+ QOpen62541Subscription *usedSubscription = nullptr;
+
+ // Create a new subscription if necessary
+ if (settings.subscriptionId()) {
+ auto sub = m_subscriptions.find(settings.subscriptionId());
+ if (sub == m_subscriptions.end()) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "There is no subscription with id %u", settings.subscriptionId());
+
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attribute){
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadSubscriptionIdInvalid);
+ emit monitoringEnableDisable(handle, attribute, true, s);
+ });
+ return;
+ }
+ usedSubscription = sub.value(); // Ignore interval != subscription.interval
+ } else {
+ usedSubscription = getSubscription(settings);
+ }
+
+ if (!usedSubscription) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not create subscription with interval %f", settings.publishingInterval());
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attribute){
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadSubscriptionIdInvalid);
+ emit monitoringEnableDisable(handle, attribute, true, s);
+ });
+ return;
+ }
+
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attribute){
+ bool success = usedSubscription->addAttributeMonitoredItem(handle, attribute, id, settings);
+ if (success)
+ m_attributeMapping[handle][attribute] = usedSubscription;
+ });
+
+ if (usedSubscription->monitoredItemsCount() == 0)
+ removeSubscription(usedSubscription->subscriptionId()); // No items were added
+
+ modifyPublishRequests();
+}
+
+void Open62541AsyncBackend::disableMonitoring(uintptr_t handle, QOpcUaNode::NodeAttributes attr)
+{
+ qt_forEachAttribute(attr, [&](QOpcUaNode::NodeAttribute attribute){
+ QOpen62541Subscription *sub = getSubscriptionForItem(handle, attribute);
+ if (sub) {
+ sub->removeAttributeMonitoredItem(handle, attribute);
+ if (sub->monitoredItemsCount() == 0)
+ removeSubscription(sub->subscriptionId());
+ }
+ });
+ modifyPublishRequests();
+}
+
+void Open62541AsyncBackend::modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value)
+{
+ QOpen62541Subscription *subscription = getSubscriptionForItem(handle, attr);
+ if (!subscription) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify parameter for %lu, the monitored item does not exist", handle);
+ QOpcUaMonitoringParameters p;
+ p.setStatusCode(QOpcUa::UaStatusCode::BadMonitoredItemIdInvalid);
+ emit monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+
+ subscription->modifyMonitoring(handle, attr, item, value);
+ modifyPublishRequests();
+}
+
+QOpen62541Subscription *Open62541AsyncBackend::getSubscription(const QOpcUaMonitoringParameters &settings)
+{
+ if (settings.shared() == QOpcUaMonitoringParameters::SubscriptionType::Shared) {
+ for (auto entry : qAsConst(m_subscriptions)) {
+ if (qFuzzyCompare(entry->interval(), settings.publishingInterval()) && entry->shared() == QOpcUaMonitoringParameters::SubscriptionType::Shared)
+ return entry;
+ }
+ }
+
+ QOpen62541Subscription *sub = new QOpen62541Subscription(this, settings);
+ UA_UInt32 id = sub->createOnServer();
+ if (!id) {
+ delete sub;
+ return nullptr;
+ }
+ m_subscriptions[id] = sub;
+ return sub;
+}
+
+bool Open62541AsyncBackend::removeSubscription(UA_UInt32 subscriptionId)
+{
+ auto sub = m_subscriptions.find(subscriptionId);
+ if (sub != m_subscriptions.end()) {
+ sub.value()->removeOnServer();
+ delete sub.value();
+ m_subscriptions.remove(subscriptionId);
+ return true;
+ }
+ return false;
+}
+
static UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *pass)
{
Q_UNUSED(referenceTypeId);
@@ -208,24 +321,6 @@ QStringList Open62541AsyncBackend::childrenIds(const UA_NodeId *parentNode)
return result;
}
-UA_UInt32 Open62541AsyncBackend::createSubscription(int interval)
-{
- UA_UInt32 result;
- UA_SubscriptionSettings settings = UA_SubscriptionSettings_default;
- settings.requestedPublishingInterval = interval;
- UA_StatusCode ret = UA_Client_Subscriptions_new(m_uaclient, settings, &result);
- if (ret != UA_STATUSCODE_GOOD)
- return 0;
- return result;
-}
-
-void Open62541AsyncBackend::deleteSubscription(UA_UInt32 id)
-{
- UA_StatusCode ret = UA_Client_Subscriptions_remove(m_uaclient, id);
- if (ret != UA_STATUSCODE_GOOD)
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "QOpcUa::Open62541: Could not remove subscription";
-}
-
void Open62541AsyncBackend::connectToEndpoint(const QUrl &url)
{
m_uaclient = UA_Client_new(UA_ClientConfig_default);
@@ -251,12 +346,6 @@ void Open62541AsyncBackend::connectToEndpoint(const QUrl &url)
return;
}
- if (!m_subscriptionTimer) {
- m_subscriptionTimer = new QTimer(this);
- m_subscriptionTimer->setInterval(5000);
- QObject::connect(m_subscriptionTimer, &QTimer::timeout,
- this, &Open62541AsyncBackend::updatePublishSubscriptionRequests);
- }
emit stateAndOrErrorChanged(QOpcUaClient::Connected, QOpcUaClient::NoError);
}
@@ -270,42 +359,58 @@ void Open62541AsyncBackend::disconnectFromEndpoint()
UA_Client_delete(m_uaclient);
m_uaclient = nullptr;
- m_subscriptionTimer->stop();
+ m_subscriptionTimer.stop();
emit stateAndOrErrorChanged(QOpcUaClient::Disconnected, QOpcUaClient::NoError);
}
-void Open62541AsyncBackend::updatePublishSubscriptionRequests() const
+void Open62541AsyncBackend::sendPublishRequest()
{
- if (m_uaclient)
- UA_Client_Subscriptions_manuallySendPublishRequest(m_uaclient);
+ if (!m_uaclient)
+ return;
+
+ if (!m_sendPublishRequests) {
+ return;
+ }
+
+ if (UA_Client_Subscriptions_manuallySendPublishRequest(m_uaclient) == UA_STATUSCODE_BADSERVERNOTCONNECTED) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Unable to send publish request");
+ m_sendPublishRequests = false;
+ return;
+ }
+
+ m_subscriptionTimer.start(m_shortestInterval);
}
-void Open62541AsyncBackend::activateSubscriptionTimer(int timeout)
+void Open62541AsyncBackend::modifyPublishRequests()
{
- // ### TODO: Check all available subscriptions and their timeout value
- // Get minimum value
- if (timeout <= 0)
+ if (m_subscriptions.count() == 0) {
+ m_subscriptionTimer.stop();
+ m_sendPublishRequests = false;
+ m_shortestInterval = std::numeric_limits<qint32>::max();
return;
+ }
- m_subscriptionIntervals.insert(timeout);
+ for (auto it : qAsConst(m_subscriptions))
+ if (it->interval() < m_shortestInterval)
+ m_shortestInterval = it->interval();
- int minInterval = timeout;
- for (auto it : m_subscriptionIntervals) {
- if (it < minInterval)
- minInterval = it;
- }
- // Update subscriptionTimer timeout
- m_subscriptionTimer->setInterval(minInterval);
- // Start / Stop timer
- m_subscriptionTimer->start();
+ m_subscriptionTimer.stop();
+ m_sendPublishRequests = true;
+ sendPublishRequest();
}
-void Open62541AsyncBackend::removeSubscriptionTimer(int timeout)
+QOpen62541Subscription *Open62541AsyncBackend::getSubscriptionForItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr)
{
- if (!m_subscriptionIntervals.remove(timeout))
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Trying to remove non-existent interval.";
- if (m_subscriptionIntervals.isEmpty())
- m_subscriptionTimer->stop();
+ auto nodeEntry = m_attributeMapping.find(handle);
+ if (nodeEntry == m_attributeMapping.end())
+ return nullptr;
+
+ auto subscription = nodeEntry->find(attr);
+ if (subscription == nodeEntry->end()) {
+ return nullptr;
+ }
+
+ return subscription.value();
}
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/open62541/qopen62541backend.h b/src/plugins/opcua/open62541/qopen62541backend.h
index 4445f90..d6e2a80 100644
--- a/src/plugins/opcua/open62541/qopen62541backend.h
+++ b/src/plugins/opcua/open62541/qopen62541backend.h
@@ -35,6 +35,7 @@
****************************************************************************/
#include "qopen62541client.h"
+#include "qopen62541subscription.h"
#include <private/qopcuabackend_p.h>
#include <QtCore/qset.h>
@@ -50,6 +51,7 @@ class Open62541AsyncBackend : public QOpcUaBackend
Q_OBJECT
public:
Open62541AsyncBackend(QOpen62541Client *parent);
+ ~Open62541AsyncBackend();
public Q_SLOTS:
void connectToEndpoint(const QUrl &url);
@@ -61,18 +63,31 @@ public Q_SLOTS:
void writeAttribute(uintptr_t handle, UA_NodeId id, QOpcUaNode::NodeAttribute attrId, QVariant value, QOpcUa::Types type);
void writeAttributes(uintptr_t handle, UA_NodeId id, QOpcUaNode::AttributeMap toWrite, QOpcUa::Types valueAttributeType);
+ void enableMonitoring(uintptr_t handle, UA_NodeId id, QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings);
+ void disableMonitoring(uintptr_t handle, QOpcUaNode::NodeAttributes attr);
+ void modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value);
// Subscription
- UA_UInt32 createSubscription(int interval);
- void deleteSubscription(UA_UInt32 id);
- void updatePublishSubscriptionRequests() const;
- void activateSubscriptionTimer(int timeout);
- void removeSubscriptionTimer(int timeout);
+ QOpen62541Subscription *getSubscription(const QOpcUaMonitoringParameters &settings);
+ bool removeSubscription(UA_UInt32 subscriptionId);
+ void sendPublishRequest();
+ void modifyPublishRequests();
+
public:
- QOpen62541Client *m_clientImpl;
UA_Client *m_uaclient;
- QTimer *m_subscriptionTimer;
- QSet<int> m_subscriptionIntervals;
+
+private:
+ QOpen62541Subscription *getSubscriptionForItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr);
+
+ QOpen62541Client *m_clientImpl;
+ QTimer m_subscriptionTimer;
+
+ QHash<quint32, QOpen62541Subscription *> m_subscriptions;
+
+ QHash<uintptr_t, QHash<QOpcUaNode::NodeAttribute, QOpen62541Subscription *>> m_attributeMapping; // Handle -> Attribute -> Subscription
+
+ bool m_sendPublishRequests;
+ qint32 m_shortestInterval;
};
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/open62541/qopen62541client.cpp b/src/plugins/opcua/open62541/qopen62541client.cpp
index bbf6467..63867e2 100644
--- a/src/plugins/opcua/open62541/qopen62541client.cpp
+++ b/src/plugins/opcua/open62541/qopen62541client.cpp
@@ -91,14 +91,6 @@ QOpcUaNode *QOpen62541Client::node(const QString &nodeId)
return new QOpcUaNode(new QOpen62541Node(uaNodeId, this, nodeId), m_client);
}
-QOpcUaSubscription *QOpen62541Client::createSubscription(quint32 interval)
-{
- QOpen62541Subscription *backendSubscription = new QOpen62541Subscription(m_backend, interval);
- QOpcUaSubscription *subscription = new QOpcUaSubscription(backendSubscription, interval);
- backendSubscription->m_qsubscription = subscription;
- return subscription;
-}
-
QString QOpen62541Client::backend() const
{
return QStringLiteral("open62541");
diff --git a/src/plugins/opcua/open62541/qopen62541client.h b/src/plugins/opcua/open62541/qopen62541client.h
index 7b605f3..3721d98 100644
--- a/src/plugins/opcua/open62541/qopen62541client.h
+++ b/src/plugins/opcua/open62541/qopen62541client.h
@@ -59,7 +59,6 @@ public:
void disconnectFromEndpoint() override;
QOpcUaNode *node(const QString &nodeId) override;
- QOpcUaSubscription *createSubscription(quint32 interval) override;
QString backend() const override;
diff --git a/src/plugins/opcua/open62541/qopen62541node.cpp b/src/plugins/opcua/open62541/qopen62541node.cpp
index 8634f65..d765784 100644
--- a/src/plugins/opcua/open62541/qopen62541node.cpp
+++ b/src/plugins/opcua/open62541/qopen62541node.cpp
@@ -72,6 +72,34 @@ bool QOpen62541Node::readAttributes(QOpcUaNode::NodeAttributes attr)
Q_ARG(QOpcUaNode::NodeAttributes, attr));
}
+bool QOpen62541Node::enableMonitoring(QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
+{
+ return QMetaObject::invokeMethod(m_client->m_backend, "enableMonitoring",
+ Qt::QueuedConnection,
+ Q_ARG(uintptr_t, reinterpret_cast<uintptr_t>(this)),
+ Q_ARG(UA_NodeId, m_nodeId),
+ Q_ARG(QOpcUaNode::NodeAttributes, attr),
+ Q_ARG(QOpcUaMonitoringParameters, settings));
+}
+
+bool QOpen62541Node::disableMonitoring(QOpcUaNode::NodeAttributes attr)
+{
+ return QMetaObject::invokeMethod(m_client->m_backend, "disableMonitoring",
+ Qt::QueuedConnection,
+ Q_ARG(uintptr_t, reinterpret_cast<uintptr_t>(this)),
+ Q_ARG(QOpcUaNode::NodeAttributes, attr));
+}
+
+bool QOpen62541Node::modifyMonitoring(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value)
+{
+ return QMetaObject::invokeMethod(m_client->m_backend, "modifyMonitoring",
+ Qt::QueuedConnection,
+ Q_ARG(uintptr_t, reinterpret_cast<uintptr_t>(this)),
+ Q_ARG(QOpcUaNode::NodeAttribute, attr),
+ Q_ARG(QOpcUaMonitoringParameters::Parameter, item),
+ Q_ARG(QVariant, value));
+}
+
QStringList QOpen62541Node::childrenIds() const
{
QStringList result = m_client->m_backend->childrenIds(&m_nodeId);
diff --git a/src/plugins/opcua/open62541/qopen62541node.h b/src/plugins/opcua/open62541/qopen62541node.h
index ef54618..95e794c 100644
--- a/src/plugins/opcua/open62541/qopen62541node.h
+++ b/src/plugins/opcua/open62541/qopen62541node.h
@@ -51,6 +51,9 @@ public:
~QOpen62541Node() override;
bool readAttributes(QOpcUaNode::NodeAttributes attr) override;
+ bool enableMonitoring(QOpcUaNode::NodeAttributes attr, const QOpcUaMonitoringParameters &settings);
+ bool disableMonitoring(QOpcUaNode::NodeAttributes attr);
+ bool modifyMonitoring(QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value);
QStringList childrenIds() const override;
QString nodeId() const override;
diff --git a/src/plugins/opcua/open62541/qopen62541subscription.cpp b/src/plugins/opcua/open62541/qopen62541subscription.cpp
index 4f40d3c..8acc41d 100644
--- a/src/plugins/opcua/open62541/qopen62541subscription.cpp
+++ b/src/plugins/opcua/open62541/qopen62541subscription.cpp
@@ -39,7 +39,6 @@
#include "qopen62541node.h"
#include "qopen62541subscription.h"
#include "qopen62541valueconverter.h"
-#include <private/qopcuamonitoredvalue_p.h>
#include <private/qopcuanode_p.h>
#include <QtCore/qloggingcategory.h>
@@ -54,115 +53,273 @@ static void monitoredValueHandler(UA_UInt32 monId, UA_DataValue *value, void *co
subscription->monitoredValueUpdated(monId, value);
}
-QOpen62541Subscription::QOpen62541Subscription(Open62541AsyncBackend *backend, quint32 interval)
+QOpen62541Subscription::QOpen62541Subscription(Open62541AsyncBackend *backend, const QOpcUaMonitoringParameters &settings)
: m_backend(backend)
- , m_interval(interval)
+ , m_interval(settings.publishingInterval())
, m_subscriptionId(0)
+ , m_lifetimeCount(settings.lifetimeCount() ? settings.lifetimeCount() : UA_SubscriptionSettings_default.requestedLifetimeCount)
+ , m_maxKeepaliveCount(settings.maxKeepAliveCount() ? settings.maxKeepAliveCount() : UA_SubscriptionSettings_default.requestedMaxKeepAliveCount)
+ , m_shared(settings.shared())
+ , m_priority(settings.priority())
{
}
QOpen62541Subscription::~QOpen62541Subscription()
{
- removeNativeSubscription();
+ removeOnServer();
+ qDeleteAll(m_itemIdToItemMapping);
}
-QOpcUaMonitoredEvent *QOpen62541Subscription::addEvent(QOpcUaNode *node)
+UA_UInt32 QOpen62541Subscription::createOnServer()
{
- Q_UNUSED(node);
- Q_UNIMPLEMENTED();
- return nullptr;
+ UA_UInt32 subscriptionId = 0;
+ UA_SubscriptionSettings settings = UA_SubscriptionSettings_default;
+ settings.requestedPublishingInterval = m_interval;
+ settings.requestedLifetimeCount = m_lifetimeCount;
+ settings.requestedMaxKeepAliveCount = m_maxKeepaliveCount;
+ settings.priority = m_priority;
+ UA_StatusCode res = UA_Client_Subscriptions_new(m_backend->m_uaclient, settings, &subscriptionId);
+
+ if (res != UA_STATUSCODE_GOOD) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not create subscription with interval %f: 0x%X", m_interval, res);
+ return 0;
+ }
+
+ m_subscriptionId = subscriptionId;
+ return subscriptionId;
}
-void QOpen62541Subscription::removeEvent(QOpcUaMonitoredEvent *event)
+bool QOpen62541Subscription::removeOnServer()
{
- Q_UNUSED(event);
- Q_UNIMPLEMENTED();
+ if (m_subscriptionId == 0)
+ return false;
+
+ UA_StatusCode res = UA_Client_Subscriptions_remove(m_backend->m_uaclient, m_subscriptionId);
+ m_subscriptionId = 0;
+
+ return (res == UA_STATUSCODE_GOOD) ? true : false;
}
-QOpcUaMonitoredValue *QOpen62541Subscription::addValue(QOpcUaNode *node)
+void QOpen62541Subscription::modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value)
{
- if (!ensureNativeSubscription())
- return nullptr;
+ QOpcUaMonitoringParameters p;
+ p.setStatusCode(QOpcUa::UaStatusCode::BadNotImplemented);
+
+ if (!getItemForAttribute(handle, attr)) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify parameter for %lu, there are no monitored items", handle);
+ p.setStatusCode(QOpcUa::UaStatusCode::BadAttributeIdInvalid);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
- QOpen62541Node *open62541node = static_cast<QOpen62541Node *>(node->d_func()->m_impl.data());
+ // SetPublishingMode service
+ if (item == QOpcUaMonitoringParameters::Parameter::PublishingEnabled) {
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+
+ // SetMonitoringMode service
+ if (item == QOpcUaMonitoringParameters::Parameter::MonitoringMode) {
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+
+ // ModifySubscription service
+ {
+ UA_ModifySubscriptionRequest req;
+ UA_ModifySubscriptionRequest_init(&req);
+ req.subscriptionId = m_subscriptionId;
+ req.requestedPublishingInterval = m_interval;
+ req.requestedLifetimeCount = m_lifetimeCount;
+ req.requestedMaxKeepAliveCount = m_maxKeepaliveCount;
+
+ bool match = false;
+
+ switch (item) {
+ case QOpcUaMonitoringParameters::Parameter::PublishingInterval: {
+ bool ok;
+ req.requestedPublishingInterval = value.toDouble(&ok);
+ if (!ok) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify PublishingInterval for %lu, value is not a double", handle);
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+ match = true;
+ break;
+ }
+ case QOpcUaMonitoringParameters::Parameter::LifetimeCount: {
+ bool ok;
+ req.requestedLifetimeCount = value.toUInt(&ok);
+ if (!ok) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify LifetimeCount for %lu, value is not an integer", handle);
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+ match = true;
+ break;
+ }
+ case QOpcUaMonitoringParameters::Parameter::MaxKeepAliveCount: {
+ bool ok;
+ req.requestedMaxKeepAliveCount = value.toUInt(&ok);
+ if (!ok) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not modify MaxKeepAliveCount for %lu, value is not an integer", handle);
+ p.setStatusCode(QOpcUa::UaStatusCode::BadTypeMismatch);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return;
+ }
+ match = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (match) {
+ UA_ModifySubscriptionResponse res = UA_Client_Service_modifySubscription(m_backend->m_uaclient, req);
+
+ if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+ p.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult));
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ } else {
+ p.setStatusCode(QOpcUa::UaStatusCode::Good);
+ p.setPublishingInterval(res.revisedPublishingInterval);
+ p.setLifetimeCount(res.revisedLifetimeCount);
+ p.setMaxKeepAliveCount(res.revisedMaxKeepAliveCount);
+
+ QOpcUaMonitoringParameters::Parameters changed = item;
+ if (!qFuzzyCompare(p.publishingInterval(), m_interval))
+ changed |= QOpcUaMonitoringParameters::Parameter::PublishingInterval;
+ if (p.lifetimeCount() != m_lifetimeCount)
+ changed |= QOpcUaMonitoringParameters::Parameter::LifetimeCount;
+ if (p.maxKeepAliveCount() != m_maxKeepaliveCount)
+ changed |= QOpcUaMonitoringParameters::Parameter::MaxKeepAliveCount;
+
+ for (auto it : qAsConst(m_itemIdToItemMapping))
+ emit m_backend->monitoringStatusChanged(it->handle, it->attr, changed, p);
+
+ m_lifetimeCount = res.revisedLifetimeCount;
+ m_maxKeepaliveCount = res.revisedMaxKeepAliveCount;
+ m_interval = res.revisedPublishingInterval;
+ }
+ return;
+ }
+ }
+
+ // ModifyMonitoredItems service
+ {
+ // TODO: Add support as soon as Open62541 supports this.
+ }
+
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Revising attribute is not implemented:" << item;
+ p.setStatusCode(QOpcUa::UaStatusCode::BadNotImplemented);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+}
+
+bool QOpen62541Subscription::addAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr, const UA_NodeId &id, QOpcUaMonitoringParameters settings)
+{
+ Q_UNUSED(settings); // This is for later applications like including parameters for the monitored item into settings
UA_UInt32 monitoredItemId = 0;
- UA_StatusCode ret = UA_Client_Subscriptions_addMonitoredItem(m_backend->m_uaclient, m_subscriptionId,
- open62541node->nativeNodeId(), UA_ATTRIBUTEID_VALUE,
+ UA_StatusCode ret = UA_Client_Subscriptions_addMonitoredItem(m_backend->m_uaclient, m_subscriptionId, id,
+ QOpen62541ValueConverter::toUaAttributeId(attr),
monitoredValueHandler, this, &monitoredItemId);
+
if (ret != UA_STATUSCODE_GOOD) {
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not add monitored item:" << ret;
- return nullptr;
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not add monitored item to subscription %u: 0x%X", m_subscriptionId, ret);
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(static_cast<QOpcUa::UaStatusCode>(ret));
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
+ return false;
}
- QOpcUaMonitoredValue *monitoredValue = new QOpcUaMonitoredValue(node, m_qsubscription);
- if (m_dataChangeHandles.contains(monitoredItemId)) {
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "monitoredItemId already handled:" << monitoredItemId;
- } else {
- m_dataChangeHandles[monitoredItemId] = monitoredValue;
- }
+ MonitoredItem *temp = new MonitoredItem(handle, attr, monitoredItemId);
+ m_handleToItemMapping[handle][attr] = temp;
+ m_itemIdToItemMapping[monitoredItemId] = temp;
- // Open62541 does not support automated subscriptions, hence we need to poll for the initial
- // value.
- UA_Client_Subscriptions_manuallySendPublishRequest(m_backend->m_uaclient);
- QMetaObject::invokeMethod(m_backend, "activateSubscriptionTimer", Qt::QueuedConnection, Q_ARG(int, m_interval));
+ QOpcUaMonitoringParameters s;
+ s.setSubscriptionId(m_subscriptionId);
+ s.setPublishingInterval(m_interval);
+ s.setMaxKeepAliveCount(m_maxKeepaliveCount);
+ s.setLifetimeCount(m_lifetimeCount);
+ s.setStatusCode(static_cast<QOpcUa::UaStatusCode>(ret));
+ s.setSamplingInterval(m_interval);
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
- return monitoredValue;
+ return true;
}
-void QOpen62541Subscription::removeValue(QOpcUaMonitoredValue *monitoredValue)
+bool QOpen62541Subscription::removeAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr)
{
- auto it = m_dataChangeHandles.begin();
- while (it != m_dataChangeHandles.end()) {
- if (it.value() == monitoredValue) {
- UA_StatusCode ret = UA_Client_Subscriptions_removeMonitoredItem(m_backend->m_uaclient, m_subscriptionId, it.key());
- if (ret != UA_STATUSCODE_GOOD) {
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not remove monitored value from subscription:" << it.key();
- }
- m_dataChangeHandles.erase(it);
- QMetaObject::invokeMethod(m_backend, "removeSubscriptionTimer", Qt::QueuedConnection, Q_ARG(int, m_interval));
- break;
- }
- ++it;
+ MonitoredItem *item = getItemForAttribute(handle, attr);
+ if (!item) {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "There is no monitored item for this attribute");
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadMonitoredItemIdInvalid);
+ emit m_backend->monitoringEnableDisable(handle, attr, false, s);
+ return false;
}
+
+ UA_StatusCode res = UA_Client_Subscriptions_removeMonitoredItem(m_backend->m_uaclient, m_subscriptionId, item->monitoredItemId);
+ if (res != UA_STATUSCODE_GOOD)
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541, "Could not remove monitored item %u from subscription %u: 0x%X",
+ item->monitoredItemId, m_subscriptionId, res);
+
+ m_itemIdToItemMapping.remove(item->monitoredItemId);
+ auto it = m_handleToItemMapping.find(handle);
+ it->remove(attr);
+ if (it->empty())
+ m_handleToItemMapping.erase(it);
+
+ delete item;
+
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(static_cast<QOpcUa::UaStatusCode>(res));
+ emit m_backend->monitoringEnableDisable(handle, attr, false, s);
+
+ return true;
}
void QOpen62541Subscription::monitoredValueUpdated(UA_UInt32 monId, UA_DataValue *value)
{
- auto monitoredValue = m_dataChangeHandles.find(monId);
- if (monitoredValue == m_dataChangeHandles.end()) {
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not find object for monitoredItemId:" << monId;
+ auto item = m_itemIdToItemMapping.constFind(monId);
+ if (item == m_itemIdToItemMapping.constEnd())
return;
- }
+ emit m_backend->attributeUpdated(item.value()->handle, item.value()->attr, QOpen62541ValueConverter::toQVariant(value->value));
+}
- if (!value || !value->hasValue)
- return;
+double QOpen62541Subscription::interval() const
+{
+ return m_interval;
+}
- QVariant var = QOpen62541ValueConverter::toQVariant(value->value);
- if (var.isValid())
- (*monitoredValue)->d_func()->triggerValueChanged(var);
- else
- qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not convert value for node:" << (*monitoredValue)->node().nodeId();
+UA_UInt32 QOpen62541Subscription::subscriptionId() const
+{
+ return m_subscriptionId;
}
-bool QOpen62541Subscription::ensureNativeSubscription()
+int QOpen62541Subscription::monitoredItemsCount() const
{
- if (m_subscriptionId == 0) {
- QMetaObject::invokeMethod(m_backend, "createSubscription",
- Qt::BlockingQueuedConnection,
- Q_RETURN_ARG(UA_UInt32, m_subscriptionId),
- Q_ARG(int, m_interval));
- }
- return m_subscriptionId != 0;
+ return m_itemIdToItemMapping.size();
}
-void QOpen62541Subscription::removeNativeSubscription()
+QOpcUaMonitoringParameters::SubscriptionType QOpen62541Subscription::shared() const
{
- if (m_subscriptionId != 0) {
- QMetaObject::invokeMethod(m_backend, "deleteSubscription",
- Qt::BlockingQueuedConnection,
- Q_ARG(UA_UInt32, m_subscriptionId));
- m_subscriptionId = 0;
- }
+ return m_shared;
+}
+
+QOpen62541Subscription::MonitoredItem *QOpen62541Subscription::getItemForAttribute(uintptr_t handle, QOpcUaNode::NodeAttribute attr)
+{
+ auto nodeEntry = m_handleToItemMapping.constFind(handle);
+
+ if (nodeEntry == m_handleToItemMapping.constEnd())
+ return nullptr;
+
+ auto item = nodeEntry->constFind(attr);
+ if (item == nodeEntry->constEnd())
+ return nullptr;
+
+ return item.value();
}
QT_END_NAMESPACE
diff --git a/src/plugins/opcua/open62541/qopen62541subscription.h b/src/plugins/opcua/open62541/qopen62541subscription.h
index e527c7b..a75aa90 100644
--- a/src/plugins/opcua/open62541/qopen62541subscription.h
+++ b/src/plugins/opcua/open62541/qopen62541subscription.h
@@ -38,39 +38,63 @@
#define QOPEN62541SUBSCRIPTION_H
#include "qopen62541.h"
-#include <QtOpcUa/qopcuamonitoredevent.h>
-#include <QtOpcUa/qopcuamonitoredvalue.h>
-#include <private/qopcuasubscriptionimpl_p.h>
+#include <QtOpcUa/qopcuanode.h>
QT_BEGIN_NAMESPACE
class QOpen62541Client;
class Open62541AsyncBackend;
-class QOpen62541Subscription : public QOpcUaSubscriptionImpl
+class QOpen62541Subscription
{
public:
- explicit QOpen62541Subscription(Open62541AsyncBackend *backend, quint32 interval);
- ~QOpen62541Subscription() override;
+ QOpen62541Subscription(Open62541AsyncBackend *backend, const QOpcUaMonitoringParameters &settings);
+ ~QOpen62541Subscription();
- QOpcUaMonitoredEvent *addEvent(QOpcUaNode *node) override;
- void removeEvent(QOpcUaMonitoredEvent *event) override;
+ UA_UInt32 createOnServer();
+ bool removeOnServer();
- QOpcUaMonitoredValue *addValue(QOpcUaNode *node) override;
- void removeValue(QOpcUaMonitoredValue *v) override;
+ void modifyMonitoring(uintptr_t handle, QOpcUaNode::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, QVariant value);
+
+ bool addAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr, const UA_NodeId &id, QOpcUaMonitoringParameters settings);
+ bool removeAttributeMonitoredItem(uintptr_t handle, QOpcUaNode::NodeAttribute attr);
void monitoredValueUpdated(UA_UInt32 monId, UA_DataValue *value);
- QOpcUaSubscription *m_qsubscription;
+ struct MonitoredItem {
+ uintptr_t handle;
+ QOpcUaNode::NodeAttribute attr;
+ UA_UInt32 monitoredItemId;
+ MonitoredItem(uintptr_t h, QOpcUaNode::NodeAttribute a, UA_UInt32 id)
+ : handle(h)
+ , attr(a)
+ , monitoredItemId(id)
+ {}
+ MonitoredItem()
+ : handle(0)
+ , monitoredItemId(0)
+ {}
+ };
+
+ double interval() const;
+ UA_UInt32 subscriptionId() const;
+ int monitoredItemsCount() const;
+
+ QOpcUaMonitoringParameters::SubscriptionType shared() const;
private:
- bool ensureNativeSubscription();
- void removeNativeSubscription();
+ MonitoredItem *getItemForAttribute(uintptr_t handle, QOpcUaNode::NodeAttribute attr);
+
Open62541AsyncBackend *m_backend;
- quint32 m_interval;
+ double m_interval;
UA_UInt32 m_subscriptionId;
- QMap<UA_UInt32, QOpcUaMonitoredValue *> m_dataChangeHandles;
- QMap<UA_UInt32, QOpcUaMonitoredEvent *> m_eventHandles;
+ UA_UInt32 m_lifetimeCount;
+ UA_UInt32 m_maxKeepaliveCount;
+ QOpcUaMonitoringParameters::SubscriptionType m_shared;
+ quint8 m_priority;
+
+ QHash<uintptr_t, QHash<QOpcUaNode::NodeAttribute, MonitoredItem *>> m_handleToItemMapping; // Handle -> Attribute -> MonitoredItem
+ QHash<UA_UInt32, MonitoredItem *> m_itemIdToItemMapping; // ItemId -> Item for fast lookup on data change
};
QT_END_NAMESPACE
diff --git a/tests/auto/qopcuaclient/tst_client.cpp b/tests/auto/qopcuaclient/tst_client.cpp
index bd01c55..ad5e7eb 100644
--- a/tests/auto/qopcuaclient/tst_client.cpp
+++ b/tests/auto/qopcuaclient/tst_client.cpp
@@ -35,8 +35,6 @@
****************************************************************************/
#include <QtOpcUa/QOpcUaClient>
-#include <QtOpcUa/QOpcUaMonitoredEvent>
-#include <QtOpcUa/QOpcUaMonitoredValue>
#include <QtOpcUa/QOpcUaNode>
#include <QtOpcUa/QOpcUaProvider>
@@ -177,10 +175,6 @@ private slots:
void dataChangeSubscriptionInvalidNode();
defineDataMethod(methodCall_data)
void methodCall();
- defineDataMethod(eventSubscription_data)
- void eventSubscription();
- defineDataMethod(eventSubscribeInvalidNode_data)
- void eventSubscribeInvalidNode();
defineDataMethod(readRange_data)
void readRange();
defineDataMethod(readEui_data)
@@ -568,19 +562,132 @@ void Tst_QOpcUaClient::dataChangeSubscription()
READ_MANDATORY_VARIABLE_NODE(node);
QTRY_COMPARE(node->attribute(QOpcUaNode::NodeAttribute::Value), 0);
- QScopedPointer<QOpcUaSubscription> subscription(opcuaClient->createSubscription(100));
- QScopedPointer<QOpcUaMonitoredValue> monitoredValue(subscription->addValue(node.data()));
- QVERIFY(monitoredValue != nullptr);
- if (!monitoredValue)
- QFAIL("can not monitor value");
+ WRITE_VALUE_ATTRIBUTE(node, QVariant(double(0)), QOpcUa::Types::Double);
+
+ QSignalSpy dataChangeSpy(node.data(), &QOpcUaNode::attributeUpdated);
+ QSignalSpy monitoringEnabledSpy(node.data(), &QOpcUaNode::enableMonitoringFinished);
+
+ node->enableMonitoring(QOpcUaNode::NodeAttribute::Value, QOpcUaMonitoringParameters(100, QOpcUaMonitoringParameters::SubscriptionType::Exclusive));
+ monitoringEnabledSpy.wait();
- QSignalSpy valueSpy(monitoredValue.data(), &QOpcUaMonitoredValue::valueChanged);
+ QVERIFY(monitoringEnabledSpy.size() == 1);
+ QVERIFY(monitoringEnabledSpy.at(0).at(0).value<QOpcUaNode::NodeAttribute>() == QOpcUaNode::NodeAttribute::Value);
+ QVERIFY(node->monitoringStatus(QOpcUaNode::NodeAttribute::Value).statusCode() == 0);
+
+ QOpcUaMonitoringParameters valueStatus = node->monitoringStatus(QOpcUaNode::NodeAttribute::Value);
+ QVERIFY(valueStatus.subscriptionId() != 0);
+ QVERIFY(valueStatus.statusCode() == 0);
WRITE_VALUE_ATTRIBUTE(node, QVariant(double(42)), QOpcUa::Types::Double);
+ dataChangeSpy.wait();
+ if (dataChangeSpy.size() < 2)
+ dataChangeSpy.wait();
+
+ QVERIFY(dataChangeSpy.size() >= 1);
+
+ int index = dataChangeSpy.size() == 1 ? 0 : 1;
+ QVERIFY(dataChangeSpy.at(index).at(0).value<QOpcUaNode::NodeAttribute>() == QOpcUaNode::NodeAttribute::Value);
+ QVERIFY(dataChangeSpy.at(index).at(1) == double(42));
+
+ monitoringEnabledSpy.clear();
+ dataChangeSpy.clear();
+
+ node->enableMonitoring(QOpcUaNode::NodeAttribute::DisplayName, QOpcUaMonitoringParameters(100,QOpcUaMonitoringParameters::SubscriptionType::Exclusive,
+ valueStatus.subscriptionId()));
+ monitoringEnabledSpy.wait();
+
+ QVERIFY(monitoringEnabledSpy.size() == 1);
+ QVERIFY(monitoringEnabledSpy.at(0).at(0).value<QOpcUaNode::NodeAttribute>() == QOpcUaNode::NodeAttribute::DisplayName);
+ QVERIFY(node->monitoringStatus(QOpcUaNode::NodeAttribute::DisplayName).statusCode() == 0);
+
+ QOpcUaMonitoringParameters displayNameStatus = node->monitoringStatus(QOpcUaNode::NodeAttribute::DisplayName);
+ QVERIFY(displayNameStatus.subscriptionId() == valueStatus.subscriptionId());
+ QVERIFY(displayNameStatus.statusCode() == 0);
+
+ dataChangeSpy.wait();
+ QVERIFY(dataChangeSpy.size() == 1);
+ QVERIFY(dataChangeSpy.at(0).at(0).value<QOpcUaNode::NodeAttribute>() == QOpcUaNode::NodeAttribute::DisplayName);
+ QVERIFY(dataChangeSpy.at(0).at(1).value<QOpcUa::QLocalizedText>().text == QLatin1String("ns=3;s=TestNode.ReadWrite"));
+
+ monitoringEnabledSpy.clear();
+ dataChangeSpy.clear();
+ node->enableMonitoring(QOpcUaNode::NodeAttribute::NodeId, QOpcUaMonitoringParameters(100));
+ monitoringEnabledSpy.wait();
+ QVERIFY(monitoringEnabledSpy.size() == 1);
+ QVERIFY(monitoringEnabledSpy.at(0).at(0).value<QOpcUaNode::NodeAttribute>() == QOpcUaNode::NodeAttribute::NodeId);
+ QVERIFY(node->monitoringStatus(QOpcUaNode::NodeAttribute::NodeId).subscriptionId() != valueStatus.subscriptionId());
+ QVERIFY(node->monitoringStatus(QOpcUaNode::NodeAttribute::NodeId).statusCode() == 0);
- valueSpy.wait();
- QCOMPARE(valueSpy.count(), 1);
- QCOMPARE(valueSpy.at(0).at(0).toDouble(), double(42));
+ QOpcUaMonitoringParameters nodeIdStatus = node->monitoringStatus(QOpcUaNode::NodeAttribute::NodeId);
+ QVERIFY(nodeIdStatus.subscriptionId() != valueStatus.subscriptionId());
+ QVERIFY(nodeIdStatus.statusCode() == 0);
+
+ dataChangeSpy.wait();
+ QVERIFY(dataChangeSpy.size() == 1);
+ QVERIFY(dataChangeSpy.at(0).at(0).value<QOpcUaNode::NodeAttribute>() == QOpcUaNode::NodeAttribute::NodeId);
+ QVERIFY(dataChangeSpy.at(0).at(1) == QLatin1String("ns=3;s=TestNode.ReadWrite"));
+
+ QVector<QOpcUaNode::NodeAttribute> attrs;
+
+ if (opcuaClient->backend() == QLatin1String("open62541")) {
+ QSignalSpy monitoringModifiedSpy(node.data(), &QOpcUaNode::monitoringStatusChanged);
+ node->modifyMonitoring(QOpcUaNode::NodeAttribute::Value, QOpcUaMonitoringParameters::Parameter::PublishingInterval, 200);
+
+ monitoringModifiedSpy.wait();
+ if (monitoringModifiedSpy.size() < 2)
+ monitoringModifiedSpy.wait();
+
+ attrs = {QOpcUaNode::NodeAttribute::Value, QOpcUaNode::NodeAttribute::DisplayName};
+ for (auto it : qAsConst(monitoringModifiedSpy)) {
+ QOpcUaNode::NodeAttribute temp = it.at(0).value<QOpcUaNode::NodeAttribute>();
+ QVERIFY(attrs.contains(temp));
+ QVERIFY(it.at(1).value<QOpcUaMonitoringParameters::Parameters>() & QOpcUaMonitoringParameters::Parameter::PublishingInterval);
+ QVERIFY(it.at(2) == QOpcUa::UaStatusCode::Good);
+ QVERIFY(node->monitoringStatus(temp).publishingInterval() == double(200));
+ attrs.remove(attrs.indexOf(temp));
+ }
+ QVERIFY(attrs.size() == 0);
+
+ QVERIFY(node->monitoringStatus(QOpcUaNode::NodeAttribute::Value).publishingInterval() == 200);
+ QVERIFY(node->monitoringStatus(QOpcUaNode::NodeAttribute::DisplayName).publishingInterval() == 200);
+
+ monitoringModifiedSpy.clear();
+ QOpcUaMonitoringParameters::DataChangeFilter filter;
+ filter.deadbandType = QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType::Absolute;
+ filter.trigger = QOpcUaMonitoringParameters::DataChangeFilter::DataChangeTrigger::StatusValue;
+ filter.deadbandValue = 10;
+ node->modifyMonitoring(QOpcUaNode::NodeAttribute::Value, QOpcUaMonitoringParameters::Parameter::Filter, QVariant::fromValue(filter));
+ monitoringModifiedSpy.wait();
+ QVERIFY(monitoringModifiedSpy.size() == 1);
+ QVERIFY(monitoringModifiedSpy.at(0).at(0).value<QOpcUaNode::NodeAttribute>() == QOpcUaNode::NodeAttribute::Value);
+ QVERIFY(monitoringModifiedSpy.at(0).at(1).value<QOpcUaMonitoringParameters::Parameters>() & QOpcUaMonitoringParameters::Parameter::Filter);
+ QEXPECT_FAIL("", "Modifying monitored items is not yet supported by open62541", Continue);
+ QVERIFY(monitoringModifiedSpy.at(0).at(2).value<QOpcUa::UaStatusCode>() == QOpcUa::UaStatusCode::Good);
+
+ } else {
+ qDebug() << "Modifying monitoring settings is not supported by the freeopcua backend";
+ }
+
+ QSignalSpy monitoringDisabledSpy(node.data(), &QOpcUaNode::disableMonitoringFinished);
+
+ node->disableMonitoring(QOpcUaNode::NodeAttribute::Value | QOpcUaNode::NodeAttribute::DisplayName | QOpcUaNode::NodeAttribute::NodeId);
+ monitoringDisabledSpy.wait();
+ if (monitoringDisabledSpy.size() < 2)
+ monitoringDisabledSpy.wait();
+ if (monitoringDisabledSpy.size() < 3)
+ monitoringDisabledSpy.wait();
+
+ QVERIFY(monitoringDisabledSpy.size() == 3);
+
+ attrs = {QOpcUaNode::NodeAttribute::Value, QOpcUaNode::NodeAttribute::DisplayName, QOpcUaNode::NodeAttribute::NodeId};
+ for (auto it : qAsConst(monitoringDisabledSpy)) {
+ QOpcUaNode::NodeAttribute temp = it.at(0).value<QOpcUaNode::NodeAttribute>();
+ QVERIFY(attrs.contains(temp));
+ QVERIFY(node->monitoringStatus(temp).subscriptionId() == 0);
+ QVERIFY(node->monitoringStatus(temp).statusCode() == QOpcUa::UaStatusCode::BadAttributeIdInvalid);
+ attrs.remove(attrs.indexOf(temp));
+ }
+ QVERIFY(attrs.size() == 0);
}
void Tst_QOpcUaClient::dataChangeSubscriptionInvalidNode()
@@ -589,10 +696,17 @@ void Tst_QOpcUaClient::dataChangeSubscriptionInvalidNode()
OpcuaConnector connector(opcuaClient, m_endpoint);
QScopedPointer<QOpcUaNode> noDataNode(opcuaClient->node("ns=0;i=84"));
- QVERIFY(noDataNode != 0);
- QScopedPointer<QOpcUaSubscription> subscription(opcuaClient->createSubscription(100));
- QOpcUaMonitoredValue *result = subscription->addValue(noDataNode.data());
- QVERIFY(result == 0);
+ QSignalSpy monitoringEnabledSpy(noDataNode.data(), &QOpcUaNode::enableMonitoringFinished);
+
+ QOpcUaMonitoringParameters settings;
+ settings.setPublishingInterval(100);
+ noDataNode->enableMonitoring(QOpcUaNode::NodeAttribute::Value, settings);
+ monitoringEnabledSpy.wait();
+
+ QVERIFY(monitoringEnabledSpy.size() == 1);
+ QVERIFY(monitoringEnabledSpy.at(0).at(0).value<QOpcUaNode::NodeAttribute>() == QOpcUaNode::NodeAttribute::Value);
+ QVERIFY(noDataNode->monitoringStatus(QOpcUaNode::NodeAttribute::Value).statusCode() == QOpcUa::UaStatusCode::BadAttributeIdInvalid);
+ QVERIFY(noDataNode->monitoringStatus(QOpcUaNode::NodeAttribute::Value).subscriptionId() == 0);
}
void Tst_QOpcUaClient::methodCall()
@@ -618,58 +732,6 @@ void Tst_QOpcUaClient::methodCall()
QVERIFY(ret[0].type() == QVariant::Double && ret[0].value<double>() == 16);
}
-void Tst_QOpcUaClient::eventSubscription()
-{
- QFETCH(QOpcUaClient *, opcuaClient);
- OpcuaConnector connector(opcuaClient, m_endpoint);
-
- QSKIP("Events are not implemented in the open62541-based testserver");
- if (opcuaClient->backend() == QLatin1String("freeopcua")) {
- QSKIP("Event subscriptions do not yet work with the freeopcua backend");
- }
- if (opcuaClient->backend() == QLatin1String("open62541")) {
- QSKIP("Event subscriptions do not yet work with the open62541 backend");
- }
-
- QScopedPointer<QOpcUaNode> triggerNode(opcuaClient->node("ns=3;s=TriggerNode"));
- QVERIFY(triggerNode != 0);
-
- QScopedPointer<QOpcUaSubscription> subscription(opcuaClient->createSubscription(100));
- QOpcUaMonitoredEvent *monitoredEvent = subscription->addEvent(triggerNode.data());
- QVERIFY(monitoredEvent != 0);
-
- if (!monitoredEvent)
- QFAIL("can not monitor event");
-
- QSignalSpy monitorSpy(monitoredEvent, &QOpcUaMonitoredEvent::newEvent);
-
- QScopedPointer<QOpcUaNode> triggerVariable(opcuaClient->node("ns=3;s=TriggerVariable"));
- QVERIFY(triggerVariable != 0);
- WRITE_VALUE_ATTRIBUTE(triggerVariable, QVariant(double(0)), QOpcUa::Types::Double);
- WRITE_VALUE_ATTRIBUTE(triggerVariable, QVariant(double(1)), QOpcUa::Types::Double);
-
- QVERIFY(monitorSpy.wait());
- QVector<QVariant> val = monitorSpy.at(0).at(0).toList().toVector();
- QCOMPARE(val.size(), 3);
- QCOMPARE(val.at(0).type(), QVariant::String);
- QCOMPARE(val.at(1).type(), QVariant::String);
- QCOMPARE(val.at(2).type(), QVariant::Int);
-
- delete monitoredEvent;
-}
-
-void Tst_QOpcUaClient::eventSubscribeInvalidNode()
-{
- QFETCH(QOpcUaClient *, opcuaClient);
- OpcuaConnector connector(opcuaClient, m_endpoint);
-
- QScopedPointer<QOpcUaNode> noEventNode(opcuaClient->node(readWriteNode));
- QVERIFY(noEventNode != 0);
- QScopedPointer<QOpcUaSubscription> subscription(opcuaClient->createSubscription(100));
- QOpcUaMonitoredEvent *monitoredEvent = subscription->addEvent(noEventNode.data());
- QVERIFY(monitoredEvent == 0);
-}
-
void Tst_QOpcUaClient::readRange()
{
QFETCH(QOpcUaClient *, opcuaClient);