소스 검색

translatebrowsepaths uses breadth-first search for less referencetype lookups

Julius Pfrommer 7 년 전
부모
커밋
6f7382154b
2개의 변경된 파일264개의 추가작업 그리고 108개의 파일을 삭제
  1. 239 71
      src/server/ua_services_view.c
  2. 25 37
      tests/check_services_view.c

+ 239 - 71
src/server/ua_services_view.c

@@ -419,12 +419,74 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
 /* TranslateBrowsePath */
 /***********************/
 
-static UA_StatusCode
-walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, const UA_RelativePath *path,
-               size_t pathindex, UA_BrowsePathTarget **targets, size_t *targets_size,
-               size_t *target_count) {
-    const UA_RelativePathElement *elem = &path->elements[pathindex];
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+static void
+walkBrowsePathElementNode(UA_BrowsePathResult *result, size_t *targetsSize,
+                          UA_NodeId **next, size_t *nextSize, size_t *nextCount,
+                          UA_UInt32 elemDepth, UA_Boolean inverse, UA_Boolean all_refs,
+                          const UA_NodeId *reftypes, size_t reftypes_count, const UA_Node *node) {
+    /* Loop over the nodes references */
+    for(size_t i = 0; i < node->referencesSize &&
+            result->statusCode == UA_STATUSCODE_GOOD; i++) {
+        UA_ReferenceNode *reference = &node->references[i];
+
+        /* Does the direction of the reference match? */
+        if(reference->isInverse != inverse)
+            continue;
+
+        /* Is the node relevant? */
+        if(!all_refs) {
+            UA_Boolean match = false;
+            for(size_t j = 0; j < reftypes_count; ++j) {
+                if(UA_NodeId_equal(&reference->referenceTypeId, &reftypes[j])) {
+                    match = true;
+                    break;
+                }
+            }
+            if(!match)
+                continue;
+        }
+
+        /* Does the reference point to an external server? Then add to the
+           targets with the right path "depth" */
+        if(reference->targetId.serverIndex != 0) {
+            UA_BrowsePathTarget *tempTargets =
+                UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) * (*targetsSize) * 2);
+            if(!tempTargets) {
+                result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+                return;
+            }
+            result->targets = tempTargets;
+            (*targetsSize) *= 2;
+            result->statusCode = UA_ExpandedNodeId_copy(&reference->targetId,
+                                                        &result->targets[result->targetsSize].targetId);
+            result->targets[result->targetsSize].remainingPathIndex = elemDepth;
+            continue;
+        }
+
+        /* Add the node to the next array for the following path element */
+        if(*nextSize <= *nextCount) {
+            UA_NodeId *tempNext = UA_realloc(*next, sizeof(UA_NodeId) * (*nextSize) * 2);
+            if(!tempNext) {
+                result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+                return;
+            }
+            *next = tempNext;
+            (*nextSize) *= 2;
+        }
+        result->statusCode = UA_NodeId_copy(&reference->targetId.nodeId,
+                                            &(*next)[*nextCount]);
+        ++(*nextCount);
+    }
+}
+
+static void
+walkBrowsePathElement(UA_Server *server, UA_Session *session,
+                      UA_BrowsePathResult *result, size_t *targetsSize,
+                      const UA_RelativePathElement *elem, UA_UInt32 elemDepth,
+                      const UA_QualifiedName *targetName,
+                      const UA_NodeId *current, const size_t currentCount,
+                      UA_NodeId **next, size_t *nextSize, size_t *nextCount) {
+    /* Get the full list of relevant referencetypes for this path element */
     UA_NodeId *reftypes = NULL;
     size_t reftypes_count = 1; // all_refs or no subtypes => 1
     UA_Boolean all_refs = false;
@@ -435,104 +497,208 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     } else {
         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, false, &reftypes, &reftypes_count);
+            return;
+        UA_StatusCode retval =
+            getTypeHierarchy(server->nodestore, rootRef, false, &reftypes, &reftypes_count);
         if(retval != UA_STATUSCODE_GOOD)
-            return retval;
+            return;
     }
 
