summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJannis Voelker <jannis.voelker@basyskom.com>2018-02-08 10:54:59 +0100
committerJannis Völker <jannis.voelker@basyskom.com>2018-03-14 09:54:46 +0000
commit54e9e56b93a1505ea75acc63ad0958aa4722bbc7 (patch)
treec94e3ec8d534a6b38b71862c6a99345dbb79f309
parentfd4adcd098cef84fb874cc0e290e9ab65504abed (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.h2
-rw-r--r--src/opcua/client/qopcuaclientimpl.cpp4
-rw-r--r--src/opcua/client/qopcuaclientimpl_p.h2
-rw-r--r--src/opcua/client/qopcuanode.cpp33
-rw-r--r--src/opcua/client/qopcuanode.h2
-rw-r--r--src/opcua/client/qopcuanode_p.h24
-rw-r--r--src/opcua/client/qopcuanodeimpl_p.h4
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuasubscription.cpp22
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuasubscription.h2
-rw-r--r--src/plugins/opcua/freeopcua/qfreeopcuaworker.cpp3
-rw-r--r--src/plugins/opcua/open62541/qopen62541backend.cpp5
-rw-r--r--src/plugins/opcua/open62541/qopen62541subscription.cpp10
-rw-r--r--src/plugins/opcua/open62541/qopen62541valueconverter.cpp16
-rw-r--r--src/plugins/opcua/open62541/qopen62541valueconverter.h2
-rw-r--r--src/plugins/opcua/uacpp/quacppbackend.cpp2
-rw-r--r--src/plugins/opcua/uacpp/quacppsubscription.cpp12
-rw-r--r--tests/auto/qopcuaclient/tst_client.cpp54
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