Browse Source

Merge branch '0.2'

Julius Pfrommer 7 years ago
parent
commit
2f0cbc3fe7

+ 1 - 1
src/server/ua_services_call.c

@@ -44,7 +44,7 @@ argumentsConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequi
     for(size_t i = 0; i < argReqsSize; ++i)
         retval |= typeCheckValue(server, &argReqs[i].dataType, argReqs[i].valueRank,
                                  argReqs[i].arrayDimensionsSize, argReqs[i].arrayDimensions,
-                                 &args[i], NULL, args);
+                                 &args[i], NULL, &args[i]);
     return retval;
 }
 

+ 25 - 13
src/server/ua_services_nodemanagement.c

@@ -1141,6 +1141,23 @@ UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
 /* Delete Nodes */
 /****************/
 
+/* In the single-threaded case, references are removed in the node (even though
+ * it appears to be const here). We always delete the last element. And then
+ * reread the node. */
+static void
+removeReferences(UA_Server *server, UA_Session *session, const volatile UA_Node *node) {
+    UA_DeleteReferencesItem item;
+    UA_DeleteReferencesItem_init(&item);
+    item.sourceNodeId = node->nodeId;
+    item.deleteBidirectional = true;
+    for(size_t refs = node->referencesSize; refs > 0; --refs) {
+        item.isForward = !node->references[refs - 1].isInverse;
+        item.targetNodeId.nodeId = node->references[refs - 1].targetId.nodeId;
+        item.referenceTypeId = node->references[refs - 1].referenceTypeId;
+        Service_DeleteReferences_single(server, session, &item);
+    }
+}
+
 UA_StatusCode
 Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
                            const UA_NodeId *nodeId, UA_Boolean deleteReferences) {
@@ -1148,7 +1165,9 @@ Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
     if(!node)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
-    /* destroy an object before removing it */
+    /* TODO: check if consistency is violated */
+
+    /* Destroy an object before removing it */
     if(node->nodeClass == UA_NODECLASS_OBJECT) {
         /* find the object type(s) */
         UA_BrowseDescription bd;
@@ -1165,18 +1184,10 @@ Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
             typenode->lifecycleManagement.destructor(*nodeId, ((const UA_ObjectNode*)node)->instanceHandle);
     }
 
-    /* remove references */
-    /* TODO: check if consistency is violated */
-    if(deleteReferences == true) { 
-        for(size_t i = 0; i < node->referencesSize; ++i) {
-            UA_DeleteReferencesItem item;
-            UA_DeleteReferencesItem_init(&item);
-            item.isForward = node->references[i].isInverse;
-            item.sourceNodeId = node->references[i].targetId.nodeId;
-            item.targetNodeId.nodeId = node->nodeId;
-            UA_Server_editNode(server, session, &node->references[i].targetId.nodeId,
-                               (UA_EditNodeCallback)deleteOneWayReference, &item);
-        }
+    /* Remove references */
+    if(deleteReferences) {
+        removeReferences(server, session, node);
+        UA_assert(node->referencesSize == 0);
     }
 
     return UA_NodeStore_remove(server->nodestore, nodeId);
@@ -1265,6 +1276,7 @@ Service_DeleteReferences_single(UA_Server *server, UA_Session *session,
     secondItem.isForward = !item->isForward;
     secondItem.sourceNodeId = item->targetNodeId.nodeId;
     secondItem.targetNodeId.nodeId = item->sourceNodeId;
+    secondItem.referenceTypeId = item->referenceTypeId;
     return UA_Server_editNode(server, session, &secondItem.sourceNodeId,
                               (UA_EditNodeCallback)deleteOneWayReference, &secondItem);
 }

+ 70 - 1
tests/check_services_nodemanagement.c

@@ -166,12 +166,80 @@ START_TEST(DeleteObjectWithDestructor) {
     /* Delete the object */
     UA_Server_deleteNode(server, objectid, true);
 
-    /* Verify that the denstructor was called */
+    /* Verify that the destructor was called */
     ck_assert_int_eq(destructorCalled, true);
 
     UA_Server_delete(server);
 } END_TEST
 
+START_TEST(DeleteObjectAndReferences) {
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+
+    /* Add an object of the type */
+    UA_ObjectAttributes attr;
+    UA_ObjectAttributes_init(&attr);
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","my object");
+    UA_NodeId objectid = UA_NODEID_NUMERIC(0, 23372337);
+    UA_StatusCode res;
+    res = UA_Server_addObjectNode(server, objectid, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""),
+                                  UA_NODEID_NULL, attr, NULL, NULL);
+    ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
+
+    /* Verify that we have a reference to the node from the objects folder */
+    UA_BrowseDescription bd;
+    UA_BrowseDescription_init(&bd);
+    bd.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+    bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
+    bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
+    
+    UA_BrowseResult br = UA_Server_browse(server, 0, &bd);
+    ck_assert_int_eq(br.statusCode, UA_STATUSCODE_GOOD);
+    size_t refCount = 0;
+    for(size_t i = 0; i < br.referencesSize; ++i) {
+        if(UA_NodeId_equal(&br.references[i].nodeId.nodeId, &objectid))
+            refCount++;
+    }
+    ck_assert_int_eq(refCount, 1);
+    UA_BrowseResult_deleteMembers(&br);
+
+    /* Delete the object */
+    UA_Server_deleteNode(server, objectid, true);
+
+    /* Browse again, this time we expect that no reference is found */
+    br = UA_Server_browse(server, 0, &bd);
+    ck_assert_int_eq(br.statusCode, UA_STATUSCODE_GOOD);
+    refCount = 0;
+    for(size_t i = 0; i < br.referencesSize; ++i) {
+        if(UA_NodeId_equal(&br.references[i].nodeId.nodeId, &objectid))
+            refCount++;
+    }
+    ck_assert_int_eq(refCount, 0);
+    UA_BrowseResult_deleteMembers(&br);
+
+    /* Add an object the second time */
+    UA_ObjectAttributes_init(&attr);
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","my object");
+    objectid = UA_NODEID_NUMERIC(0, 23372337);
+    res = UA_Server_addObjectNode(server, objectid, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""),
+                                  UA_NODEID_NULL, attr, NULL, NULL);
+    ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
+
+    /* Browse again, this time we expect that a single reference to the node is found */
+    refCount = 0;
+    br = UA_Server_browse(server, 0, &bd);
+    ck_assert_int_eq(br.statusCode, UA_STATUSCODE_GOOD);
+    for(size_t i = 0; i < br.referencesSize; ++i) {
+        if(UA_NodeId_equal(&br.references[i].nodeId.nodeId, &objectid))
+            refCount++;
+    }
+    ck_assert_int_eq(refCount, 1);
+    UA_BrowseResult_deleteMembers(&br);
+
+    UA_Server_delete(server);
+} END_TEST
+
 static Suite * testSuite_services_nodemanagement(void) {
     Suite *s = suite_create("services_nodemanagement");
 
@@ -183,6 +251,7 @@ static Suite * testSuite_services_nodemanagement(void) {
 
     TCase *tc_deletenodes = tcase_create("deletenodes");
     tcase_add_test(tc_addnodes, DeleteObjectWithDestructor);
+    tcase_add_test(tc_addnodes, DeleteObjectAndReferences);
 
     suite_add_tcase(s, tc_addnodes);
     suite_add_tcase(s, tc_deletenodes);