ua_services_view.c 12 KB

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