Ver código fonte

make function to get type-hierarchy reusable outside of services_view

Julius Pfrommer 8 anos atrás
pai
commit
2525fd527e

+ 5 - 1
src/server/ua_server_internal.h

@@ -127,8 +127,12 @@ getVariableNodeType(UA_Server *server, const UA_VariableNode *node);
 const UA_ObjectTypeNode *
 getObjectNodeType(UA_Server *server, const UA_ObjectNode *node);
 
+/* Returns an array with all subtype nodeids (including the root). Subtypes need
+ * to have the same nodeClass as root and are (recursively) related with a
+ * hasSubType reference. Since multi-inheritance is possible, we test for
+ * duplicates and return evey nodeid at most once. */
 UA_StatusCode
-getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *root,
+getTypeHierarchy(UA_NodeStore *ns, const UA_Node *root,
                  UA_NodeId **reftypes, size_t *reftypes_count);
 
 UA_StatusCode

+ 39 - 30
src/server/ua_server_utils.c

@@ -81,60 +81,69 @@ parse_numericrange(const UA_String *str, UA_NumericRange *range) {
 /* Information Model Operations */
 /********************************/
 
-/* Returns the type and all subtypes. We start with an array with a single root
- * nodeid. When a relevant reference is found, we add the nodeids to the back of
- * the array and increase the size. Since the hierarchy is not cyclic, we can
- * safely progress in the array to process the newly found referencetype
- * nodeids. */
 UA_StatusCode
-getTypeHierarchy(UA_NodeStore *ns, const UA_NodeId *root,
+getTypeHierarchy(UA_NodeStore *ns, const UA_Node *root,
                  UA_NodeId **typeHierarchy, size_t *typeHierarchySize) {
-    const UA_Node *node = UA_NodeStore_get(ns, root);
-    if(!node)
-        return UA_STATUSCODE_BADNOMATCH;
-    if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)
-        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-
     size_t results_size = 20; // probably too big, but saves mallocs
     UA_NodeId *results = UA_malloc(sizeof(UA_NodeId) * results_size);
     if(!results)
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    UA_StatusCode retval = UA_NodeId_copy(root, &results[0]);
+    UA_StatusCode retval = UA_NodeId_copy(&root->nodeId, &results[0]);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_free(results);
         return retval;
     }
 
-    size_t idx = 0; // where are we currently in the array?
-    size_t last = 0; // where is the last element in the array?
+    const UA_Node *node = root;
+    size_t idx = 0; /* Current index (contains NodeId of node) */
+    size_t last = 0; /* Index of the last element in the array */
     const UA_NodeId hasSubtypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
-    do {
-        node = UA_NodeStore_get(ns, &results[idx]);
-        if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE)
-            continue;
+    while(true) {
         for(size_t i = 0; i < node->referencesSize; i++) {
+            /* is the reference relevant? */
             if(node->references[i].isInverse == true ||
                !UA_NodeId_equal(&hasSubtypeNodeId, &node->references[i].referenceTypeId))
                 continue;
 
-            if(++last >= results_size) { // is the array big enough?
-                UA_NodeId *new_results = UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
-                if(!new_results) {
-                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
+            /* is the target already considered? (multi-inheritance) */
+            UA_Boolean duplicate = false;
+            for(size_t j = 0; j <= last; j++) {
+                if(UA_NodeId_equal(&node->references[i].targetId.nodeId, &results[j])) {
+                    duplicate = true;
                     break;
                 }
-                results = new_results;
-                results_size *= 2;
             }
+            if(duplicate)
+                continue;
 
-            retval = UA_NodeId_copy(&node->references[i].targetId.nodeId, &results[last]);
-            if(retval != UA_STATUSCODE_GOOD) {
-                last--; // for array_delete
-                break;
+            /* increase array length if necessary */
+            if(last + 1 >= results_size) {
+                                UA_NodeId *new_results =
+                                    UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
+                                if(!new_results) {
+                                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
+                                    break;
+                                }
+                                results = new_results;
+                                results_size *= 2;
             }
+
+            /* copy new nodeid to the end of the list */
+            retval = UA_NodeId_copy(&node->references[i].targetId.nodeId, &results[++last]);
+            if(retval != UA_STATUSCODE_GOOD)
+                break;
         }
-    } while(++idx <= last && retval == UA_STATUSCODE_GOOD);
+
+        /* Get the next node */
+    next:
+        idx++;
+        if(idx > last || retval != UA_STATUSCODE_GOOD)
+            break;
+        node = UA_NodeStore_get(ns, &results[idx]);
+        if(!node || node->nodeClass != root->nodeClass)
+            goto next;
+    }
 
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);

+ 11 - 72
src/server/ua_services_view.c

@@ -125,70 +125,6 @@ returnRelevantNode(UA_Server *server, const UA_BrowseDescription *descr, UA_Bool
     return node;
 }
 
-/**
- * We find all subtypes by a single iteration over the array. We start with an array with a single
- * root nodeid at the beginning. When we find relevant references, we add the nodeids to the back of
- * the array and increase the size. Since the hierarchy is not cyclic, we can safely progress in the
- * array to process the newly found referencetype nodeids (emulated recursion).
- */
-static UA_StatusCode
-findSubTypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes, size_t *reftypes_count) {
-    const UA_Node *node = UA_NodeStore_get(ns, root);
-    if(!node)
-        return UA_STATUSCODE_BADNOMATCH;
-    if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)
-        return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-
-    size_t results_size = 20; // probably too big, but saves mallocs
-    UA_NodeId *results = UA_malloc(sizeof(UA_NodeId) * results_size);
-    if(!results)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-
-    UA_StatusCode retval = UA_NodeId_copy(root, &results[0]);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_free(results);
-        return retval;
-    }
-        
-    size_t idx = 0; // where are we currently in the array?
-    size_t last = 0; // where is the last element in the array?
-    do {
-        node = UA_NodeStore_get(ns, &results[idx]);
-        if(!node || node->nodeClass != UA_NODECLASS_REFERENCETYPE)
-            continue;
-        for(size_t i = 0; i < node->referencesSize; i++) {
-            if(node->references[i].referenceTypeId.identifier.numeric != UA_NS0ID_HASSUBTYPE ||
-               node->references[i].isInverse == true)
-                continue;
-
-            if(++last >= results_size) { // is the array big enough?
-                UA_NodeId *new_results = UA_realloc(results, sizeof(UA_NodeId) * results_size * 2);
-                if(!new_results) {
-                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                    break;
-                }
-                results = new_results;
-                results_size *= 2;
-            }
-
-            retval = UA_NodeId_copy(&node->references[i].targetId.nodeId, &results[last]);
-            if(retval != UA_STATUSCODE_GOOD) {
-                last--; // for array_delete
-                break;
-            }
-        }
-    } while(++idx <= last && retval == UA_STATUSCODE_GOOD);
-
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_Array_delete(results, last, &UA_TYPES[UA_TYPES_NODEID]);
-        return retval;
-    }
-
-    *reftypes = results;
-    *reftypes_count = last + 1;
-    return UA_STATUSCODE_GOOD;
-}
-
 static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session) {
     LIST_REMOVE(cp, pointers);
     UA_ByteString_deleteMembers(&cp->identifier);
@@ -234,17 +170,17 @@ Service_Browse_single(UA_Server *server, UA_Session *session,
     UA_NodeId *relevant_refs = NULL;
     UA_Boolean all_refs = UA_NodeId_isNull(&descr->referenceTypeId);
     if(!all_refs) {
+        const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &descr->referenceTypeId);
+        if(!rootRef || rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE) {
+            result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+            return;
+        }
         if(descr->includeSubtypes) {
-            result->statusCode = findSubTypes(server->nodestore, &descr->referenceTypeId,
-                                              &relevant_refs, &relevant_refs_size);
+            result->statusCode = getTypeHierarchy(server->nodestore, rootRef,
+                                                  &relevant_refs, &relevant_refs_size);
             if(result->statusCode != UA_STATUSCODE_GOOD)
                 return;
         } else {
-            const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &descr->referenceTypeId);
-            if(!rootRef || rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE) {
-                result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
-                return;
-            }
             relevant_refs = (UA_NodeId*)(uintptr_t)&descr->referenceTypeId;
             relevant_refs_size = 1;
         }
@@ -490,7 +426,10 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     } else if(!elem->includeSubtypes) {
         reftypes = (UA_NodeId*)(uintptr_t)&elem->referenceTypeId; // ptr magic due to const cast
     } else {
-        retval = findSubTypes(server->nodestore, &elem->referenceTypeId, &reftypes, &reftypes_count);
+        const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &elem->referenceTypeId);
+        if(!rootRef || rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE)
+            return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
+        retval = getTypeHierarchy(server->nodestore, rootRef, &reftypes, &reftypes_count);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }