ua_services_view.c 12 KB

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