diff options
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); |