ua_services_view.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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. UA_Int32 Service_Browse_getReferenceDescription(UA_NodeStore *ns, UA_ReferenceNode *reference,
  7. UA_UInt32 nodeClassMask, UA_UInt32 resultMask,
  8. UA_ReferenceDescription *referenceDescription) {
  9. const UA_Node *foundNode;
  10. if(UA_NodeStore_get(ns, &reference->targetId.nodeId, &foundNode) != UA_SUCCESS)
  11. return UA_ERROR;
  12. UA_NodeId_copy(&foundNode->nodeId, &referenceDescription->nodeId.nodeId);
  13. //TODO ExpandedNodeId is a mockup
  14. referenceDescription->nodeId.serverIndex = 0;
  15. referenceDescription->nodeId.namespaceUri.length = -1;
  16. UA_UInt32 mask = 0;
  17. for(mask = 0x01;mask <= 0x40;mask *= 2) {
  18. switch(mask & (resultMask)) {
  19. case UA_BROWSERESULTMASK_REFERENCETYPEID:
  20. UA_NodeId_copy(&reference->referenceTypeId, &referenceDescription->referenceTypeId);
  21. break;
  22. case UA_BROWSERESULTMASK_ISFORWARD:
  23. referenceDescription->isForward = !reference->isInverse;
  24. break;
  25. case UA_BROWSERESULTMASK_NODECLASS:
  26. UA_NodeClass_copy(&foundNode->nodeClass, &referenceDescription->nodeClass);
  27. break;
  28. case UA_BROWSERESULTMASK_BROWSENAME:
  29. UA_QualifiedName_copy(&foundNode->browseName, &referenceDescription->browseName);
  30. break;
  31. case UA_BROWSERESULTMASK_DISPLAYNAME:
  32. UA_LocalizedText_copy(&foundNode->displayName, &referenceDescription->displayName);
  33. break;
  34. case UA_BROWSERESULTMASK_TYPEDEFINITION:
  35. if(foundNode->nodeClass != UA_NODECLASS_OBJECT &&
  36. foundNode->nodeClass != UA_NODECLASS_VARIABLE)
  37. break;
  38. for(UA_Int32 i = 0;i < foundNode->referencesSize;i++) {
  39. UA_ReferenceNode *ref = &foundNode->references[i];
  40. if(ref->referenceTypeId.identifier.numeric == 40 /* hastypedefinition */) {
  41. UA_ExpandedNodeId_copy(&ref->targetId, &referenceDescription->typeDefinition);
  42. break;
  43. }
  44. }
  45. break;
  46. }
  47. }
  48. UA_NodeStore_releaseManagedNode(foundNode);
  49. return UA_SUCCESS;
  50. }
  51. /* singly-linked list */
  52. struct SubRefTypeId {
  53. UA_NodeId id;
  54. SLIST_ENTRY(SubRefTypeId) next;
  55. };
  56. SLIST_HEAD(SubRefTypeIdList, SubRefTypeId);
  57. static UA_UInt32 walkReferenceTree(UA_NodeStore *ns, const UA_ReferenceTypeNode *current,
  58. struct SubRefTypeIdList *list) {
  59. // insert the current referencetype
  60. struct SubRefTypeId *element = UA_alloc(sizeof(struct SubRefTypeId));
  61. element->id = current->nodeId;
  62. SLIST_INSERT_HEAD(list, element, next);
  63. UA_UInt32 count = 1; // the current element
  64. // walk the tree
  65. for(UA_Int32 i = 0;i < current->referencesSize;i++) {
  66. if(current->references[i].referenceTypeId.identifier.numeric == 45 /* HasSubtype */ &&
  67. current->references[i].isInverse == UA_FALSE) {
  68. const UA_Node *node;
  69. if(UA_NodeStore_get(ns, &current->references[i].targetId.nodeId, &node) == UA_SUCCESS
  70. && node->nodeClass == UA_NODECLASS_REFERENCETYPE) {
  71. count += walkReferenceTree(ns, (UA_ReferenceTypeNode *)node, list);
  72. UA_NodeStore_releaseManagedNode(node);
  73. }
  74. }
  75. }
  76. return count;
  77. }
  78. /* We do not search across namespaces so far. The id of the father-referencetype is returned in the array also. */
  79. static UA_Int32 findSubReferenceTypes(UA_NodeStore *ns, UA_NodeId *rootReferenceType,
  80. UA_NodeId **ids, UA_UInt32 *idcount) {
  81. struct SubRefTypeIdList list;
  82. UA_UInt32 count;
  83. SLIST_INIT(&list);
  84. // walk the tree
  85. const UA_ReferenceTypeNode *root;
  86. if(UA_NodeStore_get(ns, rootReferenceType, (const UA_Node **)&root) != UA_SUCCESS ||
  87. root->nodeClass != UA_NODECLASS_REFERENCETYPE)
  88. return UA_ERROR;
  89. count = walkReferenceTree(ns, root, &list);
  90. UA_NodeStore_releaseManagedNode((const UA_Node *)root);
  91. // copy results into an array
  92. *ids = UA_alloc(sizeof(UA_NodeId)*count);
  93. for(UA_UInt32 i = 0;i < count;i++) {
  94. struct SubRefTypeId *element = SLIST_FIRST(&list);
  95. UA_NodeId_copy(&element->id, &(*ids)[i]);
  96. SLIST_REMOVE_HEAD(&list, next);
  97. UA_free(element);
  98. }
  99. *idcount = count;
  100. return UA_SUCCESS;
  101. }
  102. /* is this a relevant reference? */
  103. static INLINE UA_Boolean Service_Browse_returnReference(UA_BrowseDescription *browseDescription,
  104. UA_ReferenceNode *reference,
  105. UA_NodeId *relevantRefTypes,
  106. UA_UInt32 relevantRefTypesCount) {
  107. if(reference->isInverse == UA_TRUE &&
  108. browseDescription->browseDirection == UA_BROWSEDIRECTION_FORWARD)
  109. return UA_FALSE;
  110. else if(reference->isInverse == UA_FALSE &&
  111. browseDescription->browseDirection == UA_BROWSEDIRECTION_INVERSE)
  112. return UA_FALSE;
  113. for(UA_UInt32 i = 0;i < relevantRefTypesCount;i++) {
  114. if(UA_NodeId_equal(&browseDescription->referenceTypeId, &relevantRefTypes[i]) == UA_EQUAL)
  115. return UA_TRUE;
  116. }
  117. return UA_FALSE;
  118. }
  119. /* Return results to a single browsedescription. */
  120. static void Service_Browse_getBrowseResult(UA_NodeStore *ns,
  121. UA_BrowseDescription *browseDescription,
  122. UA_UInt32 maxReferences,
  123. UA_BrowseResult *browseResult) {
  124. const UA_Node *node;
  125. UA_NodeId *relevantReferenceTypes = UA_NULL;
  126. UA_UInt32 relevantReferenceTypesCount = 0;
  127. if(UA_NodeStore_get(ns, &browseDescription->nodeId, &node) != UA_SUCCESS) {
  128. browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
  129. return;
  130. }
  131. // 0 => unlimited references
  132. if(maxReferences == 0)
  133. maxReferences = node->referencesSize;
  134. // discover the relevant subtypes
  135. if(!browseDescription->includeSubtypes ||
  136. findSubReferenceTypes(ns, &browseDescription->referenceTypeId, &relevantReferenceTypes,
  137. &relevantReferenceTypesCount) != UA_SUCCESS) {
  138. if(!(relevantReferenceTypes = UA_alloc(sizeof(UA_NodeId)))) {
  139. return;
  140. }
  141. UA_NodeId_copy(&browseDescription->referenceTypeId, relevantReferenceTypes);
  142. relevantReferenceTypesCount = 1;
  143. }
  144. /* We do not use a linked list but traverse the nodes references list twice
  145. * (once for counting, once for generating the referencedescriptions). That
  146. * is much faster than using a linked list, since the references are
  147. * allocated in a continuous blob and RAM access is predictible/does not
  148. * miss cache lines so often. TODO: measure with some huge objects! */
  149. UA_UInt32 refs = 0;
  150. for(UA_Int32 i = 0;i < node->referencesSize && refs <= maxReferences;i++) {
  151. if(Service_Browse_returnReference(browseDescription, &node->references[i], relevantReferenceTypes,
  152. relevantReferenceTypesCount))
  153. refs++;
  154. }
  155. // can we return all relevant references at once?
  156. UA_Boolean finished = UA_TRUE;
  157. if(refs > maxReferences) {
  158. refs--;
  159. finished = UA_FALSE;
  160. }
  161. browseResult->referencesSize = refs;
  162. UA_Array_new((void **)&browseResult->references, refs, &UA_[UA_REFERENCEDESCRIPTION]);
  163. for(UA_UInt32 i = 0, j = 0;j < refs;i++) {
  164. if(!Service_Browse_returnReference(browseDescription, &node->references[i], relevantReferenceTypes,
  165. relevantReferenceTypesCount))
  166. continue;
  167. if(Service_Browse_getReferenceDescription(ns, &node->references[i], browseDescription->nodeClassMask,
  168. browseDescription->resultMask,
  169. &browseResult->references[j]) != UA_SUCCESS)
  170. browseResult->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
  171. j++;
  172. }
  173. if(!finished) {
  174. // Todo. Set the Statuscode and the continuation point.
  175. }
  176. UA_NodeStore_releaseManagedNode(node);
  177. UA_Array_delete(relevantReferenceTypes, relevantReferenceTypesCount, &UA_[UA_NODEID]);
  178. }
  179. void Service_Browse(UA_Server *server, UA_Session *session,
  180. const UA_BrowseRequest *request, UA_BrowseResponse *response) {
  181. UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
  182. if(request->nodesToBrowseSize <= 0) {
  183. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  184. return;
  185. }
  186. if(UA_Array_new((void **)&(response->results), request->nodesToBrowseSize, &UA_[UA_BROWSERESULT])
  187. != UA_SUCCESS) {
  188. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  189. return;
  190. }
  191. response->resultsSize = request->nodesToBrowseSize;
  192. for(UA_Int32 i = 0;i < request->nodesToBrowseSize;i++)
  193. Service_Browse_getBrowseResult(server->nodestore, &request->nodesToBrowse[i],
  194. request->requestedMaxReferencesPerNode, &response->results[i]);
  195. }
  196. void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
  197. const UA_TranslateBrowsePathsToNodeIdsRequest *request,
  198. UA_TranslateBrowsePathsToNodeIdsResponse *response) {
  199. UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
  200. if(request->browsePathsSize <= 0) {
  201. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  202. return;
  203. }
  204. // Allocate space for a correct answer
  205. response->resultsSize = request->browsePathsSize;
  206. // _init of the elements is done in Array_new
  207. if(UA_Array_new((void **)&response->results, request->browsePathsSize, &UA_[UA_BROWSEPATHRESULT])
  208. != UA_SUCCESS) {
  209. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  210. return;
  211. }
  212. for(UA_Int32 i = 0;i < request->browsePathsSize;i++)
  213. response->results[i].statusCode = UA_STATUSCODE_BADNOMATCH; //FIXME: implement
  214. }