open62541_nodestore_view.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * service_view_implementation.c
  3. *
  4. * Created on: Oct 27, 2014
  5. * Author: opcua
  6. */
  7. #include "open62541_nodestore.h"
  8. #include "../ua_services.h"
  9. #include "ua_namespace_0.h"
  10. #include "ua_util.h"
  11. /* Releases the current node, even if it was supplied as an argument. */
  12. static UA_StatusCode fillReferenceDescription(open62541NodeStore *ns, const UA_Node *currentNode, UA_ReferenceNode *reference,
  13. UA_UInt32 resultMask, UA_ReferenceDescription *referenceDescription) {
  14. UA_ReferenceDescription_init(referenceDescription);
  15. if(!currentNode && resultMask != 0) {
  16. if(open62541NodeStore_get(ns, &reference->targetId.nodeId, &currentNode) != UA_STATUSCODE_GOOD)
  17. return UA_STATUSCODE_BADINTERNALERROR;
  18. }
  19. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  20. retval |= UA_NodeId_copy(&currentNode->nodeId, &referenceDescription->nodeId.nodeId);
  21. //TODO: ExpandedNodeId is mocked up
  22. referenceDescription->nodeId.serverIndex = 0;
  23. referenceDescription->nodeId.namespaceUri.length = -1;
  24. if(resultMask & UA_BROWSERESULTMASK_REFERENCETYPEID)
  25. retval |= UA_NodeId_copy(&reference->referenceTypeId, &referenceDescription->referenceTypeId);
  26. if(resultMask & UA_BROWSERESULTMASK_ISFORWARD)
  27. referenceDescription->isForward = !reference->isInverse;
  28. if(resultMask & UA_BROWSERESULTMASK_NODECLASS)
  29. retval |= UA_NodeClass_copy(&currentNode->nodeClass, &referenceDescription->nodeClass);
  30. if(resultMask & UA_BROWSERESULTMASK_BROWSENAME)
  31. retval |= UA_QualifiedName_copy(&currentNode->browseName, &referenceDescription->browseName);
  32. if(resultMask & UA_BROWSERESULTMASK_DISPLAYNAME)
  33. retval |= UA_LocalizedText_copy(&currentNode->displayName, &referenceDescription->displayName);
  34. if(resultMask & UA_BROWSERESULTMASK_TYPEDEFINITION && currentNode->nodeClass != UA_NODECLASS_OBJECT &&
  35. currentNode->nodeClass != UA_NODECLASS_VARIABLE) {
  36. for(UA_Int32 i = 0;i < currentNode->referencesSize;i++) {
  37. UA_ReferenceNode *ref = &currentNode->references[i];
  38. if(ref->referenceTypeId.identifier.numeric == 40 /* hastypedefinition */) {
  39. retval |= UA_ExpandedNodeId_copy(&ref->targetId, &referenceDescription->typeDefinition);
  40. break;
  41. }
  42. }
  43. }
  44. if(currentNode)
  45. open62541NodeStore_release(currentNode);
  46. if(retval)
  47. UA_ReferenceDescription_deleteMembers(referenceDescription);
  48. return retval;
  49. }
  50. /* Tests if the node is relevant an shall be returned. If the targetNode needs
  51. to be retrieved from the nodestore to determine this, the targetNode is
  52. returned if the node is relevant. */
  53. static UA_Boolean isRelevantTargetNode(open62541NodeStore *ns, const UA_BrowseDescription *browseDescription, UA_Boolean returnAll,
  54. UA_ReferenceNode *reference, const UA_Node **currentNode,
  55. UA_NodeId *relevantRefTypes, UA_UInt32 relevantRefTypesCount) {
  56. // 1) Test Browse direction
  57. if(reference->isInverse == UA_TRUE && browseDescription->browseDirection == UA_BROWSEDIRECTION_FORWARD)
  58. return UA_FALSE;
  59. else if(reference->isInverse == UA_FALSE && browseDescription->browseDirection == UA_BROWSEDIRECTION_INVERSE)
  60. return UA_FALSE;
  61. // 2) Test if the reference type is relevant
  62. UA_Boolean isRelevant = returnAll;
  63. if(!isRelevant) {
  64. for(UA_UInt32 i = 0;i < relevantRefTypesCount;i++) {
  65. if(UA_NodeId_equal(&reference->referenceTypeId, &relevantRefTypes[i]))
  66. isRelevant = UA_TRUE;
  67. }
  68. if(!isRelevant)
  69. return UA_FALSE;
  70. }
  71. // 3) Test if the target nodeClass is relevant
  72. if(browseDescription->nodeClassMask == 0)
  73. return UA_TRUE; // the node is relevant, but we didn't need to get it from the nodestore yet.
  74. if(open62541NodeStore_get(ns, &reference->targetId.nodeId, currentNode) != UA_STATUSCODE_GOOD)
  75. return UA_FALSE;
  76. if(((*currentNode)->nodeClass & browseDescription->nodeClassMask) == 0) {
  77. open62541NodeStore_release(*currentNode);
  78. return UA_FALSE;
  79. }
  80. // the node is relevant and was retrieved from the nodestore, do not release it.
  81. return UA_TRUE;
  82. }
  83. /* We do not search across namespaces so far. The id of the root-referencetype
  84. is returned in the array also. */
  85. static UA_StatusCode findRelevantReferenceTypes(open62541NodeStore *ns, const UA_NodeId *rootReferenceType,
  86. UA_NodeId **referenceTypes, UA_UInt32 *referenceTypesSize) {
  87. /* The references form a tree. We walk the tree by adding new nodes to the end of the array. */
  88. UA_UInt32 currentIndex = 0;
  89. UA_UInt32 currentLastIndex = 0;
  90. UA_UInt32 currentArraySize = 20; // should be more than enough. if not, increase the array size.
  91. UA_NodeId *typeArray = UA_alloc(sizeof(UA_NodeId) * currentArraySize);
  92. if(!typeArray)
  93. return UA_STATUSCODE_BADOUTOFMEMORY;
  94. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  95. retval |= UA_NodeId_copy(rootReferenceType, &typeArray[0]);
  96. if(retval) {
  97. UA_free(typeArray);
  98. return UA_STATUSCODE_BADOUTOFMEMORY;
  99. }
  100. const UA_ReferenceTypeNode *node;
  101. do {
  102. retval |= open62541NodeStore_get(ns, &typeArray[currentIndex], (const UA_Node **)&node);
  103. if(retval)
  104. break;
  105. if(node->nodeClass != UA_NODECLASS_REFERENCETYPE) // subtypes of referencestypes are always referencestypes?
  106. continue;
  107. // Find subtypes of the current referencetype
  108. for(UA_Int32 i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; i++) {
  109. if(node->references[i].referenceTypeId.identifier.numeric != 45 /* HasSubtype */ ||
  110. node->references[i].isInverse == UA_TRUE)
  111. continue;
  112. if(currentLastIndex + 1 >= currentArraySize) {
  113. // we need to resize the array
  114. UA_NodeId *newArray = UA_alloc(sizeof(UA_NodeId) * currentArraySize * 2);
  115. if(newArray) {
  116. memcpy(newArray, typeArray, sizeof(UA_NodeId) * currentArraySize);
  117. currentArraySize *= 2;
  118. UA_free(typeArray);
  119. typeArray = newArray;
  120. } else {
  121. retval = UA_STATUSCODE_BADOUTOFMEMORY;
  122. break;
  123. }
  124. }
  125. // ok, we have space to add the new referencetype.
  126. retval |= UA_NodeId_copy(&node->references[i].targetId.nodeId, &typeArray[++currentLastIndex]);
  127. if(retval)
  128. currentLastIndex--; // undo if we need to delete the typeArray
  129. }
  130. open62541NodeStore_release((UA_Node*)node);
  131. } while(++currentIndex <= currentLastIndex && retval == UA_STATUSCODE_GOOD);
  132. if(retval)
  133. UA_Array_delete(typeArray, currentLastIndex, &UA_TYPES[UA_NODEID]);
  134. else {
  135. *referenceTypes = typeArray;
  136. *referenceTypesSize = currentLastIndex + 1;
  137. }
  138. return retval;
  139. }
  140. /* Results for a single browsedescription. */
  141. static void getBrowseResult(open62541NodeStore *ns, const UA_BrowseDescription *browseDescription,
  142. UA_UInt32 maxReferences, UA_BrowseResult *browseResult) {
  143. UA_UInt32 relevantReferenceTypesSize = 0;
  144. UA_NodeId *relevantReferenceTypes = NULL;
  145. // if the referencetype is null, all referencetypes are returned
  146. UA_Boolean returnAll = UA_NodeId_isNull(&browseDescription->referenceTypeId);
  147. if(!returnAll) {
  148. if(browseDescription->includeSubtypes) {
  149. browseResult->statusCode = findRelevantReferenceTypes(ns, &browseDescription->referenceTypeId,
  150. &relevantReferenceTypes, &relevantReferenceTypesSize);
  151. if(browseResult->statusCode != UA_STATUSCODE_GOOD)
  152. return;
  153. } else {
  154. relevantReferenceTypes = (UA_NodeId*)&browseDescription->referenceTypeId; // is const
  155. relevantReferenceTypesSize = 1;
  156. }
  157. }
  158. const UA_Node *parentNode;
  159. if(open62541NodeStore_get(ns, &browseDescription->nodeId, &parentNode) != UA_STATUSCODE_GOOD) {
  160. browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
  161. if(!returnAll && browseDescription->includeSubtypes)
  162. UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
  163. return;
  164. }
  165. // 0 => unlimited references
  166. if(maxReferences == 0 || maxReferences > UA_INT32_MAX || (UA_Int32)maxReferences > parentNode->referencesSize)
  167. maxReferences = parentNode->referencesSize;
  168. /* We allocate an array that is probably too big. But since most systems
  169. have more than enough memory, this has zero impact on speed and
  170. performance. Call Array_delete with the actual content size! */
  171. browseResult->references = UA_alloc(sizeof(UA_ReferenceDescription) * maxReferences);
  172. if(!browseResult->references) {
  173. browseResult->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
  174. } else {
  175. UA_UInt32 currentRefs = 0;
  176. for(UA_Int32 i = 0;i < parentNode->referencesSize && currentRefs < maxReferences;i++) {
  177. // 1) Is the node relevant? This might retrieve the node from the nodestore
  178. const UA_Node *currentNode = NULL;
  179. if(!isRelevantTargetNode(ns, browseDescription, returnAll, &parentNode->references[i], &currentNode,
  180. relevantReferenceTypes, relevantReferenceTypesSize))
  181. continue;
  182. // 2) Fill the reference description. This also releases the current node.
  183. if(fillReferenceDescription(ns, currentNode, &parentNode->references[i], browseDescription->resultMask,
  184. &browseResult->references[currentRefs]) != UA_STATUSCODE_GOOD) {
  185. UA_Array_delete(browseResult->references, currentRefs, &UA_TYPES[UA_REFERENCEDESCRIPTION]);
  186. currentRefs = 0;
  187. browseResult->references = NULL;
  188. browseResult->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
  189. break;
  190. }
  191. currentRefs++;
  192. }
  193. browseResult->referencesSize = currentRefs;
  194. }
  195. open62541NodeStore_release(parentNode);
  196. if(!returnAll && browseDescription->includeSubtypes)
  197. UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesSize, &UA_TYPES[UA_NODEID]);
  198. }
  199. UA_Int32 open62541NodeStore_BrowseNodes(const UA_RequestHeader *requestHeader,UA_BrowseDescription *browseDescriptions,UA_UInt32 *indices,UA_UInt32 indicesSize, UA_UInt32 requestedMaxReferencesPerNode,
  200. UA_BrowseResult *browseResults,
  201. UA_DiagnosticInfo *diagnosticInfos){
  202. for(UA_UInt32 i = 0; i < indicesSize; i++){
  203. getBrowseResult(open62541NodeStore_getNodeStore(),&browseDescriptions[indices[i]],requestedMaxReferencesPerNode, &browseResults[indices[i]]);
  204. }
  205. return UA_STATUSCODE_GOOD;
  206. }