소스 검색

Merge branch '0.2'

Julius Pfrommer 7 년 전
부모
커밋
706b2d2776
4개의 변경된 파일113개의 추가작업 그리고 26개의 파일을 삭제
  1. 17 6
      include/ua_server.h
  2. 3 17
      src/server/ua_services_nodemanagement.c
  3. 91 1
      tests/check_services_nodemanagement.c
  4. 2 2
      tools/travis/travis_push_release.sh

+ 17 - 6
include/ua_server.h

@@ -709,6 +709,8 @@ UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
                                      const UA_DataSource dataSource);
 
 /**
+ * .. _value-callback:
+ *
  * Value Callback
  * ~~~~~~~~~~~~~~
  * Value Callbacks can be attached to variable and variable type nodes. If
@@ -788,12 +790,21 @@ UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId
  * When creating dynamic node instances at runtime, chances are that you will
  * not care about the specific NodeId of the new node, as long as you can
  * reference it later. When passing numeric NodeIds with a numeric identifier 0,
- * the stack evaluates this as "select a randome free NodeId in that namespace".
- * To find out which NodeId was actually assigned to the new node, you may pass
- * a pointer `outNewNodeId`, which will (after a successfull node insertion)
- * contain the nodeId of the new node. You may also pass NULL pointer if this
- * result is not relevant. The namespace index for nodes you create should never
- * be 0, as that index is reserved for OPC UA's self-description (namespace 0). */
+ * the stack evaluates this as "select a random unassigned numeric NodeId in
+ * that namespace". To find out which NodeId was actually assigned to the new
+ * node, you may pass a pointer `outNewNodeId`, which will (after a successfull
+ * node insertion) contain the nodeId of the new node. You may also pass NULL
+ * pointer if this result is not relevant. The namespace index for nodes you
+ * create should never be 0, as that index is reserved for OPC UA's
+ * self-description (namespace * 0).
+ *
+ * The methods for node addition and deletion take mostly const arguments that
+ * are not modified. When creating a node, a deep copy of the node identifier,
+ * node attributes, etc. is created. Therefore, it is possible to call for
+ * example `UA_Server_addVariablenode` with a value attribute (a :ref:`variant`)
+ * pointing to a memory location on the stack. If you need changes to a variable
+ * value to manifest at a specific memory location, please use a
+ * :ref:`datasource` or a :ref:`value-callback`. */
 /* The instantiation callback is used to track the addition of new nodes. It is
  * also called for all sub-nodes contained in an object or variable type node
  * that is instantiated. */

+ 3 - 17
src/server/ua_services_nodemanagement.c

@@ -1159,24 +1159,10 @@ Service_DeleteNodes_single(UA_Server *server, UA_Session *session,
         bd.includeSubtypes = true;
         bd.nodeClassMask = UA_NODECLASS_OBJECTTYPE;
 
-        /* browse type definitions with admin rights */
-        UA_BrowseResult result;
-        UA_BrowseResult_init(&result);
-        Service_Browse_single(server, &adminSession, NULL, &bd, 0, &result);
-        for(size_t i = 0; i < result.referencesSize; ++i) {
-            /* call the destructor */
-            UA_ReferenceDescription *rd = &result.references[i];
-            const UA_ObjectTypeNode *typenode =
-                (const UA_ObjectTypeNode*)UA_NodeStore_get(server->nodestore, &rd->nodeId.nodeId);
-            if(!typenode)
-                continue;
-            if(typenode->nodeClass != UA_NODECLASS_OBJECTTYPE || !typenode->lifecycleManagement.destructor)
-                continue;
-
-            /* if there are several types with lifecycle management, call all the destructors */
+        /* Call the destructor from the object type */
+        const UA_ObjectTypeNode *typenode = getObjectNodeType(server, (const UA_ObjectNode*)node);
+        if(typenode && typenode->lifecycleManagement.destructor)
             typenode->lifecycleManagement.destructor(*nodeId, ((const UA_ObjectNode*)node)->instanceHandle);
-        }
-        UA_BrowseResult_deleteMembers(&result);
     }
 
     /* remove references */

+ 91 - 1
tests/check_services_nodemanagement.c

@@ -87,15 +87,105 @@ START_TEST(AddNodeTwiceGivesError) {
     UA_Server_delete(server);
 } END_TEST
 
