ua_services_view.c 13 KB

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