diff options
author | Jannis Voelker <jannis.voelker@basyskom.com> | 2018-04-06 08:15:23 +0200 |
---|---|---|
committer | Frank Meerkoetter <frank.meerkoetter@basyskom.com> | 2018-08-17 08:25:15 +0000 |
commit | 363e423d86039fecdb750dda15c680a2fcef5e4c (patch) | |
tree | f76e07ce1ce1e6622329297011993c9d0f693302 /tests | |
parent | d76b4776ebace5627e02361aa636c88d5f9e790c (diff) |
Add node management
This patch adds a node management API to Qt OPC UA.
The support is open62541 only for now.
- addNode()
- deleteNode()
- addReference()
- deleteReference()
Change-Id: I60631346a1a4a097ba4555e4bb3cbc93c71efee8
Reviewed-by: Frank Meerkoetter <frank.meerkoetter@basyskom.com>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/qopcuaclient/tst_client.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/tests/auto/qopcuaclient/tst_client.cpp b/tests/auto/qopcuaclient/tst_client.cpp index 864b06b..195e358 100644 --- a/tests/auto/qopcuaclient/tst_client.cpp +++ b/tests/auto/qopcuaclient/tst_client.cpp @@ -378,6 +378,13 @@ private slots: defineDataMethod(inverseBrowse_data) void inverseBrowse(); + defineDataMethod(addAndRemoveObjectNode_data) + void addAndRemoveObjectNode(); + defineDataMethod(addAndRemoveVariableNode_data) + void addAndRemoveVariableNode(); + defineDataMethod(addAndRemoveReference_data) + void addAndRemoveReference(); + defineDataMethod(dataChangeSubscription_data) void dataChangeSubscription(); defineDataMethod(dataChangeSubscriptionInvalidNode_data) @@ -1084,6 +1091,268 @@ void Tst_QOpcUaClient::inverseBrowse() QCOMPARE(ref.at(0).nodeClass(), QOpcUa::NodeClass::DataType); } +void Tst_QOpcUaClient::addAndRemoveObjectNode() +{ + QFETCH(QOpcUaClient *, opcuaClient); + + if (opcuaClient->backend() != QLatin1String("open62541")) + QSKIP("NodeManagement is currently only supported in the open62541 backend"); + + OpcuaConnector connector(opcuaClient, m_endpoint); + + QSignalSpy addNodeSpy(opcuaClient, &QOpcUaClient::addNodeFinished); + + QOpcUa::QExpandedNodeId parent; + parent.setNodeId(QStringLiteral("ns=3;s=TestFolder")); + QOpcUa::QExpandedNodeId requestedNewId; + requestedNewId.setNodeId(QStringLiteral("ns=3;s=DynamicObjectNode_%1").arg(opcuaClient->backend())); + const quint16 namespaceIndex = 3; + QOpcUa::QQualifiedName browseName(namespaceIndex, QStringLiteral("DynamicObjectNode_%1").arg(opcuaClient->backend())); + + QOpcUaNodeCreationAttributes attributes; + + QOpcUa::QLocalizedText displayName("en", browseName.name()); + QOpcUa::QLocalizedText description("en", QStringLiteral("Node added at runtime by %1").arg(opcuaClient->backend())); + + attributes.setDisplayName(displayName); + attributes.setDescription(description); + + QOpcUaAddNodeItem nodeInfo; + nodeInfo.setParentNodeId(parent); + nodeInfo.setReferenceTypeId(QOpcUa::nodeIdFromReferenceType(QOpcUa::ReferenceTypeId::Organizes)); + nodeInfo.setRequestedNewNodeId(requestedNewId); + nodeInfo.setBrowseName(browseName); + nodeInfo.setNodeClass(QOpcUa::NodeClass::Object); + nodeInfo.setNodeAttributes(attributes); + + opcuaClient->addNode(nodeInfo); + + addNodeSpy.wait(); + + QCOMPARE(addNodeSpy.count(), 1); + + QCOMPARE(addNodeSpy.at(0).at(0).value<QOpcUa::QExpandedNodeId>().nodeId(), requestedNewId.nodeId()); + QCOMPARE(addNodeSpy.at(0).at(1), requestedNewId.nodeId()); + QCOMPARE(addNodeSpy.at(0).at(2).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); + + QScopedPointer<QOpcUaNode> newNode(opcuaClient->node(requestedNewId.nodeId())); + QVERIFY(newNode != nullptr); + + QSignalSpy readSpy(newNode.data(), &QOpcUaNode::attributeRead); + newNode->readAttributes(QOpcUa::NodeAttribute::BrowseName | QOpcUa::NodeAttribute::DisplayName | QOpcUa::NodeAttribute::Description); + readSpy.wait(); + + QCOMPARE(readSpy.size(), 1); + + QCOMPARE(newNode->attributeError(QOpcUa::NodeAttribute::BrowseName), QOpcUa::UaStatusCode::Good); + QCOMPARE(newNode->attributeError(QOpcUa::NodeAttribute::DisplayName), QOpcUa::UaStatusCode::Good); + QCOMPARE(newNode->attributeError(QOpcUa::NodeAttribute::Description), QOpcUa::UaStatusCode::Good); + + QCOMPARE(newNode->attribute(QOpcUa::NodeAttribute::BrowseName).value<QOpcUa::QQualifiedName>(), browseName); + QCOMPARE(newNode->attribute(QOpcUa::NodeAttribute::DisplayName).value<QOpcUa::QLocalizedText>(), displayName); + QCOMPARE(newNode->attribute(QOpcUa::NodeAttribute::Description).value<QOpcUa::QLocalizedText>(), description); + + QSignalSpy removeNodeSpy(opcuaClient, &QOpcUaClient::deleteNodeFinished); + opcuaClient->deleteNode(requestedNewId.nodeId(), true); + removeNodeSpy.wait(); + + QCOMPARE(removeNodeSpy.size(), 1); + QCOMPARE(removeNodeSpy.at(0).at(0), requestedNewId.nodeId()); + QCOMPARE(removeNodeSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); + + QScopedPointer<QOpcUaNode> node(opcuaClient->node(requestedNewId.nodeId())); + QVERIFY(node != nullptr); + + QSignalSpy unsuccessfulReadSpy(node.data(), &QOpcUaNode::attributeRead); + + node->readAttributes(QOpcUa::NodeAttribute::NodeId); + unsuccessfulReadSpy.wait(); + QCOMPARE(unsuccessfulReadSpy.size(), 1); + QVERIFY(unsuccessfulReadSpy.at(0).at(0).value<QOpcUa::NodeAttributes>() & QOpcUa::NodeAttribute::NodeId); + QCOMPARE(node->attributeError(QOpcUa::NodeAttribute::NodeId), QOpcUa::UaStatusCode::BadNodeIdUnknown); +} + +void Tst_QOpcUaClient::addAndRemoveVariableNode() +{ + QFETCH(QOpcUaClient *, opcuaClient); + + if (opcuaClient->backend() != QLatin1String("open62541")) + QSKIP("NodeManagement is currently only supported in the open62541 backend"); + + OpcuaConnector connector(opcuaClient, m_endpoint); + + QSignalSpy addNodeSpy(opcuaClient, &QOpcUaClient::addNodeFinished); + + QOpcUa::QExpandedNodeId parent; + parent.setNodeId(QStringLiteral("ns=3;s=TestFolder")); + QOpcUa::QExpandedNodeId requestedNewId; + requestedNewId.setNodeId(QStringLiteral("ns=3;s=DynamicVariableNode_%1").arg(opcuaClient->backend())); + const quint16 namespaceIndex = 3; + QOpcUa::QQualifiedName browseName(namespaceIndex, QStringLiteral("DynamicVariableNode_%1").arg(opcuaClient->backend())); + + QOpcUaNodeCreationAttributes attributes; + + QOpcUa::QLocalizedText displayName("en", browseName.name()); + QOpcUa::QLocalizedText description("en", QStringLiteral("Node added at runtime by %1").arg(opcuaClient->backend())); + + attributes.setDisplayName(displayName); + attributes.setDescription(description); + attributes.setValue(23.0, QOpcUa::Types::Double); + attributes.setDataTypeId(QOpcUa::ns0ID(QOpcUa::NodeIds::NS0::Double)); + attributes.setValueRank(-2); // Scalar or array + + attributes.setAccessLevel(QOpcUa::AccessLevelBit::CurrentRead); + attributes.setUserAccessLevel(QOpcUa::AccessLevelBit::CurrentRead); + + QOpcUaAddNodeItem nodeInfo; + nodeInfo.setParentNodeId(parent); + nodeInfo.setReferenceTypeId(QOpcUa::nodeIdFromReferenceType(QOpcUa::ReferenceTypeId::Organizes)); + nodeInfo.setRequestedNewNodeId(requestedNewId); + nodeInfo.setBrowseName(browseName); + nodeInfo.setNodeClass(QOpcUa::NodeClass::Variable); + nodeInfo.setNodeAttributes(attributes); + + opcuaClient->addNode(nodeInfo); + + addNodeSpy.wait(); + + QCOMPARE(addNodeSpy.count(), 1); + + QCOMPARE(addNodeSpy.at(0).at(0).value<QOpcUa::QExpandedNodeId>().nodeId(), requestedNewId.nodeId()); + QCOMPARE(addNodeSpy.at(0).at(1), requestedNewId.nodeId()); + QCOMPARE(addNodeSpy.at(0).at(2).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); + + QScopedPointer<QOpcUaNode> newNode(opcuaClient->node(requestedNewId.nodeId())); + QVERIFY(newNode != nullptr); + + QSignalSpy readSpy(newNode.data(), &QOpcUaNode::attributeRead); + newNode->readAttributes(QOpcUa::NodeAttribute::BrowseName | QOpcUa::NodeAttribute::DisplayName | QOpcUa::NodeAttribute::Description | QOpcUa::NodeAttribute::Value); + readSpy.wait(); + + QCOMPARE(readSpy.size(), 1); + + QCOMPARE(newNode->attributeError(QOpcUa::NodeAttribute::BrowseName), QOpcUa::UaStatusCode::Good); + QCOMPARE(newNode->attributeError(QOpcUa::NodeAttribute::DisplayName), QOpcUa::UaStatusCode::Good); + QCOMPARE(newNode->attributeError(QOpcUa::NodeAttribute::Description), QOpcUa::UaStatusCode::Good); + QCOMPARE(newNode->attributeError(QOpcUa::NodeAttribute::Value), QOpcUa::UaStatusCode::Good); + + QCOMPARE(newNode->attribute(QOpcUa::NodeAttribute::BrowseName).value<QOpcUa::QQualifiedName>(), browseName); + QCOMPARE(newNode->attribute(QOpcUa::NodeAttribute::DisplayName).value<QOpcUa::QLocalizedText>(), displayName); + QCOMPARE(newNode->attribute(QOpcUa::NodeAttribute::Description).value<QOpcUa::QLocalizedText>(), description); + QCOMPARE(newNode->attribute(QOpcUa::NodeAttribute::Value).toDouble(), 23.0); + + QSignalSpy removeNodeSpy(opcuaClient, &QOpcUaClient::deleteNodeFinished); + opcuaClient->deleteNode(requestedNewId.nodeId(), true); + removeNodeSpy.wait(); + + QCOMPARE(removeNodeSpy.size(), 1); + QCOMPARE(removeNodeSpy.at(0).at(0), requestedNewId.nodeId()); + QCOMPARE(removeNodeSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); + + QScopedPointer<QOpcUaNode> node(opcuaClient->node(requestedNewId.nodeId())); + QVERIFY(node != nullptr); + + QSignalSpy unsuccessfulReadSpy(node.data(), &QOpcUaNode::attributeRead); + + node->readAttributes(QOpcUa::NodeAttribute::NodeId); + unsuccessfulReadSpy.wait(); + QCOMPARE(unsuccessfulReadSpy.size(), 1); + QVERIFY(unsuccessfulReadSpy.at(0).at(0).value<QOpcUa::NodeAttributes>() & QOpcUa::NodeAttribute::NodeId); + QCOMPARE(node->attributeError(QOpcUa::NodeAttribute::NodeId), QOpcUa::UaStatusCode::BadNodeIdUnknown); +} + +void Tst_QOpcUaClient::addAndRemoveReference() +{ + QFETCH(QOpcUaClient *, opcuaClient); + + if (opcuaClient->backend() != QLatin1String("open62541")) + QSKIP("NodeManagement is currently only supported in the open62541 backend"); + + OpcuaConnector connector(opcuaClient, m_endpoint); + + QSignalSpy addReferenceSpy(opcuaClient, &QOpcUaClient::addReferenceFinished); + + QOpcUa::QExpandedNodeId target; + target.setNodeId(QStringLiteral("ns=3;s=TestFolder")); + QString referenceType = QOpcUa::nodeIdFromInteger(0, static_cast<quint32>(QOpcUa::ReferenceTypeId::Organizes)); + + QOpcUaAddReferenceItem refInfo; + refInfo.setSourceNodeId(QOpcUa::ns0ID(QOpcUa::NodeIds::NS0::RootFolder)); + refInfo.setReferenceTypeId(referenceType); + refInfo.setIsForward(true); + refInfo.setTargetNodeId(target); + refInfo.setTargetNodeClass(QOpcUa::NodeClass::Variable); + + opcuaClient->addReference(refInfo); + addReferenceSpy.wait(); + + QCOMPARE(addReferenceSpy.count(), 1); + QCOMPARE(addReferenceSpy.at(0).at(0), QOpcUa::ns0ID(QOpcUa::NodeIds::NS0::RootFolder)); + QCOMPARE(addReferenceSpy.at(0).at(1), referenceType); + QCOMPARE(addReferenceSpy.at(0).at(2).value<QOpcUa::QExpandedNodeId>().nodeId(), target.nodeId()); + QCOMPARE(addReferenceSpy.at(0).at(3).value<bool>(), true); + QCOMPARE(addReferenceSpy.at(0).at(4).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); + + // Check if the reference has been added + { + QScopedPointer<QOpcUaNode> node(opcuaClient->node(QOpcUa::ns0ID(QOpcUa::NodeIds::NS0::RootFolder))); + QVERIFY(node != nullptr); + + QSignalSpy browseSpy(node.data(), &QOpcUaNode::browseFinished); + + node->browseChildren(QOpcUa::ReferenceTypeId::Organizes); + browseSpy.wait(); + QCOMPARE(browseSpy.size(), 1); + QCOMPARE(browseSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); + QVector<QOpcUaReferenceDescription> results = browseSpy.at(0).at(0).value<QVector<QOpcUaReferenceDescription>>(); + QVERIFY(!results.isEmpty()); + bool referenceExists = false; + for (auto item : results) { + if (item.targetNodeId().nodeId() == target.nodeId()) { + referenceExists = true; + break; + } + } + QVERIFY(referenceExists); + } + + QSignalSpy deleteReferenceSpy(opcuaClient, &QOpcUaClient::deleteReferenceFinished); + + QOpcUaDeleteReferenceItem refDelInfo; + refDelInfo.setSourceNodeId(QOpcUa::ns0ID(QOpcUa::NodeIds::NS0::RootFolder)); + refDelInfo.setReferenceTypeId(referenceType); + refDelInfo.setIsForward(true); + refDelInfo.setTargetNodeId(target); + refDelInfo.setDeleteBidirectional(true); + + opcuaClient->deleteReference(refDelInfo); + deleteReferenceSpy.wait(); + + QCOMPARE(deleteReferenceSpy.count(), 1); + QCOMPARE(deleteReferenceSpy.at(0).at(0), QOpcUa::ns0ID(QOpcUa::NodeIds::NS0::RootFolder)); + QCOMPARE(deleteReferenceSpy.at(0).at(1), referenceType); + QCOMPARE(deleteReferenceSpy.at(0).at(2).value<QOpcUa::QExpandedNodeId>().nodeId(), target.nodeId()); + QCOMPARE(deleteReferenceSpy.at(0).at(3).value<bool>(), true); + QCOMPARE(deleteReferenceSpy.at(0).at(4).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); + + // Check if the reference has been deleted + { + QScopedPointer<QOpcUaNode> node(opcuaClient->node(QOpcUa::ns0ID(QOpcUa::NodeIds::NS0::RootFolder))); + QVERIFY(node != nullptr); + + QSignalSpy browseSpy(node.data(), &QOpcUaNode::browseFinished); + + node->browseChildren(QOpcUa::ReferenceTypeId::Organizes); + browseSpy.wait(); + QCOMPARE(browseSpy.size(), 1); + QCOMPARE(browseSpy.at(0).at(1).value<QOpcUa::UaStatusCode>(), QOpcUa::UaStatusCode::Good); + QVector<QOpcUaReferenceDescription> results = browseSpy.at(0).at(0).value<QVector<QOpcUaReferenceDescription>>(); + QVERIFY(!results.isEmpty()); + for (auto item : results) + QVERIFY(item.targetNodeId().nodeId() != target.nodeId()); + } +} + void Tst_QOpcUaClient::dataChangeSubscription() { QFETCH(QOpcUaClient *, opcuaClient); |