diff options
author | Jannis Voelker <jannis.voelker@basyskom.com> | 2018-02-08 10:54:59 +0100 |
---|---|---|
committer | Jannis Völker <jannis.voelker@basyskom.com> | 2018-03-14 09:54:46 +0000 |
commit | 54e9e56b93a1505ea75acc63ad0958aa4722bbc7 (patch) | |
tree | c94e3ec8d534a6b38b71862c6a99345dbb79f309 | |
parent | fd4adcd098cef84fb874cc0e290e9ab65504abed (diff) |
Add server and source timestamp for read and data change
ServerTimestamp and SourceTimestamp from the last read or data change
on an attribute can be accessed using QOpcUaNode::serverTimestamp(attr)
and QOpcUaNode::sourceTimestamp(attr).
Change-Id: I6e816c18338bfd269c8a4400270afdc97f9f98b6
Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
Reviewed-by: Frank Meerkoetter <frank.meerkoetter@basyskom.com>
-rw-r--r-- | src/opcua/client/qopcuabackend_p.h | 2 | ||||
-rw-r--r-- | src/opcua/client/qopcuaclientimpl.cpp | 4 | ||||
-rw-r--r-- | src/opcua/client/qopcuaclientimpl_p.h | 2 | ||||
-rw-r--r-- | src/opcua/client/qopcuanode.cpp | 33 | ||||
-rw-r--r-- | src/opcua/client/qopcuanode.h | 2 | ||||
-rw-r--r-- | src/opcua/client/qopcuanode_p.h | 24 | ||||
-rw-r--r-- | src/opcua/client/qopcuanodeimpl_p.h | 4 | ||||
-rw-r--r-- | src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp | 22 | ||||
-rw-r--r-- | src/plugins/opcua/freeopcua/qfreeopcuasubscription.h | 2 | ||||
-rw-r--r-- | src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp | 3 | ||||
-rw-r--r-- | src/plugins/opcua/open62541/qopen62541backend.cpp | 5 | ||||
-rw-r--r-- | src/plugins/opcua/open62541/qopen62541subscription.cpp | 10 | ||||
-rw-r--r-- | src/plugins/opcua/open62541/qopen62541valueconverter.cpp | 16 | ||||
-rw-r--r-- | src/plugins/opcua/open62541/qopen62541valueconverter.h | 2 | ||||
-rw-r--r-- | src/plugins/opcua/uacpp/quacppbackend.cpp | 2 | ||||
-rw-r--r-- | src/plugins/opcua/uacpp/quacppsubscription.cpp | 12 | ||||
-rw-r--r-- | tests/auto/qopcuaclient/tst_client.cpp | 54 |
17 files changed, 170 insertions, 29 deletions
diff --git a/src/opcua/client/qopcuabackend_p.h b/src/opcua/client/qopcuabackend_p.h index ceadb58..acfc603 100644 --- a/src/opcua/client/qopcuabackend_p.h +++ b/src/opcua/client/qopcuabackend_p.h @@ -81,7 +81,7 @@ Q_SIGNALS: void attributeWritten(uintptr_t hande, QOpcUa::NodeAttribute attribute, QVariant value, QOpcUa::UaStatusCode statusCode); void methodCallFinished(uintptr_t handle, QString methodNodeId, QVariant result, QOpcUa::UaStatusCode statusCode); - void attributeUpdated(uintptr_t handle, QOpcUa::NodeAttribute attr, QVariant value); + void attributeUpdated(uintptr_t handle, QOpcUaReadResult res); void monitoringEnableDisable(uintptr_t handle, QOpcUa::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status); void monitoringStatusChanged(uintptr_t handle, QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items, QOpcUaMonitoringParameters param); diff --git a/src/opcua/client/qopcuaclientimpl.cpp b/src/opcua/client/qopcuaclientimpl.cpp index 2a1eec4..4c95e1e 100644 --- a/src/opcua/client/qopcuaclientimpl.cpp +++ b/src/opcua/client/qopcuaclientimpl.cpp @@ -83,11 +83,11 @@ void QOpcUaClientImpl::handleAttributeWritten(uintptr_t handle, QOpcUa::NodeAttr emit (*it)->attributeWritten(attr, value, statusCode); } -void QOpcUaClientImpl::handleAttributeUpdated(uintptr_t handle, QOpcUa::NodeAttribute attr, const QVariant &value) +void QOpcUaClientImpl::handleAttributeUpdated(uintptr_t handle, const QOpcUaReadResult &value) { auto it = m_handles.constFind(handle); if (it != m_handles.constEnd() && !it->isNull()) - emit (*it)->attributeUpdated(attr, value); + emit (*it)->attributeUpdated(value.attributeId, value); } void QOpcUaClientImpl::handleMonitoringEnableDisable(uintptr_t handle, QOpcUa::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status) diff --git a/src/opcua/client/qopcuaclientimpl_p.h b/src/opcua/client/qopcuaclientimpl_p.h index 1bfb78d..d7d1d11 100644 --- a/src/opcua/client/qopcuaclientimpl_p.h +++ b/src/opcua/client/qopcuaclientimpl_p.h @@ -86,7 +86,7 @@ public: private Q_SLOTS: void handleAttributesRead(uintptr_t handle, QVector<QOpcUaReadResult> attr, QOpcUa::UaStatusCode serviceResult); void handleAttributeWritten(uintptr_t handle, QOpcUa::NodeAttribute attr, const QVariant &value, QOpcUa::UaStatusCode statusCode); - void handleAttributeUpdated(uintptr_t handle, QOpcUa::NodeAttribute attr, const QVariant &value); + void handleAttributeUpdated(uintptr_t handle, const QOpcUaReadResult &value); void handleMonitoringEnableDisable(uintptr_t handle, QOpcUa::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status); void handleMonitoringStatusChanged(uintptr_t handle, QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items, QOpcUaMonitoringParameters param); diff --git a/src/opcua/client/qopcuanode.cpp b/src/opcua/client/qopcuanode.cpp index 4f79c66..990e438 100644 --- a/src/opcua/client/qopcuanode.cpp +++ b/src/opcua/client/qopcuanode.cpp @@ -276,7 +276,7 @@ QVariant QOpcUaNode::attribute(QOpcUa::NodeAttribute attribute) const if (it == d->m_nodeAttributes.constEnd()) return QVariant(); - return it->attribute; + return it->value; } /*! @@ -297,6 +297,37 @@ QOpcUa::UaStatusCode QOpcUaNode::attributeError(QOpcUa::NodeAttribute attribute) } /*! + Returns the source timestamp from the last read or data change of \a attribute. + Before at least one \l attributeRead or \l attributeUpdated signal has been emitted, + a null datetime is returned. + +*/ +QDateTime QOpcUaNode::sourceTimestamp(QOpcUa::NodeAttribute attribute) const +{ + Q_D(const QOpcUaNode); + auto it = d->m_nodeAttributes.constFind(attribute); + if (it == d->m_nodeAttributes.constEnd()) + return QDateTime(); + + return it->sourceTimestamp; +} + +/*! + Returns the server timestamp from the last read or data change of \a attribute. + Before at least one \l attributeRead or \l attributeUpdated signal has been emitted, + a null datetime is returned. +*/ +QDateTime QOpcUaNode::serverTimestamp(QOpcUa::NodeAttribute attribute) const +{ + Q_D(const QOpcUaNode); + auto it = d->m_nodeAttributes.constFind(attribute); + if (it == d->m_nodeAttributes.constEnd()) + return QDateTime(); + + return it->serverTimestamp; +} + +/*! 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. diff --git a/src/opcua/client/qopcuanode.h b/src/opcua/client/qopcuanode.h index 5ad06f8..fc12e53 100644 --- a/src/opcua/client/qopcuanode.h +++ b/src/opcua/client/qopcuanode.h @@ -71,6 +71,8 @@ public: bool readAttributeRange(QOpcUa::NodeAttribute attribute, const QString &indexRange); QVariant attribute(QOpcUa::NodeAttribute attribute) const; QOpcUa::UaStatusCode attributeError(QOpcUa::NodeAttribute attribute) const; + QDateTime sourceTimestamp(QOpcUa::NodeAttribute attribute) const; + QDateTime serverTimestamp(QOpcUa::NodeAttribute attribute) const; bool writeAttribute(QOpcUa::NodeAttribute attribute, const QVariant &value, QOpcUa::Types type = QOpcUa::Types::Undefined); bool writeAttributeRange(QOpcUa::NodeAttribute attribute, const QVariant &value, const QString &indexRange, QOpcUa::Types type = QOpcUa::Types::Undefined); diff --git a/src/opcua/client/qopcuanode_p.h b/src/opcua/client/qopcuanode_p.h index f2d7381..0db95c3 100644 --- a/src/opcua/client/qopcuanode_p.h +++ b/src/opcua/client/qopcuanode_p.h @@ -73,9 +73,13 @@ public: { for (auto &entry : qAsConst(attr)) { if (serviceResult == QOpcUa::UaStatusCode::Good) - m_nodeAttributes[entry.attributeId] = { entry.value, entry.statusCode }; - else - m_nodeAttributes[entry.attributeId] = { QVariant(), serviceResult }; + m_nodeAttributes[entry.attributeId] = entry; + else { + QOpcUaReadResult temp = entry; + temp.statusCode = serviceResult; + temp.value = QVariant(); + m_nodeAttributes[entry.attributeId] = temp; + } } QOpcUa::NodeAttributes updatedAttributes; @@ -91,18 +95,18 @@ public: { m_nodeAttributes[attr].statusCode = statusCode; if (statusCode == QOpcUa::UaStatusCode::Good) - m_nodeAttributes[attr].attribute = value; + m_nodeAttributes[attr].value = value; Q_Q(QOpcUaNode); emit q->attributeWritten(attr, statusCode); }); m_attributeUpdatedConnection = QObject::connect(impl, &QOpcUaNodeImpl::attributeUpdated, - [this](QOpcUa::NodeAttribute attr, QVariant value) + [this](QOpcUa::NodeAttribute attr, QOpcUaReadResult value) { - this->m_nodeAttributes[attr] = {value, QOpcUa::UaStatusCode::Good}; + this->m_nodeAttributes[attr] = value; Q_Q(QOpcUaNode); - emit q->attributeUpdated(attr, value); + emit q->attributeUpdated(attr, value.value); }); m_monitoringEnableDisableConnection = QObject::connect(impl, &QOpcUaNodeImpl::monitoringEnableDisable, @@ -183,11 +187,7 @@ public: QScopedPointer<QOpcUaNodeImpl> m_impl; QPointer<QOpcUaClient> m_client; - struct AttributeWithStatus { - QVariant attribute; - QOpcUa::UaStatusCode statusCode; - }; - QHash<QOpcUa::NodeAttribute, AttributeWithStatus> m_nodeAttributes; + QHash<QOpcUa::NodeAttribute, QOpcUaReadResult> m_nodeAttributes; QHash<QOpcUa::NodeAttribute, QOpcUaMonitoringParameters> m_monitoringStatus; QMetaObject::Connection m_attributesReadConnection; diff --git a/src/opcua/client/qopcuanodeimpl_p.h b/src/opcua/client/qopcuanodeimpl_p.h index 4b94c33..62f2d91 100644 --- a/src/opcua/client/qopcuanodeimpl_p.h +++ b/src/opcua/client/qopcuanodeimpl_p.h @@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE struct QOpcUaReadResult { QOpcUa::NodeAttribute attributeId; QOpcUa::UaStatusCode statusCode; + QDateTime sourceTimestamp; + QDateTime serverTimestamp; QVariant value; }; @@ -88,7 +90,7 @@ Q_SIGNALS: void attributeWritten(QOpcUa::NodeAttribute attr, QVariant value, QOpcUa::UaStatusCode statusCode); void browseFinished(QVector<QOpcUaReferenceDescription> children, QOpcUa::UaStatusCode statusCode); - void attributeUpdated(QOpcUa::NodeAttribute attr, QVariant value); + void attributeUpdated(QOpcUa::NodeAttribute attr, QOpcUaReadResult value); void monitoringEnableDisable(QOpcUa::NodeAttribute attr, bool subscribe, QOpcUaMonitoringParameters status); void monitoringStatusChanged(QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items, QOpcUaMonitoringParameters param); diff --git a/src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp b/src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp index 8f3e2f6..1005782 100644 --- a/src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp +++ b/src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp @@ -244,9 +244,17 @@ QFreeOpcUaSubscription::MonitoredItem *QFreeOpcUaSubscription::getItemForAttribu return item.value(); } -void QFreeOpcUaSubscription::DataChange(quint32 handle, const OpcUa::Node &node, - const OpcUa::Variant &val, - OpcUa::AttributeId attr) +// Override the "default dc" message the default implementation writes to std::cout. +void QFreeOpcUaSubscription::DataChange(uint32_t handle, const OpcUa::Node &node, const OpcUa::Variant &val, OpcUa::AttributeId attr) +{ + Q_UNUSED(handle); + Q_UNUSED(node); + Q_UNUSED(val); + Q_UNUSED(attr) +} + +void QFreeOpcUaSubscription::DataValueChange(uint32_t handle, const OpcUa::Node &node, + const OpcUa::DataValue &val, OpcUa::AttributeId attr) { OPCUA_UNUSED(node); OPCUA_UNUSED(attr); @@ -254,7 +262,13 @@ void QFreeOpcUaSubscription::DataChange(quint32 handle, const OpcUa::Node &node, 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)); + + QOpcUaReadResult res; + res.attributeId = item.value()->attr; + res.sourceTimestamp = QFreeOpcUaValueConverter::scalarUaToQt<QDateTime, OpcUa::DateTime>(val.SourceTimestamp); + res.serverTimestamp = QFreeOpcUaValueConverter::scalarUaToQt<QDateTime, OpcUa::DateTime>(val.ServerTimestamp); + res.value = QFreeOpcUaValueConverter::toQVariant(val.Value); + emit m_backend->attributeUpdated(item.value()->handle, res); } QT_END_NAMESPACE diff --git a/src/plugins/opcua/freeopcua/qfreeopcuasubscription.h b/src/plugins/opcua/freeopcua/qfreeopcuasubscription.h index b18067b..47264de 100644 --- a/src/plugins/opcua/freeopcua/qfreeopcuasubscription.h +++ b/src/plugins/opcua/freeopcua/qfreeopcuasubscription.h @@ -56,6 +56,8 @@ public: // FreeOPC-UA callbacks void DataChange(uint32_t handle, const OpcUa::Node &node, const OpcUa::Variant &val, OpcUa::AttributeId attr) override; + void DataValueChange(uint32_t handle, const OpcUa::Node &node, const OpcUa::DataValue &val, + OpcUa::AttributeId attr) override; void StatusChange(OpcUa::StatusCode status) override; quint32 createOnServer(); diff --git a/src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp b/src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp index fb4e20e..e0ebac7 100644 --- a/src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp +++ b/src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp @@ -161,6 +161,7 @@ void QFreeOpcUaWorker::readAttributes(uintptr_t handle, OpcUa::NodeId id, QOpcUa try { OpcUa::ReadParameters params; + params.TimestampsToReturn = OpcUa::TimestampsToReturn::Both; OpcUa::ReadValueId attribute; attribute.NodeId = id; @@ -180,6 +181,8 @@ void QFreeOpcUaWorker::readAttributes(uintptr_t handle, OpcUa::NodeId id, QOpcUa if (res[i].Status == OpcUa::StatusCode::Good) { vec[i].value = QFreeOpcUaValueConverter::toQVariant(res[i].Value); } + vec[i].sourceTimestamp = QFreeOpcUaValueConverter::scalarUaToQt<QDateTime, OpcUa::DateTime>(res[i].SourceTimestamp); + vec[i].serverTimestamp = QFreeOpcUaValueConverter::scalarUaToQt<QDateTime, OpcUa::DateTime>(res[i].ServerTimestamp); } emit attributesRead(handle, vec, QOpcUa::UaStatusCode::Good); diff --git a/src/plugins/opcua/open62541/qopen62541backend.cpp b/src/plugins/opcua/open62541/qopen62541backend.cpp index 8f5e905..f1e1873 100644 --- a/src/plugins/opcua/open62541/qopen62541backend.cpp +++ b/src/plugins/opcua/open62541/qopen62541backend.cpp @@ -102,6 +102,7 @@ void Open62541AsyncBackend::readAttributes(uintptr_t handle, UA_NodeId id, QOpcU UA_ReadResponse_init(&res); req.nodesToRead = valueIds.data(); req.nodesToReadSize = valueIds.size(); + req.timestampsToReturn = UA_TIMESTAMPSTORETURN_BOTH; res = UA_Client_Service_read(m_uaclient, req); @@ -118,6 +119,10 @@ void Open62541AsyncBackend::readAttributes(uintptr_t handle, UA_NodeId id, QOpcU vec[i].statusCode = QOpcUa::UaStatusCode::Good; if (res.results[i].hasValue && res.results[i].value.data) vec[i].value = QOpen62541ValueConverter::toQVariant(res.results[i].value); + if (res.results[i].hasServerTimestamp) + vec[i].sourceTimestamp = QOpen62541ValueConverter::uaDateTimeToQDateTime(res.results[i].sourceTimestamp); + if (res.results[i].hasSourceTimestamp) + vec[i].serverTimestamp = QOpen62541ValueConverter::uaDateTimeToQDateTime(res.results[i].serverTimestamp); } emit attributesRead(handle, vec, static_cast<QOpcUa::UaStatusCode>(res.responseHeader.serviceResult)); UA_ReadValueId_deleteMembers(&readId); diff --git a/src/plugins/opcua/open62541/qopen62541subscription.cpp b/src/plugins/opcua/open62541/qopen62541subscription.cpp index 175a4ea..32ea564 100644 --- a/src/plugins/opcua/open62541/qopen62541subscription.cpp +++ b/src/plugins/opcua/open62541/qopen62541subscription.cpp @@ -305,7 +305,15 @@ void QOpen62541Subscription::monitoredValueUpdated(UA_UInt32 monId, UA_DataValue 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)); + QOpcUaReadResult res; + res.value = QOpen62541ValueConverter::toQVariant(value->value); + res.attributeId = item.value()->attr; + if (value->hasServerTimestamp) + res.serverTimestamp = QOpen62541ValueConverter::uaDateTimeToQDateTime(value->serverTimestamp); + if (value->hasSourceTimestamp) + res.sourceTimestamp = QOpen62541ValueConverter::uaDateTimeToQDateTime(value->sourceTimestamp); + res.statusCode = QOpcUa::UaStatusCode::Good; + emit m_backend->attributeUpdated(item.value()->handle, res); } double QOpen62541Subscription::interval() const diff --git a/src/plugins/opcua/open62541/qopen62541valueconverter.cpp b/src/plugins/opcua/open62541/qopen62541valueconverter.cpp index c060ae4..2539784 100644 --- a/src/plugins/opcua/open62541/qopen62541valueconverter.cpp +++ b/src/plugins/opcua/open62541/qopen62541valueconverter.cpp @@ -325,9 +325,9 @@ template<> QVariant scalarToQVariant<QDateTime, UA_DateTime>(UA_DateTime *data, QMetaType::Type type) { Q_UNUSED(type) - // OPC-UA part 3, Table C.9 - const QDateTime epochStart(QDate(1601, 1, 1), QTime(0, 0), Qt::UTC); - return QVariant(epochStart.addMSecs(*static_cast<UA_DateTime *>(data) * UA_DATETIME_TO_MSEC).toLocalTime()); + if (!data) + return QVariant(); + return QVariant(uaDateTimeToQDateTime(*data)); } template<> @@ -568,6 +568,16 @@ void createExtensionObject(QByteArray &data, QOpcUaBinaryDataEncoding::TypeEncod UA_ExtensionObject_copy(&obj, ptr); } +QDateTime uaDateTimeToQDateTime(UA_DateTime dt) +{ + if (!dt) + return QDateTime(); + + // OPC-UA part 3, Table C.9 + const QDateTime epochStart(QDate(1601, 1, 1), QTime(0, 0), Qt::UTC); + return epochStart.addMSecs(dt * UA_DATETIME_TO_MSEC).toLocalTime(); +} + } QT_END_NAMESPACE diff --git a/src/plugins/opcua/open62541/qopen62541valueconverter.h b/src/plugins/opcua/open62541/qopen62541valueconverter.h index bf20b16..f2b3995 100644 --- a/src/plugins/opcua/open62541/qopen62541valueconverter.h +++ b/src/plugins/opcua/open62541/qopen62541valueconverter.h @@ -77,6 +77,8 @@ namespace QOpen62541ValueConverter { UA_Variant arrayFromQVariant(const QVariant &var, const UA_DataType *type); void createExtensionObject(QByteArray &data, QOpcUaBinaryDataEncoding::TypeEncodingId id, UA_ExtensionObject *ptr); + + QDateTime uaDateTimeToQDateTime(UA_DateTime dt); } QT_END_NAMESPACE diff --git a/src/plugins/opcua/uacpp/quacppbackend.cpp b/src/plugins/opcua/uacpp/quacppbackend.cpp index 20e0d49..fc4f477 100644 --- a/src/plugins/opcua/uacpp/quacppbackend.cpp +++ b/src/plugins/opcua/uacpp/quacppbackend.cpp @@ -260,6 +260,8 @@ void UACppAsyncBackend::readAttributes(uintptr_t handle, const UaNodeId &id, QOp for (int i = 0; i < vec.size(); ++i) { vec[i].statusCode = static_cast<QOpcUa::UaStatusCode>(values[i].StatusCode); vec[i].value = QUACppValueConverter::toQVariant(values[i].Value); + vec[i].serverTimestamp = QUACppValueConverter::toQDateTime(&values[i].ServerTimestamp); + vec[i].sourceTimestamp = QUACppValueConverter::toQDateTime(&values[i].SourceTimestamp); } } diff --git a/src/plugins/opcua/uacpp/quacppsubscription.cpp b/src/plugins/opcua/uacpp/quacppsubscription.cpp index bc45c6e..279bc86 100644 --- a/src/plugins/opcua/uacpp/quacppsubscription.cpp +++ b/src/plugins/opcua/uacpp/quacppsubscription.cpp @@ -334,9 +334,15 @@ void QUACppSubscription::dataChange(OpcUa_UInt32 clientSubscriptionHandle, const const QVariant var = QUACppValueConverter::toQVariant(dataNotifications[i].Value.Value); if (!m_monitoredIds.contains(monitorId)) continue; - emit m_backend->attributeUpdated(m_monitoredIds[monitorId].first, - m_monitoredIds[monitorId].second, - var); + + QOpcUaReadResult temp; + temp.value = var; + temp.serverTimestamp = QUACppValueConverter::toQDateTime(&dataNotifications[i].Value.ServerTimestamp); + temp.sourceTimestamp = QUACppValueConverter::toQDateTime(&dataNotifications[i].Value.SourceTimestamp); + temp.attributeId = m_monitoredIds[monitorId].second; + temp.statusCode = QOpcUa::UaStatusCode::Good; + + emit m_backend->attributeUpdated(m_monitoredIds[monitorId].first, temp); } } diff --git a/tests/auto/qopcuaclient/tst_client.cpp b/tests/auto/qopcuaclient/tst_client.cpp index 3f36d9f..4fc9b42 100644 --- a/tests/auto/qopcuaclient/tst_client.cpp +++ b/tests/auto/qopcuaclient/tst_client.cpp @@ -238,6 +238,8 @@ private slots: defineDataMethod(dateTimeConversion_data) void dateTimeConversion(); + defineDataMethod(timeStamps_data) + void timeStamps(); // This test case restarts the server. It must be run last to avoid // destroying state required by other test cases. @@ -1948,6 +1950,58 @@ void Tst_QOpcUaClient::dateTimeConversion() QVERIFY(dt == result); } +void Tst_QOpcUaClient::timeStamps() +{ + QFETCH(QOpcUaClient *, opcuaClient); + OpcuaConnector connector(opcuaClient, m_endpoint); + + QScopedPointer<QOpcUaNode> stringScalarNode(opcuaClient->node("ns=2;s=Demo.Static.Scalar.String")); + + QVERIFY(stringScalarNode != 0); + + QCOMPARE(stringScalarNode->sourceTimestamp(QOpcUa::NodeAttribute::Value).isValid(), false); + QCOMPARE(stringScalarNode->serverTimestamp(QOpcUa::NodeAttribute::Value).isValid(), false); + + READ_MANDATORY_VARIABLE_NODE(stringScalarNode); + + const QDateTime sourceRead = stringScalarNode->sourceTimestamp(QOpcUa::NodeAttribute::Value); + const QDateTime serverRead = stringScalarNode->serverTimestamp(QOpcUa::NodeAttribute::Value); + + QVERIFY(sourceRead.isValid()); + QVERIFY(serverRead.isValid()); + + QOpcUaMonitoringParameters p(100); + QSignalSpy monitoringEnabledSpy(stringScalarNode.data(), &QOpcUaNode::enableMonitoringFinished); + QSignalSpy monitoringDisabledSpy(stringScalarNode.data(), &QOpcUaNode::disableMonitoringFinished); + QSignalSpy dataChangeSpy(stringScalarNode.data(), &QOpcUaNode::attributeUpdated); + + QTest::qWait(10); // Make sure the timestamp has a chance to change + + WRITE_VALUE_ATTRIBUTE(stringScalarNode, "Reset", QOpcUa::Types::String); + + stringScalarNode->enableMonitoring(QOpcUa::NodeAttribute::Value, QOpcUaMonitoringParameters(100)); + + monitoringEnabledSpy.wait(); + QCOMPARE(monitoringEnabledSpy.size(), 1); + QCOMPARE(monitoringEnabledSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value); + QCOMPARE(monitoringEnabledSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); + + dataChangeSpy.wait(); + const QDateTime sourceDataChange = stringScalarNode->sourceTimestamp(QOpcUa::NodeAttribute::Value); + const QDateTime serverDataChange = stringScalarNode->serverTimestamp(QOpcUa::NodeAttribute::Value); + + QVERIFY(sourceDataChange.isValid()); + QVERIFY(serverDataChange.isValid()); + QVERIFY(sourceRead < sourceDataChange); + QVERIFY(serverRead < serverDataChange); + + stringScalarNode->disableMonitoring(QOpcUa::NodeAttribute::Value); + monitoringDisabledSpy.wait(); + QCOMPARE(monitoringDisabledSpy.size(), 1); + QCOMPARE(monitoringDisabledSpy.at(0).at(0).value<QOpcUa::NodeAttribute>(), QOpcUa::NodeAttribute::Value); + QCOMPARE(monitoringDisabledSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); +} + void Tst_QOpcUaClient::connectionLost() { // Restart the test server if necessary |