-    for(size_t i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; ++i) {
-        UA_Boolean match = all_refs;
-        for(size_t j = 0; j < reftypes_count && !match; ++j) {
-            if(node->references[i].isInverse == elem->isInverse &&
-               UA_NodeId_equal(&node->references[i].referenceTypeId, &reftypes[j]))
-                match = true;
+    /* Iterate over all nodes at the current depth-level */
+    for(size_t i = 0; i < currentCount; ++i) {
+        /* Get the node */
+        const UA_Node *node = UA_NodeStore_get(server->nodestore, &current[i]);
+        if(!node) {
+            /* If we cannot find the node at depth 0, the starting node does not exist */
+            if(elemDepth == 0)
+                result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
+            continue;
         }
-        if(!match)
+
+        /* Test whether the current node has the target name required in the
+         * previous path element */
+        if(targetName && (targetName->namespaceIndex != node->browseName.namespaceIndex ||
+                          !UA_String_equal(&targetName->name, &node->browseName.name)))
             continue;
 
-        // get the node, todo: expandednodeid
-        const UA_Node *next = UA_NodeStore_get(server->nodestore, &node->references[i].targetId.nodeId);
-        if(!next)
+        /* Walk over the references in the node */
+        walkBrowsePathElementNode(result, targetsSize, next, nextSize, nextCount, elemDepth,
+                                  elem->isInverse, all_refs, reftypes, reftypes_count, node);
+    }
+
+    if(!all_refs && elem->includeSubtypes)
+        UA_Array_delete(reftypes, reftypes_count, &UA_TYPES[UA_TYPES_NODEID]);
+}
+
+/* This assumes that result->targets has enough room for all currentCount elements */
+static void
+addBrowsePathTargets(UA_Server *server, UA_Session *session, UA_BrowsePathResult *result,
+                     const UA_QualifiedName *targetName, UA_NodeId *current, size_t currentCount) {
+    for(size_t i = 0; i < currentCount; i++) {
+        /* Get the node */
+        const UA_Node *node = UA_NodeStore_get(server->nodestore, &current[i]);
+        if(!node) {
+            UA_NodeId_deleteMembers(&current[i]);
             continue;
+        }
 
-        // test the browsename
-        if(elem->targetName.namespaceIndex != next->browseName.namespaceIndex ||
-           !UA_String_equal(&elem->targetName.name, &next->browseName.name)) {
+        /* Test whether the current node has the target name required in the
+         * previous path element */
+        if(targetName->namespaceIndex != node->browseName.namespaceIndex ||
+           !UA_String_equal(&targetName->name, &node->browseName.name)) {
+            UA_NodeId_deleteMembers(&current[i]);
             continue;
         }
 
-        if(pathindex + 1 < path->elementsSize) {
-            // recursion if the path is longer
-            retval = walkBrowsePath(server, session, next, path, pathindex + 1,
-                                    targets, targets_size, target_count);
-        } else {
-            // add the browsetarget
-            if(*target_count >= *targets_size) {
-                UA_BrowsePathTarget *newtargets;
-                newtargets = UA_realloc(targets, sizeof(UA_BrowsePathTarget) * (*targets_size) * 2);
-                if(!newtargets) {
-                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                    break;
-                }
-                *targets = newtargets;
-                *targets_size *= 2;
-            }
+        /* Move the nodeid to the target array */
+        UA_BrowsePathTarget_init(&result->targets[result->targetsSize]);
+        result->targets[result->targetsSize].targetId.nodeId = current[i];
+        result->targets[result->targetsSize].remainingPathIndex = UA_UINT32_MAX;
+        ++result->targetsSize;
+    }
+}
 
-            UA_BrowsePathTarget *res = *targets;
-            UA_ExpandedNodeId_init(&res[*target_count].targetId);
-            retval = UA_NodeId_copy(&next->nodeId, &res[*target_count].targetId.nodeId);
-            if(retval != UA_STATUSCODE_GOOD)
-                break;
-            res[*target_count].remainingPathIndex = UA_UINT32_MAX;
-            *target_count += 1;
+static void
+walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path,
+               UA_BrowsePathResult *result, size_t targetsSize,
+               UA_NodeId **current, size_t *currentSize, size_t *currentCount,
+               UA_NodeId **next, size_t *nextSize, size_t *nextCount) {
+    UA_assert(*currentCount == 1);
+    UA_assert(*nextCount == 0);
+
+    /* Points to the targetName of the _previous_ path element */
+    const UA_QualifiedName *targetName = NULL;
+
+    /* Iterate over path elements */
+    UA_assert(path->relativePath.elementsSize > 0);
+    for(UA_UInt32 i = 0; i < path->relativePath.elementsSize; ++i) {
+        walkBrowsePathElement(server, session, result, &targetsSize,
+                              &path->relativePath.elements[i], i, targetName,
+                              *current, *currentCount, next, nextSize, nextCount);
+
+        /* Clean members of current */
+        for(size_t j = 0; j < *currentCount; j++)
+            UA_NodeId_deleteMembers(&(*current)[j]);
+        *currentCount = 0;
+
+        /* When no targets are left or an error occurred. None of next's
+         * elements will be copied to result->targets */
+        if(nextCount == 0 || result->statusCode != UA_STATUSCODE_GOOD) {
+            UA_assert(*currentCount == 0);
+            UA_assert(*nextCount == 0);
+            return;
         }
+
+        /* Exchange current and next for the next depth */
+        size_t tSize = *currentSize; size_t tCount = *currentCount; UA_NodeId *tT = *current;
+        *currentSize = *nextSize; *currentCount = *nextCount; *current = *next;
+        *nextSize = tSize; *nextCount = tCount; *next = tT;
+
+        /* Store the target name of the previous path element */
+        targetName = &path->relativePath.elements[i].targetName;
     }
 
-    if(!all_refs && elem->includeSubtypes)
-        UA_Array_delete(reftypes, reftypes_count, &UA_TYPES[UA_TYPES_NODEID]);
-    return retval;
+    UA_assert(targetName != NULL);
+    UA_assert(*nextCount == 0);
+
+    /* After the last BrowsePathElement, move members from current to the
+     * result targets */
+
+    /* Realloc if more space is needed */
+    if(targetsSize < result->targetsSize + (*currentCount)) {
+        UA_BrowsePathTarget *newTargets =
+            (UA_BrowsePathTarget*)UA_realloc(result->targets, sizeof(UA_BrowsePathTarget) *
+                                             (result->targetsSize + (*currentCount)));
+        if(!newTargets) {
+            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+            for(size_t i = 0; i < *currentCount; ++i)
+                UA_NodeId_deleteMembers(&(*current)[i]);
+            *currentCount = 0;
+            return;
+        }
+        result->targets = newTargets;
+    }
+
+    /* Move the elements of current to the targets */
+    addBrowsePathTargets(server, session, result, targetName, *current, *currentCount);
+    *currentCount = 0;
 }
 
 static void
-translateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
-                              const UA_BrowsePath *path, UA_BrowsePathResult *result) {
+translateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
+                             const UA_BrowsePath *path, UA_BrowsePathResult *result) {
     if(path->relativePath.elementsSize <= 0) {
         result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }
         
-    //relativePath elements should not have an empty targetName
-    for(size_t i=0;i<path->relativePath.elementsSize;++i){
-        UA_QualifiedName *qname = &(path->relativePath.elements[i].targetName);
-        if(UA_QualifiedName_isNull(qname)){
+    /* RelativePath elements must not have an empty targetName */
+    for(size_t i = 0; i < path->relativePath.elementsSize; ++i) {
+        if(UA_QualifiedName_isNull(&path->relativePath.elements[i].targetName)) {
             result->statusCode = UA_STATUSCODE_BADBROWSENAMEINVALID;
             return;
         }
     }
 
-    size_t arraySize = 10;
-    result->targets = UA_malloc(sizeof(UA_BrowsePathTarget) * arraySize);
+    /* Allocate memory for the targets */
+    size_t targetsSize = 10; /* When to realloc; the member count is stored in
+                              * result->targetsSize */
+    result->targets = (UA_BrowsePathTarget*)UA_malloc(sizeof(UA_BrowsePathTarget) * targetsSize);
     if(!result->targets) {
         result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         return;
     }
-    result->targetsSize = 0;
-    const UA_Node *firstNode = UA_NodeStore_get(server->nodestore, &path->startingNode);
-    if(!firstNode) {
-        result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
+
+    /* Allocate memory for two temporary arrays. One with the results for the
+     * previous depth of the path. The other for the new results at the current
+     * depth. The two arrays alternate as we descend down the tree. */
+    size_t currentSize = 10; /* When to realloc */
+    size_t currentCount = 0; /* Current elements */
+    UA_NodeId *current = (UA_NodeId*)UA_malloc(sizeof(UA_NodeId) * currentSize);
+    if(!current) {
+        result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         UA_free(result->targets);
-        result->targets = NULL;
         return;
     }
+    size_t nextSize = 10; /* When to realloc */
+    size_t nextCount = 0; /* Current elements */
+    UA_NodeId *next = (UA_NodeId*)UA_malloc(sizeof(UA_NodeId) * nextSize);
+    if(!next) {
+        result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
+        UA_free(result->targets);
+        UA_free(current);
+        return;
+    }
+
+    /* Copy the starting node into current */
+    result->statusCode = UA_NodeId_copy(&path->startingNode, &current[0]);
+    if(result->statusCode != UA_STATUSCODE_GOOD) {
+        UA_free(result->targets);
+        UA_free(current);
+        UA_free(next);
+        return;
+    }
+    currentCount = 1;
+
+    /* Walk the path elements */
+    walkBrowsePath(server, session, path, result, targetsSize,
+                   &current, &currentSize, &currentCount,
+                   &next, &nextSize, &nextCount);
+
+    UA_assert(currentCount == 0);
+    UA_assert(nextCount == 0);
 
-    result->statusCode = walkBrowsePath(server, session, firstNode, &path->relativePath, 0,
-                                        &result->targets, &arraySize, &result->targetsSize);
+    /* No results => BadNoMatch status code */
     if(result->targetsSize == 0 && result->statusCode == UA_STATUSCODE_GOOD)
         result->statusCode = UA_STATUSCODE_BADNOMATCH;
 
+    /* Clean up the temporary arrays and the targets */
+    UA_free(current);
+    UA_free(next);
     if(result->statusCode != UA_STATUSCODE_GOOD) {
-        UA_Array_delete(result->targets, result->targetsSize, &UA_TYPES[UA_TYPES_BROWSEPATHTARGET]);
+        for(size_t i = 0; i < result->targetsSize; ++i)
+            UA_BrowsePathTarget_deleteMembers(&result->targets[i]);
+        UA_free(result->targets);
         result->targets = NULL;
         result->targetsSize = 0;
     }
@@ -544,7 +710,7 @@ UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
     UA_BrowsePathResult result;
     UA_BrowsePathResult_init(&result);
     UA_RCU_LOCK();
-    translateBrowsePathsToNodeIds(server, &adminSession, browsePath, &result);
+    translateBrowsePathToNodeIds(server, &adminSession, browsePath, &result);
     UA_RCU_UNLOCK();
     return result;
 }
@@ -590,16 +756,18 @@ void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *sessio
                                            indices, (UA_UInt32)indexSize, response->results,
                                            response->diagnosticInfos);
     }
-#endif
-
     response->resultsSize = size;
     for(size_t i = 0; i < size; ++i) {
-#ifdef UA_ENABLE_EXTERNAL_NAMESPACES
         if(!isExternal[i])
-#endif
-            translateBrowsePathsToNodeIds(server, session, &request->browsePaths[i],
-                                          &response->results[i]);
+            translateBrowsePathToNodeIds(server, session, &request->browsePaths[i],
+                                         &response->results[i]);
     }
+#else
+    response->resultsSize = size;
+    for(size_t i = 0; i < size; ++i)
+        translateBrowsePathToNodeIds(server, session, &request->browsePaths[i],
+                                     &response->results[i]);
+#endif
 }
 
 void Service_RegisterNodes(UA_Server *server, UA_Session *session, const UA_RegisterNodesRequest *request,

+ 25 - 37
tests/check_services_view.c

@@ -70,8 +70,6 @@ START_TEST(Service_Browse_WithBrowseName)
     }
 END_TEST
 
-#define BROWSE_PATHS_SIZE 3
-
 START_TEST(Service_TranslateBrowsePathsToNodeIds)
     {
         UA_Client *client = UA_Client_new(UA_ClientConfig_standard);
@@ -84,50 +82,40 @@ START_TEST(Service_TranslateBrowsePathsToNodeIds)
         // Equals the following node IDs:
         // /85/2253/2256/2259
 
+#define BROWSE_PATHS_SIZE 3
         char *paths[BROWSE_PATHS_SIZE] = {"Server", "ServerStatus", "State"};
-        UA_UInt32 ids[BROWSE_PATHS_SIZE] = {2253, 2256, 2259};
-
-
-        UA_BrowsePath *browsePaths = UA_Array_new(BROWSE_PATHS_SIZE, &UA_TYPES[UA_TYPES_BROWSEPATH]);
-
-        for (unsigned int i = 0; i < BROWSE_PATHS_SIZE; i++) {
-            UA_BrowsePath_init(&browsePaths[i]);
-            browsePaths[i].startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
-            browsePaths[i].relativePath.elementsSize = i + 1;
-            browsePaths[i].relativePath.elements = UA_Array_new(i + 1, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
-            for (unsigned int j = 0; j <= i; j++) {
-                UA_RelativePathElement_init(&browsePaths[i].relativePath.elements[j]);
-                browsePaths[i].relativePath.elements[j].isInverse = UA_TRUE;
-
-                browsePaths[i].relativePath.elements[j].targetName = UA_QUALIFIEDNAME_ALLOC(0, paths[j]);
-            }
+        UA_UInt32 ids[BROWSE_PATHS_SIZE] = {UA_NS0ID_ORGANIZES, UA_NS0ID_HASCOMPONENT, UA_NS0ID_HASCOMPONENT};
+        UA_BrowsePath browsePath;
+        UA_BrowsePath_init(&browsePath);
+        browsePath.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+        browsePath.relativePath.elements = UA_Array_new(BROWSE_PATHS_SIZE, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
+        browsePath.relativePath.elementsSize = BROWSE_PATHS_SIZE;
+
+        for(size_t i = 0; i < BROWSE_PATHS_SIZE; i++) {
+            UA_RelativePathElement *elem = &browsePath.relativePath.elements[i];
+            elem->referenceTypeId = UA_NODEID_NUMERIC(0, ids[i]);
+            elem->targetName = UA_QUALIFIEDNAME_ALLOC(0, paths[i]);
         }
 
         UA_TranslateBrowsePathsToNodeIdsRequest request;
         UA_TranslateBrowsePathsToNodeIdsRequest_init(&request);
-        request.browsePaths = browsePaths;
-        request.browsePathsSize = BROWSE_PATHS_SIZE;
-
-        {
-
-            UA_TranslateBrowsePathsToNodeIdsResponse response = UA_Client_Service_translateBrowsePathsToNodeIds(client, request);
+        request.browsePaths = &browsePath;
+        request.browsePathsSize = 1;
 
-            ck_assert_int_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
-            ck_assert_int_eq(response.resultsSize, BROWSE_PATHS_SIZE);
+        UA_TranslateBrowsePathsToNodeIdsResponse response = UA_Client_Service_translateBrowsePathsToNodeIds(client, request);
 
-            for (int i = 0; i < BROWSE_PATHS_SIZE; i++) {
-                ck_assert_int_eq(response.results[i].targetsSize, 1);
-                ck_assert_int_eq(response.results[i].targets[0].targetId.nodeId.identifierType, UA_NODEIDTYPE_NUMERIC);
-                ck_assert_int_eq(response.results[i].targets[0].targetId.nodeId.identifier.numeric, ids[i]);
+        ck_assert_int_eq(response.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
+        ck_assert_int_eq(response.resultsSize, 1);
 
-            }
+        ck_assert_int_eq(response.results[0].targetsSize, 1);
+        ck_assert_int_eq(response.results[0].targets[0].targetId.nodeId.identifierType, UA_NODEIDTYPE_NUMERIC);
+        ck_assert_int_eq(response.results[0].targets[0].targetId.nodeId.identifier.numeric, UA_NS0ID_SERVER_SERVERSTATUS_STATE);
 
-            UA_TranslateBrowsePathsToNodeIdsRequest_deleteMembers(&request);
-            UA_TranslateBrowsePathsToNodeIdsResponse_deleteMembers(&response);
-            retVal = UA_Client_disconnect(client);
-            ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
-            UA_Client_delete(client);
-        }
+        UA_BrowsePath_deleteMembers(&browsePath);
+        UA_TranslateBrowsePathsToNodeIdsResponse_deleteMembers(&response);
+        retVal = UA_Client_disconnect(client);
+        ck_assert_int_eq(retVal, UA_STATUSCODE_GOOD);
+        UA_Client_delete(client);
     }
 END_TEST