summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJannis Voelker <jannis.voelker@basyskom.com>2018-02-07 12:32:56 +0100
committerFrank Meerkoetter <frank.meerkoetter@basyskom.com>2018-07-04 12:26:33 +0000
commit6719f8c2661699dd327a550159930f2b0a421b5e (patch)
tree8b4be32132feb62b49863586b1254869a3dba43f /src
parent85109d00ac4954bf65cf0bed024f90f413886d83 (diff)
Add event support
This patch adds support for events in open62541 and uacpp. The EventFilterResult is returned in QOpcUaMonitoringParameters::filter(). The Qt based test server does not support events, so the event subscription test is manual. The open62541 example server tutorial_server_events.c is required to run the test. Change-Id: Ic2ff78077a3834d5f94a19585e13feaa706cf8ea Reviewed-by: Frank Meerkoetter <frank.meerkoetter@basyskom.com>
Diffstat (limited to 'src')
-rw-r--r--src/opcua/client/qopcuabackend_p.h1
-rw-r--r--src/opcua/client/qopcuaclientimpl.cpp8
-rw-r--r--src/opcua/client/qopcuaclientimpl_p.h2
-rw-r--r--src/opcua/client/qopcuamonitoringparameters.cpp163
-rw-r--r--src/opcua/client/qopcuamonitoringparameters.h26
-rw-r--r--src/opcua/client/qopcuanode.cpp28
-rw-r--r--src/opcua/client/qopcuanode.h2
-rw-r--r--src/opcua/client/qopcuanode_p.h9
-rw-r--r--src/opcua/client/qopcuanodeimpl_p.h1
-rw-r--r--src/opcua/client/qopcuatype.cpp865
-rw-r--r--src/opcua/client/qopcuatype.h204
-rw-r--r--src/opcua/doc/src/qtopcua.qdoc5
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.cpp271
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.h8
-rw-r--r--src/plugins/opcua/uacpp/quacppsubscription.cpp256
-rw-r--r--src/plugins/opcua/uacpp/quacppsubscription.h3
16 files changed, 1810 insertions, 42 deletions
diff --git a/src/opcua/client/qopcuabackend_p.h b/src/opcua/client/qopcuabackend_p.h
index 175231f..d8a3421 100644
--- a/src/opcua/client/qopcuabackend_p.h
+++ b/src/opcua/client/qopcuabackend_p.h
@@ -84,6 +84,7 @@ Q_SIGNALS:
void methodCallFinished(quint64 handle, QString methodNodeId, QVariant result, QOpcUa::UaStatusCode statusCode);
void attributeUpdated(quint64 handle, QOpcUaReadResult res);
+ void eventOccurred(quint64 handle, QVariantList fields);
void monitoringEnableDisable(quint64 handle, QOpcUa::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status);
void monitoringStatusChanged(quint64 handle, QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items,
QOpcUaMonitoringParameters param);
diff --git a/src/opcua/client/qopcuaclientimpl.cpp b/src/opcua/client/qopcuaclientimpl.cpp
index 30fd97c..6122d0e 100644
--- a/src/opcua/client/qopcuaclientimpl.cpp
+++ b/src/opcua/client/qopcuaclientimpl.cpp
@@ -80,6 +80,7 @@ void QOpcUaClientImpl::connectBackendWithClient(QOpcUaBackend *backend)
connect(backend, &QOpcUaBackend::methodCallFinished, this, &QOpcUaClientImpl::handleMethodCallFinished);
connect(backend, &QOpcUaBackend::browseFinished, this, &QOpcUaClientImpl::handleBrowseFinished);
connect(backend, &QOpcUaBackend::resolveBrowsePathFinished, this, &QOpcUaClientImpl::handleResolveBrowsePathFinished);
+ connect(backend, &QOpcUaBackend::eventOccurred, this, &QOpcUaClientImpl::handleNewEvent);
}
void QOpcUaClientImpl::handleAttributesRead(quint64 handle, QVector<QOpcUaReadResult> attr, QOpcUa::UaStatusCode serviceResult)
@@ -139,4 +140,11 @@ void QOpcUaClientImpl::handleResolveBrowsePathFinished(quint64 handle, QVector<Q
emit (*it)->resolveBrowsePathFinished(targets, path, status);
}
+void QOpcUaClientImpl::handleNewEvent(quint64 handle, QVariantList eventFields)
+{
+ auto it = m_handles.constFind(handle);
+ if (it != m_handles.constEnd() && !it->isNull())
+ emit (*it)->eventOccurred(eventFields);
+}
+
QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuaclientimpl_p.h b/src/opcua/client/qopcuaclientimpl_p.h
index 28b833e..2fd3620 100644
--- a/src/opcua/client/qopcuaclientimpl_p.h
+++ b/src/opcua/client/qopcuaclientimpl_p.h
@@ -96,6 +96,8 @@ private Q_SLOTS:
void handleResolveBrowsePathFinished(quint64 handle, QVector<QOpcUa::QBrowsePathTarget> targets,
QVector<QOpcUa::QRelativePathElement> path, QOpcUa::UaStatusCode status);
+ void handleNewEvent(quint64 handle, QVariantList eventFields);
+
signals:
void connected();
void disconnected();
diff --git a/src/opcua/client/qopcuamonitoringparameters.cpp b/src/opcua/client/qopcuamonitoringparameters.cpp
index 30cf91e..4599d06 100644
--- a/src/opcua/client/qopcuamonitoringparameters.cpp
+++ b/src/opcua/client/qopcuamonitoringparameters.cpp
@@ -437,7 +437,7 @@ QVariant QOpcUaMonitoringParameters::filter() const
/*!
Request \l DataChangeFilter \a filter as filter for the monitored item.
- \sa setFilter()
+ \sa setFilter() setEventFilter()
*/
void QOpcUaMonitoringParameters::setDataChangeFilter(const QOpcUaMonitoringParameters::DataChangeFilter &filter)
{
@@ -445,11 +445,20 @@ void QOpcUaMonitoringParameters::setDataChangeFilter(const QOpcUaMonitoringParam
}
/*!
+ Request \a eventFilter as filter for the monitored item.
+ \sa setFilter() setDataChangeFilter()
+*/
+void QOpcUaMonitoringParameters::setEventFilter(const EventFilter &eventFilter)
+{
+ d_ptr->filter = QVariant::fromValue(eventFilter);
+}
+
+/*!
Request \a filter as filter for the monitored item.
For general use, the type-safe versions that are listed below are preferred.
- \sa setDataChangeFilter()
+ \sa setDataChangeFilter() setEventFilter()
*/
void QOpcUaMonitoringParameters::setFilter(const QVariant &filter)
{
@@ -628,4 +637,154 @@ QOpcUaMonitoringParameters::DataChangeFilter::operator QVariant() const
return QVariant::fromValue(*this);
}
+/*!
+ \class QOpcUaMonitoringParameters::EventFilter
+ \inmodule QtOpcUa
+ \inheaderfile QOpcUaMonitoringParameters
+ \brief Defines an EventFilter for a monitored item
+
+ An event filter is required for monitoring events on the server.
+ It consists of \c select clauses and a \c where clause.
+
+ The \c select clauses are used to specify the data the user wants to receive when an event occurs.
+ It consists of \l {QOpcUa::QSimpleAttributeOperand} simple attribute operands which select
+ attributes of child nodes of an event type, for example the value attribute of the "Message"
+ property of BaseEventType.
+
+ The \c where clause is used to restrict the reported events by matching against certain criteria.
+ Several operators and four different operand types allow filtering based on the values of the
+ attributes of the child nodes of an event type.
+
+ Filters can be constructed using the setter or the streaming operator. Streaming a \l QOpcUa::QSimpleAttributeOperand
+ into an event filter adds a new \c select clause to the filter, a \l QOpcUa::QContentFilterElement is appended to the \c where clause.
+ A content filter element can be constructed by streaming operands of the types \l QOpcUa::QLiteralOperand,
+ \l QOpcUa::QElementOperand, \l QOpcUa::QAttributeOperand and \l QOpcUa::QSimpleAttributeOperand and an operator into a content
+ filter element. Only the last operator is used, previous operators will be discarded.
+
+ The following EventFilter tells the server to report the value of the "Message" field for events that have a "Severity" field with value >= 500:
+
+ \code
+ QOpcUaMonitoringParameters::EventFilter filter;
+ filter << QOpcUa::QSimpleAttributeOperand("Message"); // Select clause of the filter
+
+ QOpcUa::QContentFilterElement condition;
+ condition << OpcUa::QContentFilterElement::FilterOperator::GreaterThanOrEqual;
+ condition << QOpcUa::QSimpleAttributeOperand("Severity");
+ condition << QOpcUa::QLiteralOperand(500, QOpcUa::Types::UInt16);
+ filter << condition; // Where clause of the filter
+ \endcode
+
+ For a more complex example with two conditions, see \l QOpcUa::QElementOperand.
+*/
+
+class QOpcUaMonitoringParameters::EventFilterData : public QSharedData
+{
+public:
+ QVector<QOpcUa::QSimpleAttributeOperand> selectClauses;
+ QVector<QOpcUa::QContentFilterElement> whereClause;
+};
+
+QOpcUaMonitoringParameters::EventFilter::EventFilter()
+ : data(new EventFilterData)
+{
+}
+
+/*!
+ Constructs an \l event filter from \a rhs.
+*/
+QOpcUaMonitoringParameters::EventFilter::EventFilter(const QOpcUaMonitoringParameters::EventFilter &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Sets the values from \a rhs in this event filter.
+*/
+QOpcUaMonitoringParameters::EventFilter &QOpcUaMonitoringParameters::EventFilter::operator=(const QOpcUaMonitoringParameters::EventFilter &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Adds the content filter element \a whereClauseElement to the where clause of this event filter.
+*/
+QOpcUaMonitoringParameters::EventFilter &QOpcUaMonitoringParameters::EventFilter::operator<<(const QOpcUa::QContentFilterElement &whereClauseElement)
+{
+ whereClauseRef().append(whereClauseElement);
+ return *this;
+}
+
+/*!
+ Adds the simple attribute operand \a selectClauseElement to the select clause of this content filter element.
+*/
+QOpcUaMonitoringParameters::EventFilter &QOpcUaMonitoringParameters::EventFilter::operator<<(const QOpcUa::QSimpleAttributeOperand &selectClauseElement)
+{
+ selectClausesRef().append(selectClauseElement);
+ return *this;
+}
+
+/*!
+ Converts this \l event filter to \l QVariant.
+*/
+QOpcUaMonitoringParameters::EventFilter::operator const QVariant()
+{
+ return QVariant::fromValue(*this);
+}
+
+QOpcUaMonitoringParameters::EventFilter::~EventFilter()
+{
+}
+
+/*!
+ Returns the content filter used to restrict the reported events to events matching certain criteria.
+*/
+QVector<QOpcUa::QContentFilterElement> QOpcUaMonitoringParameters::EventFilter::whereClause() const
+{
+ return data->whereClause;
+}
+
+/*!
+ Returns a reference to the where clause.
+
+ \sa whereClause().
+*/
+QVector<QOpcUa::QContentFilterElement> &QOpcUaMonitoringParameters::EventFilter::whereClauseRef()
+{
+ return data->whereClause;
+}
+
+/*!
+ Sets the where clause to \a value.
+*/
+void QOpcUaMonitoringParameters::EventFilter::setWhereClause(const QVector<QOpcUa::QContentFilterElement> &value)
+{
+ data->whereClause = value;
+}
+
+/*!
+ Returns the selected event fields that shall be included when a new event is reported.
+*/
+QVector<QOpcUa::QSimpleAttributeOperand> QOpcUaMonitoringParameters::EventFilter::selectClauses() const
+{
+ return data->selectClauses;
+}
+
+/*!
+ Returns a reference to the select clauses.
+*/
+QVector<QOpcUa::QSimpleAttributeOperand> &QOpcUaMonitoringParameters::EventFilter::selectClausesRef()
+{
+ return data->selectClauses;
+}
+
+/*!
+ Sets the select clauses to \a value.
+*/
+void QOpcUaMonitoringParameters::EventFilter::setSelectClauses(const QVector<QOpcUa::QSimpleAttributeOperand> &value)
+{
+ data->selectClauses = value;
+}
+
QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuamonitoringparameters.h b/src/opcua/client/qopcuamonitoringparameters.h
index f357fa8..86ce093 100644
--- a/src/opcua/client/qopcuamonitoringparameters.h
+++ b/src/opcua/client/qopcuamonitoringparameters.h
@@ -116,6 +116,30 @@ public:
QSharedDataPointer<DataChangeFilterData> data;
};
+ class EventFilterData;
+ class Q_OPCUA_EXPORT EventFilter
+ {
+ public:
+ EventFilter();
+ EventFilter(const EventFilter &);
+ EventFilter &operator=(const EventFilter &);
+ operator QVariant const();
+ EventFilter &operator<<(const QOpcUa::QContentFilterElement &whereClauseElement);
+ EventFilter &operator<<(const QOpcUa::QSimpleAttributeOperand &selectClauseElement);
+ ~EventFilter();
+
+ QVector<QOpcUa::QSimpleAttributeOperand> selectClauses() const;
+ QVector<QOpcUa::QSimpleAttributeOperand> &selectClausesRef();
+ void setSelectClauses(const QVector<QOpcUa::QSimpleAttributeOperand> &value);
+
+ QVector<QOpcUa::QContentFilterElement> whereClause() const;
+ QVector<QOpcUa::QContentFilterElement> &whereClauseRef();
+ void setWhereClause(const QVector<QOpcUa::QContentFilterElement> &value);
+
+ private:
+ QSharedDataPointer<QOpcUaMonitoringParameters::EventFilterData> data;
+ };
+
QOpcUaMonitoringParameters();
~QOpcUaMonitoringParameters();
QOpcUaMonitoringParameters(double publishingInterval, SubscriptionType shared = SubscriptionType::Shared, quint32 subscriptionId = 0);
@@ -126,6 +150,7 @@ public:
void setSamplingInterval(double samplingInterval);
QVariant filter() const;
void setDataChangeFilter(const QOpcUaMonitoringParameters::DataChangeFilter &filter);
+ void setEventFilter(const QOpcUaMonitoringParameters::EventFilter &eventFilter);
void setFilter(const QVariant &filter);
quint32 queueSize() const;
void setQueueSize(quint32 queueSize);
@@ -173,5 +198,6 @@ Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::DataChangeFilter::DeadbandType)
Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::Parameter)
Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::Parameters)
Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::MonitoringMode)
+Q_DECLARE_METATYPE(QOpcUaMonitoringParameters::EventFilter)
#endif // QOPCUAMONITORINGPARAMETERS_H
diff --git a/src/opcua/client/qopcuanode.cpp b/src/opcua/client/qopcuanode.cpp
index 8fe99b9..2554d49 100644
--- a/src/opcua/client/qopcuanode.cpp
+++ b/src/opcua/client/qopcuanode.cpp
@@ -214,6 +214,14 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn void QOpcUaNode::eventOccurred(QVariantList eventFields)
+
+ This signal is emitted after a new event has been received.
+
+ \a eventFields contains the values of the event fields in the order specified in the \c select clause of the event filter.
+*/
+
+/*!
\fn QOpcUa::NodeAttributes QOpcUaNode::mandatoryBaseAttributes()
Contains all mandatory attributes of the OPC UA base node class.
@@ -348,6 +356,14 @@ QDateTime QOpcUaNode::serverTimestamp(QOpcUa::NodeAttribute attribute) const
There are multiple error cases in which a bad status code is generated: A subscription with the subscription id specified in \a settings 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.
+
+ The same method is used to enable event monitoring. Events are special objects in the OPC UA address space which contain information
+ about an event that has occurred. If an event is triggered on the server, an event monitored item collects selected values of
+ node attributes of the event object and its child nodes.
+ Every node that has an event source can be monitored for events.
+ To monitor a node for events, the attribute \l {QOpcUa::NodeAttribute} {EventNotifier} must be monitored using an EventFilter which contains the event fields
+ the user needs and optionally a where clause which is used to filter events by criteria (for more details, see \l QOpcUaMonitoringParameters::EventFilter).
+
*/
bool QOpcUaNode::enableMonitoring(QOpcUa::NodeAttributes attr, const QOpcUaMonitoringParameters &settings)
{
@@ -396,6 +412,18 @@ QOpcUaMonitoringParameters QOpcUaNode::monitoringStatus(QOpcUa::NodeAttribute at
}
/*!
+ Modifies an existing event monitoring to use \a eventFilter as event filter.
+
+ Returns \c true if the filter modification request has been successfully dispatched to the backend.
+
+ \l monitoringStatusChanged for \l {QOpcUa::NodeAttribute} {EventNotifier} is emitted after the operation has finished.
+*/
+bool QOpcUaNode::modifyEventFilter(const QOpcUaMonitoringParameters::EventFilter &eventFilter)
+{
+ return modifyMonitoring(QOpcUa::NodeAttribute::EventNotifier, QOpcUaMonitoringParameters::Parameter::Filter, QVariant::fromValue(eventFilter));
+}
+
+/*!
Modifies an existing data change monitoring to use \a filter as data change filter.
Returns \c true if the filter modification request has been successfully dispatched to the backend.
diff --git a/src/opcua/client/qopcuanode.h b/src/opcua/client/qopcuanode.h
index ec5f2ff..607a8e2 100644
--- a/src/opcua/client/qopcuanode.h
+++ b/src/opcua/client/qopcuanode.h
@@ -82,6 +82,7 @@ public:
bool disableMonitoring(QOpcUa::NodeAttributes attr);
bool modifyMonitoring(QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameter item, const QVariant &value);
QOpcUaMonitoringParameters monitoringStatus(QOpcUa::NodeAttribute attr);
+ bool modifyEventFilter(const QOpcUaMonitoringParameters::EventFilter &eventFilter);
bool modifyDataChangeFilter(QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::DataChangeFilter &filter);
bool browseChildren(QOpcUa::ReferenceTypeId referenceType = QOpcUa::ReferenceTypeId::HierarchicalReferences,
@@ -98,6 +99,7 @@ Q_SIGNALS:
void attributeRead(QOpcUa::NodeAttributes attributes);
void attributeWritten(QOpcUa::NodeAttribute attribute, QOpcUa::UaStatusCode statusCode);
void attributeUpdated(QOpcUa::NodeAttribute attr, QVariant value);
+ void eventOccurred(QVariantList eventFields);
void monitoringStatusChanged(QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items,
QOpcUa::UaStatusCode statusCode);
diff --git a/src/opcua/client/qopcuanode_p.h b/src/opcua/client/qopcuanode_p.h
index 5d0c52b..fb09176 100644
--- a/src/opcua/client/qopcuanode_p.h
+++ b/src/opcua/client/qopcuanode_p.h
@@ -180,6 +180,13 @@ public:
Q_Q(QOpcUaNode);
emit q->resolveBrowsePathFinished(targets, path, status);
});
+
+ m_eventOccurredConnection = QObject::connect(impl, &QOpcUaNodeImpl::eventOccurred,
+ [this](QVariantList eventFields)
+ {
+ Q_Q(QOpcUaNode);
+ emit q->eventOccurred(eventFields);
+ });
}
~QOpcUaNodePrivate()
@@ -192,6 +199,7 @@ public:
QObject::disconnect(m_methodCallFinishedConnection);
QObject::disconnect(m_browseFinishedConnection);
QObject::disconnect(m_resolveBrowsePathFinishedConnection);
+ QObject::disconnect(m_eventOccurredConnection);
}
QScopedPointer<QOpcUaNodeImpl> m_impl;
@@ -208,6 +216,7 @@ public:
QMetaObject::Connection m_methodCallFinishedConnection;
QMetaObject::Connection m_browseFinishedConnection;
QMetaObject::Connection m_resolveBrowsePathFinishedConnection;
+ QMetaObject::Connection m_eventOccurredConnection;
};
QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuanodeimpl_p.h b/src/opcua/client/qopcuanodeimpl_p.h
index 2475da4..edbdf56 100644
--- a/src/opcua/client/qopcuanodeimpl_p.h
+++ b/src/opcua/client/qopcuanodeimpl_p.h
@@ -99,6 +99,7 @@ Q_SIGNALS:
void browseFinished(QVector<QOpcUaReferenceDescription> children, QOpcUa::UaStatusCode statusCode);
void attributeUpdated(QOpcUa::NodeAttribute attr, QOpcUaReadResult value);
+ void eventOccurred(QVariantList eventFields);
void monitoringEnableDisable(QOpcUa::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status);
void monitoringStatusChanged(QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items,
QOpcUaMonitoringParameters param);
diff --git a/src/opcua/client/qopcuatype.cpp b/src/opcua/client/qopcuatype.cpp
index 1f728a5..906cd15 100644
--- a/src/opcua/client/qopcuatype.cpp
+++ b/src/opcua/client/qopcuatype.cpp
@@ -1923,4 +1923,869 @@ void QOpcUa::QBrowsePathTarget::setTargetId(const QOpcUa::QExpandedNodeId &value
data->targetId = value;
}
+/*!
+ \class QOpcUa::QContentFilterElement
+ \inmodule QtOpcUa
+ \inheaderfile QtOpcUa/qopcuatype.h
+ \brief The OPC UA ContentFilterElement
+
+ A content filter element contains an operator and a list of operands.
+ There are four different operator types which contain literal values, references to
+ attributes of nodes or to other content filter elements.
+
+ A combination of one or more content filter elements makes a content filter which is used
+ by the server to filter data for the criteria defined by the content filter elements.
+ For example, the where clause of an event filter is a content filter which is used to decide
+ if a notification is generated for an event.
+*/
+
+/*!
+ \enum QContentFilterElement::FilterOperator
+
+ FilterOperator enumerates all possible operators for a ContentFilterElement that are specified in
+ OPC-UA part 4, Tables 115 and 116.
+
+ \value Equals
+ \value IsNull
+ \value GreaterThan
+ \value LessThan
+ \value GreaterThanOrEqual
+ \value LessThanOrEqual
+ \value Like
+ \value Not
+ \value Between
+ \value InList
+ \value And
+ \value Or
+ \value Cast
+ \value InView
+ \value OfType
+ \value RelatedTo
+ \value BitwiseAnd
+ \value BitwiseOr
+*/
+
+class QOpcUa::QContentFilterElementData : public QSharedData
+{
+public:
+ QOpcUa::QContentFilterElement::FilterOperator filterOperator;
+ QVector<QVariant> filterOperands;
+};
+
+QOpcUa::QContentFilterElement::QContentFilterElement()
+ : data(new QContentFilterElementData)
+{
+}
+
+/*!
+ Constructs a content filter element from \a rhs.
+*/
+QOpcUa::QContentFilterElement::QContentFilterElement(const QOpcUa::QContentFilterElement &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Sets the values from \a rhs in this content filter element.
+*/
+QOpcUa::QContentFilterElement &QOpcUa::QContentFilterElement::operator=(const QOpcUa::QContentFilterElement &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Converts this content filter element to \l QVariant.
+*/
+QOpcUa::QContentFilterElement::operator QVariant() const
+{
+ return QVariant::fromValue(*this);
+}
+
+/*!
+ Returns the operands of the filter element.
+*/
+QVector<QVariant> QOpcUa::QContentFilterElement::filterOperands() const
+{
+ return data->filterOperands;
+}
+
+/*!
+ Returns a reference to the filter operands.
+
+ \sa filterOperands()
+*/
+QVector<QVariant> &QOpcUa::QContentFilterElement::filterOperandsRef()
+{
+ return data->filterOperands;
+}
+
+/*!
+ Sets the filter operands for this content filter element to \a value.
+ Supported classes are \l QOpcUa::QElementOperand, \l QOpcUa::QLiteralOperand,
+ \l QOpcUa::QSimpleAttributeOperand and \l QOpcUa::QAttributeOperand.
+*/
+void QOpcUa::QContentFilterElement::setFilterOperands(const QVector<QVariant> &value)
+{
+ data->filterOperands = value;
+}
+
+/*!
+ Returns the filter operator.
+*/
+QOpcUa::QContentFilterElement::FilterOperator QOpcUa::QContentFilterElement::filterOperator() const
+{
+ return data->filterOperator;
+}
+
+/*!
+ Sets the operator that is applied to the filter operands to \a value.
+*/
+void QOpcUa::QContentFilterElement::setFilterOperator(const QOpcUa::QContentFilterElement::FilterOperator &value)
+{
+ data->filterOperator = value;
+}
+
+QOpcUa::QContentFilterElement::~QContentFilterElement()
+{
+}
+
+/*!
+ Sets filter operator \a op in this content filter element.
+ If multiple operators are streamed into one content filter element, only the last operator is used.
+ All others are discarded.
+*/
+QOpcUa::QContentFilterElement &QOpcUa::QContentFilterElement::operator<<(QOpcUa::QContentFilterElement::FilterOperator op)
+{
+ setFilterOperator(op);
+ return *this;
+}
+
+/*!
+ Adds the simple attribute operand \a op to the operands list of this content filter element.
+*/
+QOpcUa::QContentFilterElement &QOpcUa::QContentFilterElement::operator<<(const QOpcUa::QSimpleAttributeOperand &op)
+{
+ filterOperandsRef().append(op);
+ return *this;
+}
+
+/*!
+ Adds the attribute operand \a op to the operands list of this content filter element.
+*/
+QOpcUa::QContentFilterElement &QOpcUa::QContentFilterElement::operator<<(const QOpcUa::QAttributeOperand &op)
+{
+ filterOperandsRef().append(op);
+ return *this;
+}
+
+/*!
+ Adds the literal operand \a op to the operands list of this content filter element.
+*/
+QOpcUa::QContentFilterElement &QOpcUa::QContentFilterElement::operator<<(const QOpcUa::QLiteralOperand &op)
+{
+ filterOperandsRef().append(op);
+ return *this;
+}
+
+/*!
+ Adds the element operand \a op to the operands list of this content filter element.
+*/
+QOpcUa::QContentFilterElement &QOpcUa::QContentFilterElement::operator<<(const QOpcUa::QElementOperand &op)
+{
+ filterOperandsRef().append(op);
+ return *this;
+}
+
+/*!
+ \class QOpcUa::QElementOperand
+ \inmodule QtOpcUa
+ \inheaderfile QtOpcUa/qopcuatype.h
+ \brief The OPC UA ElementOperand type
+
+ The ElementOperand is defined in OPC-UA part 4, 7.4.4.2.
+ It is used to identify another element in the filter by its index
+ (the first element has the index 0).
+
+ This is required to create complex filters, for example to reference
+ the two operands of the AND operation in ((Severity > 500) AND (Message == "TestString")).
+ The first step is to create content filter elements for the two conditions (Severity > 500)
+ and (Message == "TestString"). A third content filter element is required to create an AND
+ combination of the two conditions. It consists of the AND operator and two element operands
+ with the indices of the two conditions created before:
+
+ \code
+ QOpcUaMonitoringParameters::EventFilter filter;
+ ...
+ // setup select clauses
+ ...
+ QOpcUa::QContentFilterElement condition1;
+ QOpcUa::QContentFilterElement condition2;
+ QOpcUa::QContentFilterElement condition3;
+ condition1 << QOpcUa::QContentFilterElement::FilterOperator::GreaterThan << QOpcUa::QSimpleAttributeOperand("Severity") <<
+ QOpcUa::QLiteralOperand(quint16(500), QOpcUa::Types::UInt16);
+ condition2 << QOpcUa::QContentFilterElement::FilterOperator::Equals << QOpcUa::QSimpleAttributeOperand("Message") <<
+ QOpcUa::QLiteralOperand("TestString", QOpcUa::Types::String);
+ condition3 << QOpcUa::QContentFilterElement::FilterOperator::And << QOpcUa::QElementOperand(0) << QOpcUa::QElementOperand(1);
+ filter << condition1 << condition2 << condition3;
+ \endcode
+*/
+
+class QOpcUa::QElementOperandData : public QSharedData
+{
+public:
+ quint32 index;
+};
+
+QOpcUa::QElementOperand::QElementOperand()
+ : data(new QOpcUa::QElementOperandData)
+{
+}
+
+/*!
+ Constructs an element operand from \a rhs.
+*/
+QOpcUa::QElementOperand::QElementOperand(const QOpcUa::QElementOperand &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Constructs an element operand with index \a index.
+*/
+QOpcUa::QElementOperand::QElementOperand(quint32 index)
+ : data(new QOpcUa::QElementOperandData)
+{
+ setIndex(index);
+}
+
+/*!
+ Sets the values from \a rhs in this element operand.
+*/
+QOpcUa::QElementOperand &QOpcUa::QElementOperand::operator=(const QOpcUa::QElementOperand &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Converts this element operand to \l QVariant.
+*/
+QOpcUa::QElementOperand::operator QVariant() const
+{
+ return QVariant::fromValue(*this);
+}
+
+QOpcUa::QElementOperand::~QElementOperand()
+{
+}
+
+/*!
+ Returns the index of the filter element that is going to be used as operand.
+*/
+quint32 QOpcUa::QElementOperand::index() const
+{
+ return data->index;
+}
+
+/*!
+ Sets the index of the filter element that is going to be used as operand to \a value.
+*/
+void QOpcUa::QElementOperand::setIndex(const quint32 &value)
+{
+ data->index = value;
+}
+
+/*!
+ \class QOpcUa::QLiteralOperand
+ \inmodule QtOpcUa
+ \inheaderfile QtOpcUa/qopcuatype.h
+ \brief The OPC UA LiteralOperand type
+
+ The LiteralOperand is defined in OPC-UA part 4, 7.4.4.3.
+ It contains a literal value that is to be used as operand.
+*/
+class QOpcUa::QLiteralOperandData : public QSharedData
+{
+public:
+ QVariant value;
+ QOpcUa::Types type;
+};
+
+QOpcUa::QLiteralOperand::QLiteralOperand()
+ : data(new QOpcUa::QLiteralOperandData)
+{
+ data->type = QOpcUa::Types::Undefined;
+}
+
+/*!
+ Constructs a literal operand from \a rhs.
+*/
+QOpcUa::QLiteralOperand::QLiteralOperand(const QLiteralOperand &rhs)
+ : data(rhs.data)
+{
+ data->type = QOpcUa::Types::Undefined;
+}
+
+/*!
+ Constructs a literal operand of value \a value and type \a type.
+*/
+QOpcUa::QLiteralOperand::QLiteralOperand(const QVariant &value, QOpcUa::Types type)
+ : data(new QOpcUa::QLiteralOperandData)
+{
+ setValue(value);
+ setType(type);
+}
+
+/*!
+ Sets the values from \a rhs in this \l QLiteralOperand.
+*/
+QOpcUa::QLiteralOperand &QOpcUa::QLiteralOperand::operator=(const QOpcUa::QLiteralOperand &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Converts this literal operand to \l QVariant.
+*/
+QOpcUa::QLiteralOperand::operator QVariant() const
+{
+ return QVariant::fromValue(*this);
+}
+
+QOpcUa::QLiteralOperand::~QLiteralOperand()
+{
+}
+
+/*!
+ Returns the type of the value of the literal operand.
+*/
+QOpcUa::Types QOpcUa::QLiteralOperand::type() const
+{
+ return data->type;
+}
+
+/*!
+ Sets the type of the value of the literal operand to \a value.
+*/
+void QOpcUa::QLiteralOperand::setType(QOpcUa::Types value)
+{
+ data->type = value;
+}
+
+/*!
+ Returns the value of the literal operand.
+*/
+QVariant QOpcUa::QLiteralOperand::value() const
+{
+ return data->value;
+}
+
+/*!
+ Sets the value of the literal operand to \a value.
+*/
+void QOpcUa::QLiteralOperand::setValue(const QVariant &value)
+{
+ data->value = value;
+}
+
+/*!
+ \class QOpcUa::QSimpleAttributeOperand
+ \inmodule QtOpcUa
+ \inheaderfile QtOpcUa/qopcuatype.h
+ \brief The OPC UA SimpleAttributeOperand type
+
+ The SimpleAttributeOperand is specified in OPC-UA part 4, 7.4.4.5.
+ It is used when a node attribute is required as operand.
+
+ For example, the following simple attribute operand represents the value
+ of the "Severity" field of the base event type:
+ \code
+ QOpcUa::QSimpleAttributeOperand("Severity");
+ \endcode
+*/
+class QOpcUa::QSimpleAttributeOperandData : public QSharedData
+{
+public:
+ QString typeId{QStringLiteral("ns=0;i=2041")}; // BaseEventType
+ QVector<QOpcUa::QQualifiedName> browsePath;
+ QOpcUa::NodeAttribute attributeId;
+ QString indexRange;
+};
+
+QOpcUa::QSimpleAttributeOperand::QSimpleAttributeOperand()
+ : data(new QSimpleAttributeOperandData)
+{
+}
+
+/*!
+ Constructs a simple attribute operand from \a rhs.
+*/
+QOpcUa::QSimpleAttributeOperand::QSimpleAttributeOperand(const QOpcUa::QSimpleAttributeOperand &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Constructs a simple attribute operand for attribute \a attribute of the direct child with the browse name
+ \a name in namespace \a namespaceIndex. \a typeId is the node id of a type definition node. The operand will
+ be restricted to instances of type \a typeId or a subtype.
+*/
+QOpcUa::QSimpleAttributeOperand::QSimpleAttributeOperand(const QString &name, quint16 namespaceIndex, const QString &typeId, QOpcUa::NodeAttribute attributeId)
+ : data(new QOpcUa::QSimpleAttributeOperandData)
+{
+ browsePathRef().append(QOpcUa::QQualifiedName(namespaceIndex, name));
+ setTypeId(typeId);
+ setAttributeId(attributeId);
+}
+
+/*!
+ Sets the values from \a rhs in this simple attribute operand.
+*/
+QOpcUa::QSimpleAttributeOperand &QOpcUa::QSimpleAttributeOperand::operator=(const QOpcUa::QSimpleAttributeOperand &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Converts this simple attribute operand to \l QVariant.
+*/
+QOpcUa::QSimpleAttributeOperand::operator QVariant() const
+{
+ return QVariant::fromValue(*this);
+}
+
+QOpcUa::QSimpleAttributeOperand::~QSimpleAttributeOperand()
+{
+}
+
+/*!
+ Returns the index range string.
+*/
+QString QOpcUa::QSimpleAttributeOperand::indexRange() const
+{
+ return data->indexRange;
+}
+
+/*!
+ Sets the index range string used to identify a single value or subset of the attribute's value to \a value.
+*/
+void QOpcUa::QSimpleAttributeOperand::setIndexRange(const QString &value)
+{
+ data->indexRange = value;
+}
+
+/*!
+ Returns the attribute of the node \l browsePath is pointing to.
+*/
+QOpcUa::NodeAttribute QOpcUa::QSimpleAttributeOperand::attributeId() const
+{
+ return data->attributeId;
+}
+
+/*!
+ Sets the attribute id to \a value.
+*/
+void QOpcUa::QSimpleAttributeOperand::setAttributeId(const QOpcUa::NodeAttribute &value)
+{
+ data->attributeId = value;
+}
+
+/*!
+ Returns the relative path to a node starting from \l typeId.
+*/
+QVector<QOpcUa::QQualifiedName> QOpcUa::QSimpleAttributeOperand::browsePath() const
+{
+ return data->browsePath;
+}
+
+/*!
+ Returns a reference to the browse path.
+
+ \sa browsePath().
+*/
+QVector<QOpcUa::QQualifiedName> &QOpcUa::QSimpleAttributeOperand::browsePathRef()
+{
+ return data->browsePath;
+}
+
+/*!
+ Sets the browse path to the node holding the attribute to \a value.
+*/
+void QOpcUa::QSimpleAttributeOperand::setBrowsePath(const QVector<QOpcUa::QQualifiedName> &value)
+{
+ data->browsePath = value;
+}
+
+/*!
+ Returns the node id of a type definition node.
+*/
+QString QOpcUa::QSimpleAttributeOperand::typeId() const
+{
+ return data->typeId;
+}
+
+/*!
+ Sets the node id of the type definition node to \a value. The operand will be of the type or one of its subtypes.
+*/
+void QOpcUa::QSimpleAttributeOperand::setTypeId(const QString &value)
+{
+ data->typeId = value;
+}
+
+/*!
+ \class QOpcUa::QAttributeOperand
+ \inmodule QtOpcUa
+ \inheaderfile QtOpcUa/qopcuatype.h
+ \brief The OPC UA AttributeOperand type
+
+ The AttributeOperand is defined in OPC-UA part 4, 7.4.4.4.
+ It has the same purpose as \l QSimpleAttributeOperand but has more configurable options.
+*/
+
+class QOpcUa::QAttributeOperandData : public QSharedData
+{
+public:
+ QString nodeId;
+ QString alias;
+ QVector<QOpcUa::QRelativePathElement> browsePath;
+ QOpcUa::NodeAttribute attributeId;
+ QString indexRange;
+};
+
+QOpcUa::QAttributeOperand::QAttributeOperand()
+ : data(new QAttributeOperandData)
+{
+}
+
+/*!
+ Constructs an attribute operand from \a rhs.
+*/
+QOpcUa::QAttributeOperand::QAttributeOperand(const QOpcUa::QAttributeOperand &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Sets the values from \a rhs in this attribute operand.
+*/
+QOpcUa::QAttributeOperand &QOpcUa::QAttributeOperand::operator=(const QOpcUa::QAttributeOperand &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Converts this attribute operand to \l QVariant.
+*/
+QOpcUa::QAttributeOperand::operator QVariant() const
+{
+ return QVariant::fromValue(*this);
+}
+
+QOpcUa::QAttributeOperand::~QAttributeOperand()
+{
+}
+
+/*!
+ Returns the index range string.
+*/
+QString QOpcUa::QAttributeOperand::indexRange() const
+{
+ return data->indexRange;
+}
+
+/*!
+ Sets the index range string used to identify a single value or subset of the attribute's value to \a value.
+*/
+void QOpcUa::QAttributeOperand::setIndexRange(const QString &value)
+{
+ data->indexRange = value;
+}
+
+/*!
+ Returns the attribute id for an attribute of the node \l browsePath is pointing to.
+*/
+QOpcUa::NodeAttribute QOpcUa::QAttributeOperand::attributeId() const
+{
+ return data->attributeId;
+}
+
+/*!
+ Sets the attribute id to \a value.
+*/
+void QOpcUa::QAttributeOperand::setAttributeId(const QOpcUa::NodeAttribute &value)
+{
+ data->attributeId = value;
+}
+
+/*!
+ Returns the browse path.
+*/
+QVector<QOpcUa::QRelativePathElement> QOpcUa::QAttributeOperand::browsePath() const
+{
+ return data->browsePath;
+}
+
+/*!
+ Returns a reference to the browse path.
+
+ \sa browsePath()
+*/
+QVector<QOpcUa::QRelativePathElement> &QOpcUa::QAttributeOperand::browsePathRef()
+{
+ return data->browsePath;
+}
+
+/*!
+ Sets the relative path to a node starting from \l nodeId() to \a value.
+*/
+void QOpcUa::QAttributeOperand::setBrowsePath(const QVector<QOpcUa::QRelativePathElement> &value)
+{
+ data->browsePath = value;
+}
+
+/*!
+ Returns the alias for this QAttributeOperand.
+*/
+QString QOpcUa::QAttributeOperand::alias() const
+{
+ return data->alias;
+}
+
+/*!
+ Sets the alias to \a value. This allows using this instance
+ as operand for other operations in the filter.
+*/
+void QOpcUa::QAttributeOperand::setAlias(const QString &value)
+{
+ data->alias = value;
+}
+
+/*!
+ Returns the node id of the type definition node.
+*/
+QString QOpcUa::QAttributeOperand::nodeId() const
+{
+ return data->nodeId;
+}
+
+/*!
+ Sets the node id of the type definition node to \a value.
+*/
+void QOpcUa::QAttributeOperand::setNodeId(const QString &value)
+{
+ data->nodeId = value;
+}
+
+/*!
+ \class QOpcUa::QContentFilterElementResult
+ \inmodule QtOPcUa
+ \inheaderfile QtOpcUa/qopcuatype.h
+ \brief The OPC UA ContentFilterElementResult
+
+ QContentFilterElementResult contains the status code for a
+ filter element and all its operands.
+*/
+
+class QOpcUa::QContentFilterElementResultData : public QSharedData
+{
+public:
+ QOpcUa::UaStatusCode statusCode;
+ QVector<QOpcUa::UaStatusCode> operandStatusCodes;
+};
+
+QOpcUa::QContentFilterElementResult::QContentFilterElementResult()
+ : data(new QOpcUa::QContentFilterElementResultData)
+{
+}
+
+/*!
+ Constructs a content filter element result from \a rhs.
+*/
+QOpcUa::QContentFilterElementResult::QContentFilterElementResult(const QOpcUa::QContentFilterElementResult &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Sets the values from \a rhs in this content filter element result.
+*/
+QOpcUa::QContentFilterElementResult &QOpcUa::QContentFilterElementResult::operator=(const QOpcUa::QContentFilterElementResult &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+QOpcUa::QContentFilterElementResult::~QContentFilterElementResult()
+{
+}
+
+/*!
+ Returns the status code for the filter element.
+*/
+QOpcUa::UaStatusCode QOpcUa::QContentFilterElementResult::statusCode() const
+{
+ return data->statusCode;
+}
+
+/*!
+ Sets the status code for the filter element to \a value.
+*/
+void QOpcUa::QContentFilterElementResult::setStatusCode(const QOpcUa::UaStatusCode &value)
+{
+ data->statusCode = value;
+}
+
+/*!
+ Returns the status codes for all filter operands in the order that was used in the filter.
+*/
+QVector<QOpcUa::UaStatusCode> QOpcUa::QContentFilterElementResult::operandStatusCodes() const
+{
+ return data->operandStatusCodes;
+}
+
+/*!
+ Sets the status codes for all filter operands to \a value.
+*/
+void QOpcUa::QContentFilterElementResult::setOperandStatusCodes(const QVector<QOpcUa::UaStatusCode> &value)
+{
+ data->operandStatusCodes = value;
+}
+
+/*!
+ Returns a reference to the operand status codes.
+
+ \sa operandStatusCodes()
+*/
+QVector<QOpcUa::UaStatusCode> &QOpcUa::QContentFilterElementResult::operandStatusCodesRef()
+{
+ return data->operandStatusCodes;
+}
+
+/*!
+ \class QOpcUa::QEventFilterResult
+ \inmodule QtOpcUa
+ \inheaderfile QtOpcUa/qopcuatype.h
+ \brief The OPCUA EventFilterResult
+
+ The EventFilterResult contains status codes for all elements of the \c select clauses
+ and all elements of the \c where clause.
+*/
+
+class QOpcUa::QEventFilterResultData : public QSharedData
+{
+public:
+ QVector<QOpcUa::UaStatusCode> selectClauseResults;
+ QVector<QOpcUa::QContentFilterElementResult> whereClauseResults;
+};
+
+QOpcUa::QEventFilterResult::QEventFilterResult()
+ : data(new QOpcUa::QEventFilterResultData)
+{
+}
+
+/*!
+ Constructs an event filter result from \a rhs.
+*/
+QOpcUa::QEventFilterResult::QEventFilterResult(const QOpcUa::QEventFilterResult &rhs)
+ : data(rhs.data)
+{
+}
+
+/*!
+ Sets the values from \a rhs in this event filter result.
+*/
+QOpcUa::QEventFilterResult &QOpcUa::QEventFilterResult::operator=(const QOpcUa::QEventFilterResult &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+QOpcUa::QEventFilterResult::~QEventFilterResult()
+{
+}
+
+/*!
+ Returns \c true if this event filter result is good.
+*/
+bool QOpcUa::QEventFilterResult::isGood() const
+{
+ for (auto status : qAsConst(data->selectClauseResults)) {
+ if (status != QOpcUa::UaStatusCode::Good)
+ return false;
+ }
+ for (QOpcUa::QContentFilterElementResult element : qAsConst(data->whereClauseResults)) {
+ if (element.statusCode() != QOpcUa::UaStatusCode::Good)
+ return false;
+ for (auto status : qAsConst(element.operandStatusCodesRef())) {
+ if (status != QOpcUa::UaStatusCode::Good)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*!
+ Returns the status codes for all elements of the \c where clause in the order that was used in the filter.
+*/
+QVector<QOpcUa::QContentFilterElementResult> QOpcUa::QEventFilterResult::whereClauseResults() const
+{
+ return data->whereClauseResults;
+}
+
+/*!
+ Returns a reference to the \c where clause results.
+
+ \sa whereClauseResults()
+*/
+QVector<QOpcUa::QContentFilterElementResult> &QOpcUa::QEventFilterResult::whereClauseResultsRef()
+{
+ return data->whereClauseResults;
+}
+
+/*!
+ Sets the where clause results to \a value.
+*/
+void QOpcUa::QEventFilterResult::setWhereClauseResults(const QVector<QOpcUa::QContentFilterElementResult> &value)
+{
+ data->whereClauseResults = value;
+}
+
+/*!
+ Returns the status codes for all elements of the \c select clauses in the order that was used in the filter.
+*/
+QVector<QOpcUa::UaStatusCode> QOpcUa::QEventFilterResult::selectClauseResults() const
+{
+ return data->selectClauseResults;
+}
+
+/*!
+ Returns a reference to the \c select clause results.
+
+ \sa selectClauseResults()
+*/
+QVector<QOpcUa::UaStatusCode> &QOpcUa::QEventFilterResult::selectClauseResultsRef()
+{
+ return data->selectClauseResults;
+}
+
+/*!
+ Sets the \¢ select clause results to \a value.
+*/
+void QOpcUa::QEventFilterResult::setSelectClauseResults(const QVector<QOpcUa::UaStatusCode> &value)
+{
+ data->selectClauseResults = value;
+}
+
QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuatype.h b/src/opcua/client/qopcuatype.h
index 84a34b0..9a89647 100644
--- a/src/opcua/client/qopcuatype.h
+++ b/src/opcua/client/qopcuatype.h
@@ -702,6 +702,203 @@ public:
private:
QSharedDataPointer<QOpcUa::QBrowsePathTargetData> data;
};
+
+// OPC-UA part 4, 7.4.4.2
+class QElementOperandData;
+class Q_OPCUA_EXPORT QElementOperand
+{
+public:
+ QElementOperand();
+ QElementOperand(const QElementOperand &);
+ QElementOperand(quint32 index);
+ QElementOperand &operator=(const QElementOperand &);
+ operator QVariant() const;
+ ~QElementOperand();
+
+ quint32 index() const;
+ void setIndex(const quint32 &value);
+
+private:
+ QSharedDataPointer<QOpcUa::QElementOperandData> data;
+};
+
+// OPC-UA part 4, 7.4.4.3
+class QLiteralOperandData;
+class Q_OPCUA_EXPORT QLiteralOperand
+{
+public:
+ QLiteralOperand();
+ QLiteralOperand(const QOpcUa::QLiteralOperand &);
+ QLiteralOperand(const QVariant &value, QOpcUa::Types type = QOpcUa::Types::Undefined);
+ QLiteralOperand &operator=(const QOpcUa::QLiteralOperand &);
+ operator QVariant() const;
+ ~QLiteralOperand();
+
+ QVariant value() const;
+ void setValue(const QVariant &value);
+
+ QOpcUa::Types type() const;
+ void setType(QOpcUa::Types value);
+
+private:
+ QSharedDataPointer<QOpcUa::QLiteralOperandData> data;
+};
+
+// OPC-UA part 4, 7.4.4.5
+class QSimpleAttributeOperandData;
+class Q_OPCUA_EXPORT QSimpleAttributeOperand
+{
+public:
+ QSimpleAttributeOperand();
+ QSimpleAttributeOperand(const QOpcUa::QSimpleAttributeOperand &);
+ QSimpleAttributeOperand(const QString &name, quint16 namespaceIndex = 0,
+ const QString &typeId = QStringLiteral("ns=0;i=2041"), // BaseEventType
+ QOpcUa::NodeAttribute attributeId = QOpcUa::NodeAttribute::Value);
+ QSimpleAttributeOperand &operator=(const QOpcUa::QSimpleAttributeOperand &);
+ operator QVariant() const;
+ ~QSimpleAttributeOperand();
+
+ QString typeId() const;
+ void setTypeId(const QString &value);
+
+ QVector<QOpcUa::QQualifiedName> browsePath() const;
+ QVector<QOpcUa::QQualifiedName> &browsePathRef();
+ void setBrowsePath(const QVector<QOpcUa::QQualifiedName> &value);
+
+ QOpcUa::NodeAttribute attributeId() const;
+ void setAttributeId(const QOpcUa::NodeAttribute &value);
+
+ QString indexRange() const;
+ void setIndexRange(const QString &value);
+
+private:
+ QSharedDataPointer<QOpcUa::QSimpleAttributeOperandData> data;
+};
+
+// OPC-UA part 4, 7.4.4.4
+class QAttributeOperandData;
+class Q_OPCUA_EXPORT QAttributeOperand
+{
+public:
+ QAttributeOperand();
+ QAttributeOperand(const QOpcUa::QAttributeOperand &);
+ QAttributeOperand &operator=(const QOpcUa::QAttributeOperand &);
+ operator QVariant() const;
+ ~QAttributeOperand();
+
+ QString nodeId() const;
+ void setNodeId(const QString &value);
+
+ QString alias() const;
+ void setAlias(const QString &value);
+
+ QVector<QOpcUa::QRelativePathElement> browsePath() const;
+ QVector<QOpcUa::QRelativePathElement> &browsePathRef();
+ void setBrowsePath(const QVector<QOpcUa::QRelativePathElement> &value);
+
+ QOpcUa::NodeAttribute attributeId() const;
+ void setAttributeId(const QOpcUa::NodeAttribute &value);
+
+ QString indexRange() const;
+ void setIndexRange(const QString &value);
+
+private:
+ QSharedDataPointer<QOpcUa::QAttributeOperandData> data;
+};
+
+class QContentFilterElementData;
+class Q_OPCUA_EXPORT QContentFilterElement
+{
+public:
+ QContentFilterElement();
+ QContentFilterElement(const QContentFilterElement &);
+ QContentFilterElement &operator=(const QContentFilterElement &);
+ operator QVariant() const;
+ ~QContentFilterElement();
+
+ // Specified in OPC-UA part 4, Tables 115 and 116
+ enum FilterOperator : quint32 {
+ Equals = 0,
+ IsNull = 1,
+ GreaterThan = 2,
+ LessThan = 3,
+ GreaterThanOrEqual = 4,
+ LessThanOrEqual = 5,
+ Like = 6,
+ Not = 7,
+ Between = 8,
+ InList = 9,
+ And = 10,
+ Or = 11,
+ Cast = 12,
+ InView = 13,
+ OfType = 14,
+ RelatedTo = 15,
+ BitwiseAnd = 16,
+ BitwiseOr = 17
+ };
+
+ QContentFilterElement &operator<<(FilterOperator op);
+ QContentFilterElement &operator<<(const QSimpleAttributeOperand &op);
+ QContentFilterElement &operator<<(const QAttributeOperand &op);
+ QContentFilterElement &operator<<(const QLiteralOperand &op);
+ QContentFilterElement &operator<<(const QElementOperand &op);
+
+
+ QOpcUa::QContentFilterElement::FilterOperator filterOperator() const;
+ void setFilterOperator(const QOpcUa::QContentFilterElement::FilterOperator &value);
+
+ QVector<QVariant> filterOperands() const;
+ QVector<QVariant> &filterOperandsRef();
+ void setFilterOperands(const QVector<QVariant> &value);
+
+private:
+ QSharedDataPointer<QOpcUa::QContentFilterElementData> data;
+};
+
+class QContentFilterElementResultData;
+class Q_OPCUA_EXPORT QContentFilterElementResult
+{
+public:
+ QContentFilterElementResult();
+ QContentFilterElementResult(const QOpcUa::QContentFilterElementResult &);
+ QContentFilterElementResult &operator=(const QOpcUa::QContentFilterElementResult &);
+ ~QContentFilterElementResult();
+
+ QOpcUa::UaStatusCode statusCode() const;
+ void setStatusCode(const QOpcUa::UaStatusCode &value);
+
+ QVector<QOpcUa::UaStatusCode> operandStatusCodes() const;
+ QVector<QOpcUa::UaStatusCode> &operandStatusCodesRef();
+ void setOperandStatusCodes(const QVector<QOpcUa::UaStatusCode> &value);
+
+private:
+ QSharedDataPointer<QOpcUa::QContentFilterElementResultData> data;
+};
+
+class QEventFilterResultData;
+class Q_OPCUA_EXPORT QEventFilterResult
+{
+public:
+ QEventFilterResult();
+ QEventFilterResult(const QOpcUa::QEventFilterResult &);
+ QEventFilterResult &operator=(const QOpcUa::QEventFilterResult &);
+ ~QEventFilterResult();
+
+ bool isGood() const;
+
+ QVector<QOpcUa::UaStatusCode> selectClauseResults() const;
+ QVector<QOpcUa::UaStatusCode> &selectClauseResultsRef();
+ void setSelectClauseResults(const QVector<QOpcUa::UaStatusCode> &value);
+
+ QVector<QOpcUa::QContentFilterElementResult> whereClauseResults() const;
+ QVector<QOpcUa::QContentFilterElementResult> &whereClauseResultsRef();
+ void setWhereClauseResults(const QVector<QOpcUa::QContentFilterElementResult> &value);
+
+private:
+ QSharedDataPointer<QEventFilterResultData> data;
+};
+
}
Q_DECLARE_TYPEINFO(QOpcUa::Types, Q_PRIMITIVE_TYPE);
@@ -736,5 +933,12 @@ Q_DECLARE_METATYPE(QOpcUa::QXValue)
Q_DECLARE_METATYPE(QOpcUa::QExpandedNodeId)
Q_DECLARE_METATYPE(QOpcUa::QRelativePathElement)
Q_DECLARE_METATYPE(QOpcUa::QBrowsePathTarget)
+Q_DECLARE_METATYPE(QOpcUa::QContentFilterElement)
+Q_DECLARE_METATYPE(QOpcUa::QElementOperand)
+Q_DECLARE_METATYPE(QOpcUa::QLiteralOperand)
+Q_DECLARE_METATYPE(QOpcUa::QSimpleAttributeOperand)
+Q_DECLARE_METATYPE(QOpcUa::QAttributeOperand)
+Q_DECLARE_METATYPE(QOpcUa::QContentFilterElementResult)
+Q_DECLARE_METATYPE(QOpcUa::QEventFilterResult)
#endif // QOPCUATYPE
diff --git a/src/opcua/doc/src/qtopcua.qdoc b/src/opcua/doc/src/qtopcua.qdoc
index 4a5db11..5bef190 100644
--- a/src/opcua/doc/src/qtopcua.qdoc
+++ b/src/opcua/doc/src/qtopcua.qdoc
@@ -117,6 +117,11 @@
\li X
\li X
\row
+ \li Event subscriptions
+ \li X
+ \li X
+ \li
+ \row
\li Modify subscriptions / monitored Items
\li X
\li X
diff --git a/src/plugins/opcua/open62541/qopen62541subscription.cpp b/src/plugins/opcua/open62541/qopen62541subscription.cpp
index f791c9f..2239876 100644
--- a/src/plugins/opcua/open62541/qopen62541subscription.cpp
+++ b/src/plugins/opcua/open62541/qopen62541subscription.cpp
@@ -38,6 +38,7 @@
#include "qopen62541client.h"
#include "qopen62541node.h"
#include "qopen62541subscription.h"
+#include "qopen62541utils.h"
#include "qopen62541valueconverter.h"
#include "qopen62541utils.h"
#include <private/qopcuanode_p.h>
@@ -69,6 +70,21 @@ static void stateChangeHandler(UA_Client *client, UA_UInt32 subId, void *subCont
sub->sendTimeoutNotification();
}
+static void eventHandler(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext,
+ size_t numFields, UA_Variant *eventFields)
+{
+ Q_UNUSED(client);
+ Q_UNUSED(subId);
+ Q_UNUSED(subContext);
+
+ QOpen62541Subscription *subscription = static_cast<QOpen62541Subscription *>(monContext);
+
+ QVariantList list;
+ for (size_t i = 0; i < numFields; ++i)
+ list.append(QOpen62541ValueConverter::toQVariant(eventFields[i]));
+ subscription->eventReceived(monId, list);
+}
+
QOpen62541Subscription::QOpen62541Subscription(Open62541AsyncBackend *backend, const QOpcUaMonitoringParameters &settings)
: m_backend(backend)
, m_interval(settings.publishingInterval())
@@ -241,10 +257,27 @@ bool QOpen62541Subscription::addAttributeMonitoredItem(quint64 handle, QOpcUa::N
req.requestedParameters.queueSize = settings.queueSize() == 0 ? 1 : settings.queueSize();
req.requestedParameters.discardOldest = settings.discardOldest();
req.requestedParameters.clientHandle = ++m_clientHandle;
- if (settings.filter().type() == QVariant::UserType && settings.filter().userType() == QMetaType::type("QOpcUaMonitoringParameters::DataChangeFilter"))
- req.requestedParameters.filter = createFilter(settings.filter());
- UA_MonitoredItemCreateResult res = UA_Client_MonitoredItems_createDataChange(m_backend->m_uaclient, m_subscriptionId, UA_TIMESTAMPSTORETURN_BOTH, req, this, monitoredValueHandler, nullptr);
+ if (settings.filter().isValid()) {
+ UA_ExtensionObject filter = createFilter(settings.filter());
+ if (filter.content.decoded.data)
+ req.requestedParameters.filter = filter;
+ else {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not create monitored item, filter creation failed";
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadInternalError);
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
+ return false;
+ }
+ }
+
+ UA_MonitoredItemCreateResult res;
+
+ if (attr == QOpcUa::NodeAttribute::EventNotifier && settings.filter().canConvert<QOpcUaMonitoringParameters::EventFilter>())
+ res = UA_Client_MonitoredItems_createEvent(m_backend->m_uaclient, m_subscriptionId,
+ UA_TIMESTAMPSTORETURN_BOTH, req, this, eventHandler, nullptr);
+ else
+ res = UA_Client_MonitoredItems_createDataChange(m_backend->m_uaclient, m_subscriptionId, UA_TIMESTAMPSTORETURN_BOTH, req, this, monitoredValueHandler, nullptr);
UA_MonitoredItemCreateRequest_deleteMembers(&req);
@@ -271,7 +304,13 @@ bool QOpen62541Subscription::addAttributeMonitoredItem(quint64 handle, QOpcUa::N
temp->parameters = s;
temp->clientHandle = m_clientHandle;
- s.setFilter(QVariant());
+ if (settings.filter().canConvert<QOpcUaMonitoringParameters::EventFilter>())
+ s.setFilter(QVariant::fromValue(convertEventFilterResult(&res.filterResult)));
+ else
+ s.setFilter(QVariant());
+
+ UA_MonitoredItemCreateResult_deleteMembers(&res);
+
emit m_backend->monitoringEnableDisable(handle, attr, true, s);
return true;
@@ -342,6 +381,14 @@ void QOpen62541Subscription::sendTimeoutNotification()
m_timeout = true;
}
+void QOpen62541Subscription::eventReceived(UA_UInt32 monId, QVariantList list)
+{
+ auto item = m_itemIdToItemMapping.constFind(monId);
+ if (item == m_itemIdToItemMapping.constEnd())
+ return;
+ emit m_backend->eventOccurred(item.value()->handle, list);
+}
+
double QOpen62541Subscription::interval() const
{
return m_interval;
@@ -381,15 +428,13 @@ UA_ExtensionObject QOpen62541Subscription::createFilter(const QVariant &filterDa
UA_ExtensionObject obj;
UA_ExtensionObject_init(&obj);
- if (filterData.type() == QVariant::UserType && filterData.userType() == QMetaType::type("QOpcUaMonitoringParameters::DataChangeFilter")) {
- QOpcUaMonitoringParameters::DataChangeFilter temp = filterData.value<QOpcUaMonitoringParameters::DataChangeFilter>();
- UA_DataChangeFilter *filter = UA_DataChangeFilter_new();
- filter->deadbandType = static_cast<UA_UInt32>(temp.deadbandType());
- filter->deadbandValue = temp.deadbandValue();
- filter->trigger = static_cast<UA_DataChangeTrigger>(temp.trigger());
- obj.encoding = UA_EXTENSIONOBJECT_DECODED;
- obj.content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGEFILTER];
- obj.content.decoded.data = filter;
+ if (filterData.canConvert<QOpcUaMonitoringParameters::DataChangeFilter>()) {
+ createDataChangeFilter(filterData.value<QOpcUaMonitoringParameters::DataChangeFilter>(), &obj);
+ return obj;
+ }
+
+ if (filterData.canConvert<QOpcUaMonitoringParameters::EventFilter>()) {
+ createEventFilter(filterData.value<QOpcUaMonitoringParameters::EventFilter>(), &obj);
return obj;
}
@@ -399,6 +444,174 @@ UA_ExtensionObject QOpen62541Subscription::createFilter(const QVariant &filterDa
return obj;
}
+void QOpen62541Subscription::createDataChangeFilter(const QOpcUaMonitoringParameters::DataChangeFilter &filter, UA_ExtensionObject *out)
+{
+ UA_DataChangeFilter *uaFilter = UA_DataChangeFilter_new();
+ uaFilter->deadbandType = static_cast<UA_UInt32>(filter.deadbandType());
+ uaFilter->deadbandValue = filter.deadbandValue();
+ uaFilter->trigger = static_cast<UA_DataChangeTrigger>(filter.trigger());
+ out->encoding = UA_EXTENSIONOBJECT_DECODED;
+ out->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGEFILTER];
+ out->content.decoded.data = uaFilter;
+}
+
+void QOpen62541Subscription::createEventFilter(const QOpcUaMonitoringParameters::EventFilter &filter, UA_ExtensionObject *out)
+{
+ UA_EventFilter *uaFilter = UA_EventFilter_new();
+ UA_EventFilter_init(uaFilter);
+ out->encoding = UA_EXTENSIONOBJECT_DECODED;
+ out->content.decoded.data = uaFilter;
+ out->content.decoded.type = &UA_TYPES[UA_TYPES_EVENTFILTER];
+
+ convertSelectClause(filter, &uaFilter->selectClauses, &uaFilter->selectClausesSize);
+ if (!convertWhereClause(filter, &uaFilter->whereClause))
+ UA_ExtensionObject_deleteMembers(out);
+}
+
+bool QOpen62541Subscription::convertSelectClause(const QOpcUaMonitoringParameters::EventFilter &filter,
+ UA_SimpleAttributeOperand **selectClauses, size_t *size)
+{
+ if (!filter.selectClauses().isEmpty()) {
+ UA_SimpleAttributeOperand *select = static_cast<UA_SimpleAttributeOperand *>(
+ UA_Array_new(filter.selectClauses().size(), &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]));
+
+ for (int i = 0; i < filter.selectClauses().size(); ++i) {
+ UA_SimpleAttributeOperand_init(&select[i]);
+ if (!filter.selectClauses().at(i).typeId().isEmpty())
+ select[i].typeDefinitionId = Open62541Utils::nodeIdFromQString(filter.selectClauses().at(i).typeId());
+ select[i].browsePathSize = filter.selectClauses().at(i).browsePath().size();
+ if (select[i].browsePathSize) {
+ select[i].browsePath = static_cast<UA_QualifiedName *>(
+ UA_Array_new(select[i].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]));
+ for (size_t j = 0; j < select[i].browsePathSize; ++j)
+ QOpen62541ValueConverter::scalarFromQt<UA_QualifiedName, QOpcUa::QQualifiedName>(
+ filter.selectClauses().at(i).browsePath().at(j), &select[i].browsePath[j]);
+ }
+ QOpen62541ValueConverter::scalarFromQt<UA_String, QString>(filter.selectClauses().at(i).indexRange(),
+ &select[i].indexRange);
+ select[i].attributeId = QOpen62541ValueConverter::toUaAttributeId(filter.selectClauses().at(i).attributeId());
+ }
+
+ *selectClauses = select;
+ *size = filter.selectClauses().size();
+
+ return true;
+ }
+
+ *selectClauses = nullptr;
+ *size = 0;
+ return true;
+}
+
+bool QOpen62541Subscription::convertWhereClause(const QOpcUaMonitoringParameters::EventFilter &filter, UA_ContentFilter *result)
+{
+ if (!filter.whereClause().isEmpty()) {
+ result->elementsSize = filter.whereClause().size();
+ result->elements = static_cast<UA_ContentFilterElement *>(
+ UA_Array_new(filter.whereClause().size(), &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT]));
+ for (int i = 0; i < filter.whereClause().size(); ++i) {
+ UA_ContentFilterElement_init(&result->elements[i]);
+ result->elements[i].filterOperator = static_cast<UA_FilterOperator>(filter.whereClause().at(i).filterOperator());
+ result->elements[i].filterOperandsSize = filter.whereClause().at(i).filterOperands().size();
+ result->elements[i].filterOperands = static_cast<UA_ExtensionObject *>(
+ UA_Array_new(filter.whereClause().at(i).filterOperands().size(), &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]));
+ for (int j = 0; j < filter.whereClause().at(i).filterOperands().size(); ++j) {
+ UA_ExtensionObject_init(&result->elements[i].filterOperands[j]);
+ result->elements[i].filterOperands[j].encoding = UA_EXTENSIONOBJECT_DECODED;
+ const QVariant &currentOperand = filter.whereClause().at(i).filterOperands().at(j);
+ if (currentOperand.canConvert<QOpcUa::QElementOperand>()) {
+ UA_ElementOperand *op = UA_ElementOperand_new();
+ UA_ElementOperand_init(op);
+ op->index = currentOperand.value<QOpcUa::QElementOperand>().index();
+ result->elements[i].filterOperands[j].content.decoded.data = op;
+ result->elements[i].filterOperands[j].content.decoded.type = &UA_TYPES[UA_TYPES_ELEMENTOPERAND];
+ } else if (currentOperand.canConvert<QOpcUa::QLiteralOperand>()) {
+ UA_LiteralOperand *op = UA_LiteralOperand_new();
+ UA_LiteralOperand_init(op);
+ QOpcUa::QLiteralOperand litOp = currentOperand.value<QOpcUa::QLiteralOperand>();
+ op->value = QOpen62541ValueConverter::toOpen62541Variant(litOp.value(), litOp.type());
+ result->elements[i].filterOperands[j].content.decoded.data = op;
+ result->elements[i].filterOperands[j].content.decoded.type = &UA_TYPES[UA_TYPES_LITERALOPERAND];
+ } else if (currentOperand.canConvert<QOpcUa::QSimpleAttributeOperand>()) {
+ UA_SimpleAttributeOperand *op = UA_SimpleAttributeOperand_new();
+ UA_SimpleAttributeOperand_init(op);
+ QOpcUa::QSimpleAttributeOperand operand = currentOperand.value<QOpcUa::QSimpleAttributeOperand>();
+ op->attributeId = QOpen62541ValueConverter::toUaAttributeId(operand.attributeId());
+ QOpen62541ValueConverter::scalarFromQt<UA_String, QString>(operand.indexRange(), &op->indexRange);
+ if (!operand.typeId().isEmpty())
+ op->typeDefinitionId = Open62541Utils::nodeIdFromQString(operand.typeId());
+ op->browsePathSize = operand.browsePath().size();
+ op->browsePath = static_cast<UA_QualifiedName *>(UA_Array_new(operand.browsePath().size(),
+ &UA_TYPES[UA_TYPES_QUALIFIEDNAME]));
+ for (int k = 0; k < operand.browsePath().size(); ++k)
+ QOpen62541ValueConverter::scalarFromQt<UA_QualifiedName, QOpcUa::QQualifiedName>(
+ operand.browsePath().at(k), &op->browsePath[k]);
+ result->elements[i].filterOperands[j].content.decoded.data = op;
+ result->elements[i].filterOperands[j].content.decoded.type = &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND];
+ } else if (currentOperand.canConvert<QOpcUa::QAttributeOperand>()) {
+ UA_AttributeOperand *op = UA_AttributeOperand_new();
+ UA_AttributeOperand_init(op);
+ QOpcUa::QAttributeOperand operand = currentOperand.value<QOpcUa::QAttributeOperand>();
+ op->attributeId = QOpen62541ValueConverter::toUaAttributeId(operand.attributeId());
+ QOpen62541ValueConverter::scalarFromQt<UA_String, QString>(operand.indexRange(), &op->indexRange);
+ op->alias = UA_STRING_ALLOC(operand.alias().toUtf8().constData());
+ if (!operand.nodeId().isEmpty())
+ op->nodeId = Open62541Utils::nodeIdFromQString(operand.nodeId());
+ op->browsePath.elementsSize = operand.browsePath().size();
+ op->browsePath.elements = static_cast<UA_RelativePathElement *>(
+ UA_Array_new(operand.browsePathRef().size(), &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]));
+
+ for (int k = 0; k < operand.browsePathRef().size(); ++k) {
+ UA_RelativePathElement_init(&op->browsePath.elements[k]);
+ op->browsePath.elements[k].includeSubtypes = operand.browsePath().at(k).includeSubtypes();
+ op->browsePath.elements[k].isInverse = operand.browsePath().at(k).isInverse();
+ if (!operand.browsePath().at(k).referenceTypeId().isEmpty())
+ op->browsePath.elements[k].referenceTypeId = Open62541Utils::nodeIdFromQString(
+ operand.browsePath().at(k).referenceTypeId());
+ QOpen62541ValueConverter::scalarFromQt<UA_QualifiedName, QOpcUa::QQualifiedName>(
+ operand.browsePath().at(k).targetName(), &op->browsePath.elements[k].targetName);
+ }
+
+ result->elements[i].filterOperands[j].content.decoded.data = op;
+ result->elements[i].filterOperands[j].content.decoded.type = &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND];
+ } else {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Unknown filter operand type for event filter" <<
+ filter.whereClause().at(i).filterOperands().at(j).typeName();
+ UA_ContentFilter_deleteMembers(result);
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+QOpcUa::QEventFilterResult QOpen62541Subscription::convertEventFilterResult(UA_ExtensionObject *obj)
+{
+ QOpcUa::QEventFilterResult result;
+
+ if (!obj)
+ return result;
+
+ if (obj->encoding == UA_EXTENSIONOBJECT_DECODED && obj->content.decoded.type == &UA_TYPES[UA_TYPES_EVENTFILTERRESULT]) {
+ UA_EventFilterResult *filterResult = static_cast<UA_EventFilterResult *>(obj->content.decoded.data);
+
+ for (size_t i = 0; i < filterResult->selectClauseResultsSize; ++i)
+ result.selectClauseResultsRef().append(static_cast<QOpcUa::UaStatusCode>(filterResult->selectClauseResults[i]));
+
+ for (size_t i = 0; i < filterResult->whereClauseResult.elementResultsSize; ++i) {
+ QOpcUa::QContentFilterElementResult temp;
+ temp.setStatusCode(static_cast<QOpcUa::UaStatusCode>(filterResult->whereClauseResult.elementResults[i].statusCode));
+ for (size_t j = 0; j < filterResult->whereClauseResult.elementResults[i].operandStatusCodesSize; ++j)
+ temp.operandStatusCodesRef().append(static_cast<QOpcUa::UaStatusCode>(
+ filterResult->whereClauseResult.elementResults[i].operandStatusCodes[j]));
+ result.whereClauseResultsRef().append(temp);
+ }
+ }
+
+ return result;
+}
+
bool QOpen62541Subscription::modifySubscriptionParameters(quint64 handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value)
{
QOpcUaMonitoringParameters p;
@@ -529,8 +742,6 @@ bool QOpen62541Subscription::modifyMonitoredItemParameters(quint64 handle, QOpcU
req.itemsToModify->requestedParameters.samplingInterval = monItem->parameters.samplingInterval();
req.itemsToModify->monitoredItemId = monItem->monitoredItemId;
req.itemsToModify->requestedParameters.clientHandle = monItem->clientHandle;
- if (item != QOpcUaMonitoringParameters::Parameter::Filter)
- req.itemsToModify->requestedParameters.filter = createFilter(monItem->parameters.filter());
bool match = true;
@@ -569,7 +780,16 @@ bool QOpen62541Subscription::modifyMonitoredItemParameters(quint64 handle, QOpcU
break;
}
case QOpcUaMonitoringParameters::Parameter::Filter: {
- req.itemsToModify->requestedParameters.filter = createFilter(value);
+ UA_ExtensionObject filter = createFilter(value);
+ if (filter.content.decoded.data)
+ req.itemsToModify->requestedParameters.filter = filter;
+ else {
+ qCDebug(QT_OPCUA_PLUGINS_OPEN62541) << "Unable to modify filter, filter creation failed";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadInternalError);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ UA_ModifyMonitoredItemsRequest_deleteMembers(&req);
+ return true;
+ }
break;
}
default:
@@ -578,6 +798,19 @@ bool QOpen62541Subscription::modifyMonitoredItemParameters(quint64 handle, QOpcU
}
if (match) {
+ if (item != QOpcUaMonitoringParameters::Parameter::Filter && p.filter().isValid()) {
+ UA_ExtensionObject filter = createFilter(monItem->parameters.filter());
+ if (filter.content.decoded.data)
+ req.itemsToModify->requestedParameters.filter = filter;
+ else {
+ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not modify monitored item, filter creation failed";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadInternalError);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ UA_ModifyMonitoredItemsRequest_deleteMembers(&req);
+ return true;
+ }
+ }
+
UA_ModifyMonitoredItemsResponse res = UA_Client_MonitoredItems_modify(m_backend->m_uaclient, req);
if (res.responseHeader.serviceResult != UA_STATUSCODE_GOOD || res.results[0].statusCode != UA_STATUSCODE_GOOD) {
@@ -603,6 +836,12 @@ bool QOpen62541Subscription::modifyMonitoredItemParameters(quint64 handle, QOpcU
changed |= QOpcUaMonitoringParameters::Parameter::DiscardOldest;
}
+ if (res.results[0].filterResult.content.decoded.type == &UA_TYPES[UA_TYPES_EVENTFILTERRESULT]) {
+ if (item == QOpcUaMonitoringParameters::Parameter::Filter)
+ changed |= QOpcUaMonitoringParameters::Parameter::Filter;
+ p.setFilter(QVariant::fromValue(convertEventFilterResult(&res.results[0].filterResult)));
+ }
+
emit m_backend->monitoringStatusChanged(handle, attr, changed, p);
monItem->parameters = p;
UA_ModifyMonitoredItemsRequest_deleteMembers(&req);
diff --git a/src/plugins/opcua/open62541/qopen62541subscription.h b/src/plugins/opcua/open62541/qopen62541subscription.h
index f3c1f78..b20b50f 100644
--- a/src/plugins/opcua/open62541/qopen62541subscription.h
+++ b/src/plugins/opcua/open62541/qopen62541subscription.h
@@ -61,6 +61,8 @@ public:
bool removeAttributeMonitoredItem(quint64 handle, QOpcUa::NodeAttribute attr);
void monitoredValueUpdated(UA_UInt32 monId, UA_DataValue *value);
+ void eventReceived(UA_UInt32 monId, QVariantList list);
+
void sendTimeoutNotification();
struct MonitoredItem {
@@ -92,9 +94,15 @@ signals:
private:
MonitoredItem *getItemForAttribute(quint64 handle, QOpcUa::NodeAttribute attr);
UA_ExtensionObject createFilter(const QVariant &filterData);
+ void createDataChangeFilter(const QOpcUaMonitoringParameters::DataChangeFilter &filter, UA_ExtensionObject *out);
+ void createEventFilter(const QOpcUaMonitoringParameters::EventFilter &filter, UA_ExtensionObject *out);
+ bool convertSelectClause(const QOpcUaMonitoringParameters::EventFilter &filter,
+ UA_SimpleAttributeOperand **selectClauses, size_t *size);
+ bool convertWhereClause(const QOpcUaMonitoringParameters::EventFilter &filter, UA_ContentFilter *result);
bool modifySubscriptionParameters(quint64 handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value);
bool modifyMonitoredItemParameters(quint64 handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value);
+ QOpcUa::QEventFilterResult convertEventFilterResult(UA_ExtensionObject *obj);
Open62541AsyncBackend *m_backend;
double m_interval;
diff --git a/src/plugins/opcua/uacpp/quacppsubscription.cpp b/src/plugins/opcua/uacpp/quacppsubscription.cpp
index 1c4f488..f2f9b57 100644
--- a/src/plugins/opcua/uacpp/quacppsubscription.cpp
+++ b/src/plugins/opcua/uacpp/quacppsubscription.cpp
@@ -21,11 +21,13 @@
#include "quacppsubscription.h"
#include "quacppclient.h"
+#include "quacpputils.h"
#include "quacppvalueconverter.h"
#include <QtCore/QLoggingCategory>
#include <uaclient/uasession.h>
+#include <uaeventfilterresult.h>
using namespace UaClientSdk;
@@ -111,8 +113,16 @@ bool QUACppSubscription::addAttributeMonitoredItem(quint64 handle, QOpcUa::NodeA
createRequests[0].RequestedParameters.QueueSize = 1;
createRequests[0].RequestedParameters.DiscardOldest = OpcUa_True;
createRequests[0].MonitoringMode = static_cast<OpcUa_MonitoringMode>(parameters.monitoringMode());
- if (parameters.filter().type() == QVariant::UserType && parameters.filter().userType() == QMetaType::type("QOpcUaMonitoringParameters::DataChangeFilter"))
+ if (parameters.filter().isValid()) {
createRequests[0].RequestedParameters.Filter = createFilter(parameters.filter());
+ if (!createRequests[0].RequestedParameters.Filter.Body.EncodeableObject.Object) {
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Unable to create the monitored item, filter creation failed";
+ QOpcUaMonitoringParameters s;
+ s.setStatusCode(QOpcUa::UaStatusCode::BadInternalError);
+ emit m_backend->monitoringEnableDisable(handle, attr, true, s);
+ return false;
+ }
+ }
result = m_nativeSubscription->createMonitoredItems(settings, OpcUa_TimestampsToReturn_Both,
createRequests, createResults);
@@ -142,7 +152,10 @@ bool QUACppSubscription::addAttributeMonitoredItem(quint64 handle, QOpcUa::NodeA
m_monitoredIds.insert(monitorId, key);
monitorId++;
- s.setFilter(QVariant());
+ if (UaNodeId(createResults[0].FilterResult.TypeId.NodeId) == UaNodeId(OpcUaId_EventFilterResult_Encoding_DefaultBinary, 0))
+ s.setFilter(QVariant::fromValue(convertEventFilterResult(createResults[0].FilterResult)));
+ else
+ s.setFilter(QVariant()); // The server did not return an EventFilterResult
emit m_backend->monitoringEnableDisable(handle, attr, true, s);
return true;
@@ -316,9 +329,20 @@ void QUACppSubscription::dataChange(OpcUa_UInt32 clientSubscriptionHandle, const
void QUACppSubscription::newEvents(OpcUa_UInt32 clientSubscriptionHandle, UaEventFieldLists &eventFieldList)
{
Q_UNUSED(clientSubscriptionHandle);
- Q_UNUSED(eventFieldList);
- Q_UNIMPLEMENTED();
- qCWarning(QT_OPCUA_PLUGINS_UACPP) << "eventsChange unhandled";
+
+ for (quint32 i = 0; i < eventFieldList.length(); ++i) {
+ const quint32 monitorId = eventFieldList[i].ClientHandle;
+
+ if (!m_monitoredIds.contains(monitorId))
+ continue;
+
+ QVariantList eventFields;
+
+ for (int j = 0; j < eventFieldList[i].NoOfEventFields; ++j)
+ eventFields.append(QUACppValueConverter::toQVariant(eventFieldList[i].EventFields[j]));
+
+ emit m_backend->eventOccurred(m_monitoredIds[monitorId].first, eventFields);
+ }
}
OpcUa_ExtensionObject QUACppSubscription::createFilter(const QVariant &filterData)
@@ -326,23 +350,13 @@ OpcUa_ExtensionObject QUACppSubscription::createFilter(const QVariant &filterDat
OpcUa_ExtensionObject obj;
OpcUa_ExtensionObject_Initialize(&obj);
- if (filterData.type() == QVariant::UserType && filterData.userType() == QMetaType::type("QOpcUaMonitoringParameters::DataChangeFilter")) {
- const QOpcUaMonitoringParameters::DataChangeFilter temp = filterData.value<QOpcUaMonitoringParameters::DataChangeFilter>();
-
- OpcUa_DataChangeFilter *filter = nullptr;
-
- OpcUa_EncodeableObject_CreateExtension(&OpcUa_DataChangeFilter_EncodeableType,
- &obj,
- reinterpret_cast<OpcUa_Void **>(&filter));
-
- if (!filter) {
- qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Could not create DataChangeFilter";
- return obj;
- }
- filter->DeadbandType = static_cast<OpcUa_UInt32>(temp.deadbandType());
- filter->DeadbandValue = static_cast<OpcUa_Double>(temp.deadbandValue());
- filter->Trigger = static_cast<OpcUa_DataChangeTrigger>(temp.trigger());
+ if (filterData.canConvert<QOpcUaMonitoringParameters::DataChangeFilter>()) {
+ createDataChangeFilter(filterData.value<QOpcUaMonitoringParameters::DataChangeFilter>(), &obj);
+ return obj;
+ }
+ if (filterData.canConvert<QOpcUaMonitoringParameters::EventFilter>()) {
+ createEventFilter(filterData.value<QOpcUaMonitoringParameters::EventFilter>(), &obj);
return obj;
}
@@ -352,6 +366,180 @@ OpcUa_ExtensionObject QUACppSubscription::createFilter(const QVariant &filterDat
return obj;
}
+void QUACppSubscription::createDataChangeFilter(const QOpcUaMonitoringParameters::DataChangeFilter &filter, OpcUa_ExtensionObject *out)
+{
+ OpcUa_DataChangeFilter *uaFilter = nullptr;
+ OpcUa_EncodeableObject_CreateExtension(&OpcUa_DataChangeFilter_EncodeableType,
+ out, reinterpret_cast<OpcUa_Void **>(&uaFilter));
+ if (!uaFilter) {
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Could not create DataChangeFilter";
+ return;
+ }
+
+ uaFilter->DeadbandType = static_cast<OpcUa_UInt32>(filter.deadbandType());
+ uaFilter->DeadbandValue = static_cast<OpcUa_Double>(filter.deadbandValue());
+ uaFilter->Trigger = static_cast<OpcUa_DataChangeTrigger>(filter.trigger());
+}
+
+void QUACppSubscription::createEventFilter(const QOpcUaMonitoringParameters::EventFilter &filter, OpcUa_ExtensionObject *out)
+{
+ OpcUa_EventFilter *uaFilter = nullptr; // Use the stack structures because the C++ layer does not support AttributeOperand
+ OpcUa_EncodeableObject_CreateExtension(&OpcUa_EventFilter_EncodeableType,
+ out, reinterpret_cast<OpcUa_Void **>(&uaFilter));
+ if (!uaFilter) {
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Could not create EventFilter";
+ return;
+ }
+
+ // Select clause
+ if (filter.selectClauses().size()) {
+ UaSimpleAttributeOperands select;
+ select.create(filter.selectClauses().size());
+
+ for (int i = 0; i < filter.selectClauses().size(); ++i) {
+ if (!filter.selectClauses()[i].typeId().isEmpty())
+ UACppUtils::nodeIdFromQString(filter.selectClauses()[i].typeId()).copyTo(&select[i].TypeDefinitionId);
+ UaString(filter.selectClauses()[i].indexRange().toUtf8().constData()).copyTo(&select[i].IndexRange);
+ select[i].AttributeId = QUACppValueConverter::toUaAttributeId(filter.selectClauses()[i].attributeId());
+ UaQualifiedNameArray path;
+ path.create(filter.selectClauses()[i].browsePathRef().size());
+ for (int j = 0; j < filter.selectClauses()[i].browsePathRef().size(); ++j) {
+ UaQualifiedName(UaString(filter.selectClauses()[i].browsePathRef()[j].name().toUtf8().constData()),
+ filter.selectClauses()[i].browsePathRef()[j].namespaceIndex()).copyTo(&path[j]);
+ }
+ select[i].NoOfBrowsePath = filter.selectClauses()[i].browsePathRef().size();
+ select[i].BrowsePath = path.detach();
+ }
+ uaFilter->NoOfSelectClauses = filter.selectClauses().size();
+ uaFilter->SelectClauses = select.detach();
+ }
+
+ // Where clause
+ if (filter.whereClause().size()) {
+ uaFilter->WhereClause.NoOfElements = filter.whereClause().size();
+ uaFilter->WhereClause.Elements = static_cast<OpcUa_ContentFilterElement *>(malloc(filter.whereClause().size()*sizeof(OpcUa_ContentFilterElement)));
+
+ for (int i = 0; i < filter.whereClause().size(); ++i) {
+ uaFilter->WhereClause.Elements[i].FilterOperator = static_cast<OpcUa_FilterOperator>(filter.whereClause()[i].filterOperator());
+ uaFilter->WhereClause.Elements[i].NoOfFilterOperands = filter.whereClause()[i].filterOperandsRef().size();
+ UaExtensionObjectArray operands;
+ operands.create(filter.whereClause()[i].filterOperandsRef().size());
+
+ for (int j = 0; j < filter.whereClause()[i].filterOperandsRef().size(); ++j) {
+ if (filter.whereClause()[i].filterOperandsRef()[j].canConvert<QOpcUa::QElementOperand>()) {
+ OpcUa_ElementOperand *op;
+ OpcUa_EncodeableObject_CreateExtension(&OpcUa_ElementOperand_EncodeableType,
+ &operands[j],
+ reinterpret_cast<OpcUa_Void **>(&op));
+ if (!op) {
+ OpcUa_ExtensionObject_Clear(out);
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Could not allocate an ElementOperand for the event filter";
+ return;
+ }
+ op->Index = filter.whereClause()[i].filterOperandsRef()[j].value<QOpcUa::QElementOperand>().index();
+ } else if (filter.whereClause()[i].filterOperandsRef()[j].canConvert<QOpcUa::QLiteralOperand>()) {
+ OpcUa_LiteralOperand *op;
+ OpcUa_EncodeableObject_CreateExtension(&OpcUa_LiteralOperand_EncodeableType,
+ &operands[j],
+ reinterpret_cast<OpcUa_Void **>(&op));
+ if (!op) {
+ OpcUa_ExtensionObject_Clear(out);
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Could not allocate a LiteralOperand for the event filter";
+ return ;
+ }
+ QOpcUa::QLiteralOperand litOp = filter.whereClause()[i].filterOperandsRef()[j].value<QOpcUa::QLiteralOperand>();
+ op->Value = QUACppValueConverter::toUACppVariant(litOp.value(), litOp.type());
+ } else if (filter.whereClause()[i].filterOperandsRef()[j].canConvert<QOpcUa::QSimpleAttributeOperand>()) {
+ OpcUa_SimpleAttributeOperand *op;
+ OpcUa_EncodeableObject_CreateExtension(&OpcUa_SimpleAttributeOperand_EncodeableType,
+ &operands[j],
+ reinterpret_cast<OpcUa_Void **>(&op));
+ if (!op) {
+ OpcUa_ExtensionObject_Clear(out);
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Could not allocate a SimpleAttributeOperand for the event filter";
+ return;
+ }
+ QOpcUa::QSimpleAttributeOperand operand = filter.whereClause()[i].filterOperandsRef()[j].value<QOpcUa::QSimpleAttributeOperand>();
+ op->AttributeId = QUACppValueConverter::toUaAttributeId(operand.attributeId());
+ UaString(operand.indexRange().toUtf8().constData()).copyTo(&op->IndexRange);
+ if (!operand.typeId().isEmpty())
+ UACppUtils::nodeIdFromQString(operand.typeId()).copyTo(&op->TypeDefinitionId);
+ UaQualifiedNameArray path;
+ path.create(operand.browsePathRef().size());
+ for (int k = 0; k < operand.browsePathRef().size(); ++k) {
+ UaQualifiedName(UaString(operand.browsePathRef()[k].name().toUtf8().constData()), operand.browsePathRef()[k].namespaceIndex()).copyTo(&path[k]);
+ }
+ op->NoOfBrowsePath = operand.browsePathRef().size();
+ op->BrowsePath = path.detach();
+ } else if (filter.whereClause()[i].filterOperandsRef()[j].canConvert<QOpcUa::QAttributeOperand>()) {
+ OpcUa_AttributeOperand *op;
+ OpcUa_EncodeableObject_CreateExtension(&OpcUa_AttributeOperand_EncodeableType,
+ &operands[j],
+ reinterpret_cast<OpcUa_Void **>(&op));
+ if (!op) {
+ OpcUa_ExtensionObject_Clear(out);
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Could not allocate an AttributeOperand for the event filter";
+ return;
+ }
+ QOpcUa::QAttributeOperand operand = filter.whereClause()[i].filterOperandsRef()[j].value<QOpcUa::QAttributeOperand>();
+ op->AttributeId = QUACppValueConverter::toUaAttributeId(operand.attributeId());
+ UaString(operand.indexRange().toUtf8().constData()).copyTo(&op->IndexRange);
+ if (!operand.nodeId().isEmpty())
+ UACppUtils::nodeIdFromQString(operand.nodeId()).copyTo(&op->NodeId);
+ UaString(operand.alias().toUtf8().constData()).copyTo(&op->Alias);
+ UaRelativePathElements path;
+ path.create(operand.browsePathRef().size());
+ op->BrowsePath.NoOfElements = operand.browsePathRef().size();
+ for (int k = 0; k < operand.browsePathRef().size(); ++k) {
+ path[k].IncludeSubtypes = operand.browsePathRef()[k].includeSubtypes();
+ path[k].IsInverse = operand.browsePathRef()[k].isInverse();
+ if (!operand.browsePathRef()[k].referenceTypeId().isEmpty())
+ UACppUtils::nodeIdFromQString(operand.browsePathRef()[k].referenceTypeId()).copyTo(&path[k].ReferenceTypeId);
+ UaQualifiedName(UaString(operand.browsePathRef()[k].targetName().name().toUtf8().constData()),
+ operand.browsePathRef()[k].targetName().namespaceIndex()).copyTo(&path[k].TargetName);
+ }
+ op->BrowsePath.Elements = path.detach();
+
+ } else {
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Unknown filter operand type for event filter" << filter.whereClause()[i].filterOperands()[j].typeName();
+ OpcUa_ExtensionObject_Clear(out);
+ return;
+ }
+ }
+ uaFilter->WhereClause.Elements[i].FilterOperands = operands.detach();
+ }
+ }
+}
+
+QOpcUa::QEventFilterResult QUACppSubscription::convertEventFilterResult(const OpcUa_ExtensionObject &obj)
+{
+ QOpcUa::QEventFilterResult result;
+
+ if (UaNodeId(obj.TypeId.NodeId) == UaNodeId(OpcUaId_EventFilterResult_Encoding_DefaultBinary, 0)) {
+ UaEventFilterResult filterResult(obj);
+
+ UaStatusCodeArray arr;
+ filterResult.getSelectClauseResults(arr);
+ for (size_t i = 0; i < arr.length(); ++i)
+ result.selectClauseResultsRef().append(static_cast<QOpcUa::UaStatusCode>(arr[i]));
+
+ UaContentFilterResult contentFilterResult = filterResult.getWhereClauseResult();
+
+ UaContentFilterElementResults elementResults;
+ contentFilterResult.getElementResults(elementResults);
+
+ for (size_t i = 0; i < elementResults.length(); ++i) {
+ QOpcUa::QContentFilterElementResult temp;
+ temp.setStatusCode(static_cast<QOpcUa::UaStatusCode>(elementResults[i].StatusCode));
+ for (int j = 0; j < elementResults[i].NoOfOperandStatusCodes; ++j)
+ temp.operandStatusCodesRef().append(static_cast<QOpcUa::UaStatusCode>(elementResults[i].OperandStatusCodes[j]));
+ result.whereClauseResultsRef().append(temp);
+ }
+ }
+
+ return result;
+}
+
bool QUACppSubscription::modifySubscriptionParameters(quint64 handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value)
{
QOpcUaMonitoringParameters p;
@@ -459,9 +647,6 @@ bool QUACppSubscription::modifyMonitoredItemParameters(quint64 handle, QOpcUa::N
modifyRequest.RequestedParameters.ClientHandle = m_monitoredIds.key(key);
QOpcUaMonitoringParameters p = valuePair.second;
- if (item != QOpcUaMonitoringParameters::Parameter::Filter)
- modifyRequest.RequestedParameters.Filter = createFilter(valuePair.second.filter());
-
bool match = true;
switch (item) {
@@ -497,6 +682,12 @@ bool QUACppSubscription::modifyMonitoredItemParameters(quint64 handle, QOpcUa::N
}
case QOpcUaMonitoringParameters::Parameter::Filter: {
modifyRequest.RequestedParameters.Filter = createFilter(value);
+ if (!modifyRequest.RequestedParameters.Filter.Body.EncodeableObject.Object) {
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Could not modify the filter, filter creation failed";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadInternalError);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return true;
+ }
break;
}
default:
@@ -505,6 +696,16 @@ bool QUACppSubscription::modifyMonitoredItemParameters(quint64 handle, QOpcUa::N
}
if (match) {
+ if (item != QOpcUaMonitoringParameters::Parameter::Filter && valuePair.second.filter().isValid()) {
+ modifyRequest.RequestedParameters.Filter = createFilter(valuePair.second.filter());
+ if (!modifyRequest.RequestedParameters.Filter.Body.EncodeableObject.Object) {
+ qCWarning(QT_OPCUA_PLUGINS_UACPP) << "Unable to modify the monitored item, filter creation failed";
+ p.setStatusCode(QOpcUa::UaStatusCode::BadInternalError);
+ emit m_backend->monitoringStatusChanged(handle, attr, item, p);
+ return true;
+ }
+ }
+
ServiceSettings service;
UaMonitoredItemModifyRequests requests(1, &modifyRequest);
UaMonitoredItemModifyResults results;
@@ -532,8 +733,15 @@ bool QUACppSubscription::modifyMonitoredItemParameters(quint64 handle, QOpcUa::N
p.setDiscardOldest(value.toBool());
changed | QOpcUaMonitoringParameters::Parameter::DiscardOldest;
}
+ if (item == QOpcUaMonitoringParameters::Parameter::Filter &&
+ UaNodeId(results[0].FilterResult.TypeId.NodeId) == UaNodeId(OpcUaId_EventFilterResult_Encoding_DefaultBinary, 0)) {
+ p.setFilter(QVariant::fromValue(convertEventFilterResult(results[0].FilterResult)));
+ changed | QOpcUaMonitoringParameters::Parameter::Filter;
+ }
emit m_backend->monitoringStatusChanged(handle, attr, changed, p);
+ if (item == QOpcUaMonitoringParameters::Parameter::Filter)
+ p.setFilter(m_monitoredItems[key].second.filter()); // Don't overwrite the filter
m_monitoredItems[key].second = p;
}
return true;
diff --git a/src/plugins/opcua/uacpp/quacppsubscription.h b/src/plugins/opcua/uacpp/quacppsubscription.h
index 7a805c6..e3a7450 100644
--- a/src/plugins/opcua/uacpp/quacppsubscription.h
+++ b/src/plugins/opcua/uacpp/quacppsubscription.h
@@ -55,6 +55,9 @@ public:
void newEvents(OpcUa_UInt32 clientSubscriptionHandle, UaEventFieldLists &eventFieldList) override;
private:
OpcUa_ExtensionObject createFilter(const QVariant &filterData);
+ void createDataChangeFilter(const QOpcUaMonitoringParameters::DataChangeFilter &filter, OpcUa_ExtensionObject *out);
+ void createEventFilter(const QOpcUaMonitoringParameters::EventFilter &filter, OpcUa_ExtensionObject *out);
+ QOpcUa::QEventFilterResult convertEventFilterResult(const OpcUa_ExtensionObject &obj);
bool modifySubscriptionParameters(quint64 handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value);
bool modifyMonitoredItemParameters(quint64 handle, QOpcUa::NodeAttribute attr, const QOpcUaMonitoringParameters::Parameter &item, const QVariant &value);