+static UA_Boolean constructorCalled = false;
+
+static void * objectConstructor(const UA_NodeId instance) {
+    constructorCalled = true;
+    return NULL;
+}
+
+START_TEST(AddObjectWithConstructor) {
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+
+    /* Add an object type */
+    UA_NodeId objecttypeid = UA_NODEID_NUMERIC(0, 13371337);
+    UA_ObjectTypeAttributes attr;
+    UA_ObjectTypeAttributes_init(&attr);
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","my objecttype");
+    UA_StatusCode res = UA_Server_addObjectTypeNode(server, objecttypeid,
+                                                    UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
+                                                    UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                                    UA_QUALIFIEDNAME(0, "myobjecttype"), attr, NULL, NULL);
+    ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
+
+    /* Add a constructor to the object type */
+    UA_ObjectLifecycleManagement olm = {objectConstructor, NULL};
+    res = UA_Server_setObjectTypeNode_lifecycleManagement(server, objecttypeid, olm);
+    ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
+
+    /* Add an object of the type */
+    UA_ObjectAttributes attr2;
+    UA_ObjectAttributes_init(&attr2);
+    attr2.displayName = UA_LOCALIZEDTEXT("en_US","my object");
+    res = UA_Server_addObjectNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""),
+                                  objecttypeid, attr2, NULL, NULL);
+    ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
+
+    /* Verify that the constructor was called */
+    ck_assert_int_eq(constructorCalled, true);
+
+    UA_Server_delete(server);
+} END_TEST
+
+static UA_Boolean destructorCalled = false;
+
+static void objectDestructor(const UA_NodeId instance, void *handle) {
+    destructorCalled = true;
+}
+
+START_TEST(DeleteObjectWithDestructor) {
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+
+    /* Add an object type */
+    UA_NodeId objecttypeid = UA_NODEID_NUMERIC(0, 13371337);
+    UA_ObjectTypeAttributes attr;
+    UA_ObjectTypeAttributes_init(&attr);
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","my objecttype");
+    UA_StatusCode res = UA_Server_addObjectTypeNode(server, objecttypeid,
+                                                    UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
+                                                    UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                                    UA_QUALIFIEDNAME(0, "myobjecttype"), attr, NULL, NULL);
+    ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
+
+    /* Add a constructor to the object type */
+    UA_ObjectLifecycleManagement olm = {NULL, objectDestructor};
+    res = UA_Server_setObjectTypeNode_lifecycleManagement(server, objecttypeid, olm);
+    ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
+
+    /* Add an object of the type */
+    UA_NodeId objectid = UA_NODEID_NUMERIC(0, 23372337);
+    UA_ObjectAttributes attr2;
+    UA_ObjectAttributes_init(&attr2);
+    attr2.displayName = UA_LOCALIZEDTEXT("en_US","my object");
+    res = UA_Server_addObjectNode(server, objectid, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(0, ""),
+                                  objecttypeid, attr2, NULL, NULL);
+    ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
+
+    /* Delete the object */
+    UA_Server_deleteNode(server, objectid, true);
+
+    /* Verify that the denstructor was called */
+    ck_assert_int_eq(destructorCalled, true);
+
+    UA_Server_delete(server);
+} END_TEST
+
 static Suite * testSuite_services_nodemanagement(void) {
     Suite *s = suite_create("services_nodemanagement");
 
     TCase *tc_addnodes = tcase_create("addnodes");
     tcase_add_test(tc_addnodes, AddVariableNode);
-        tcase_add_test(tc_addnodes, AddComplexTypeWithInheritance);
+    tcase_add_test(tc_addnodes, AddComplexTypeWithInheritance);
     tcase_add_test(tc_addnodes, AddNodeTwiceGivesError);
+    tcase_add_test(tc_addnodes, AddObjectWithConstructor);
+
+    TCase *tc_deletenodes = tcase_create("deletenodes");
+    tcase_add_test(tc_addnodes, DeleteObjectWithDestructor);
 
     suite_add_tcase(s, tc_addnodes);
+    suite_add_tcase(s, tc_deletenodes);
     return s;
 }
 

+ 2 - 2
tools/travis/travis_push_release.sh

@@ -23,8 +23,8 @@ if [ ! -e "$TAG.zip" ]; then
     cat head.txt rawtable.txt foot.txt > index.html
 
     #create a zip for single-file release and copy the files
-    cp ../../open62541.c .
-    cp ../../open62541.h .
+    cp ../../../open62541.c .
+    cp ../../../open62541.h .
     cp ../../../doc_latex/open62541.pdf .
     zip -r "$TAG.zip" open62541.c open62541.h open62541.pdf
     rm open62541.c