ua_services_view.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  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 * getRelevantTargetNode(UA_NodeStore *ns, const UA_BrowseDescription *browseDescription,
  43. UA_Boolean returnAll, UA_ReferenceNode *reference,
  44. UA_NodeId *relevantRefTypes, size_t relevantRefTypesCount) {
  45. if(reference->isInverse == UA_TRUE &&
  46. browseDescription->browseDirection == UA_BROWSEDIRECTION_FORWARD)
  47. return UA_NULL;
  48. else if(reference->isInverse == UA_FALSE &&
  49. browseDescription->browseDirection == UA_BROWSEDIRECTION_INVERSE)
  50. return UA_NULL;
  51. UA_Boolean isRelevant = returnAll;
  52. if(!isRelevant) {
  53. for(size_t i = 0;i < relevantRefTypesCount;i++) {
  54. if(UA_NodeId_equal(&reference->referenceTypeId, &relevantRefTypes[i]))
  55. isRelevant = UA_TRUE;
  56. }
  57. if(!isRelevant)
  58. return UA_NULL;
  59. }
  60. const UA_Node *node = UA_NodeStore_get(ns, &reference->targetId.nodeId);
  61. if(node && browseDescription->nodeClassMask != 0 && (node->nodeClass & browseDescription->nodeClassMask) == 0) {
  62. UA_NodeStore_release(node);
  63. node = UA_NULL;
  64. }
  65. return node;
  66. }
  67. /* Find all referencetypes that are subtypes (of subtypes) of the given root referencetype */
  68. static UA_StatusCode findReferenceTypeSubTypes(UA_NodeStore *ns, const UA_NodeId *rootReferenceType,
  69. UA_NodeId **referenceTypes, size_t *referenceTypesSize) {
  70. /* The references form a tree. We walk the tree by adding new nodes to the end of the array. */
  71. size_t index = 0;
  72. size_t lastIndex = 0;
  73. size_t arraySize = 20; // should be more than enough. if not, increase the array size.
  74. UA_NodeId *typeArray = UA_malloc(sizeof(UA_NodeId) * arraySize);
  75. if(!typeArray)
  76. return UA_STATUSCODE_BADOUTOFMEMORY;
  77. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  78. retval |= UA_NodeId_copy(rootReferenceType, &typeArray[0]);
  79. if(retval) {
  80. UA_free(typeArray);
  81. return UA_STATUSCODE_BADOUTOFMEMORY;
  82. }
  83. /**
  84. * We find all subtypes by a single iteration over the array. We start with an array with a
  85. * single root nodeid at the beginning. When we find relevant references, we add the nodeids to
  86. * the back of the array and increase the size. Since the hierarchy is not cyclic, we can safely
  87. * progress in the array to process the newly found referencetype nodeids (emulated recursion).
  88. */
  89. do {
  90. // 1) Get the node
  91. const UA_ReferenceTypeNode *node =
  92. (const UA_ReferenceTypeNode *)UA_NodeStore_get(ns, &typeArray[index]);
  93. if(!node)
  94. break;
  95. // 2) Check that we have the right type of node
  96. if(node->nodeClass != UA_NODECLASS_REFERENCETYPE)
  97. continue;
  98. // 3) Find references to subtypes
  99. for(UA_Int32 i = 0; i < node->referencesSize && retval == UA_STATUSCODE_GOOD; i++) {
  100. // Is this a subtype reference?
  101. if(node->references[i].referenceTypeId.identifier.numeric != 45 /* HasSubtype */ ||
  102. node->references[i].isInverse == UA_TRUE)
  103. continue;
  104. // Do we have enough space in the array?
  105. if(lastIndex + 1 >= arraySize) {
  106. UA_NodeId *newArray = UA_malloc(sizeof(UA_NodeId) * arraySize * 2);
  107. if(!newArray) {
  108. retval = UA_STATUSCODE_BADOUTOFMEMORY;
  109. break;
  110. }
  111. UA_memcpy(newArray, typeArray, sizeof(UA_NodeId) * arraySize);
  112. arraySize *= 2;
  113. UA_free(typeArray);
  114. typeArray = newArray;
  115. }
  116. // Copy the node
  117. retval |= UA_NodeId_copy(&node->references[i].targetId.nodeId, &typeArray[++lastIndex]);
  118. if(retval)
  119. lastIndex--; // undo if we need to delete the typeArray
  120. }
  121. UA_NodeStore_release((const UA_Node*)node);
  122. } while(++index <= lastIndex && retval == UA_STATUSCODE_GOOD);
  123. if(retval) {
  124. UA_Array_delete(typeArray, &UA_TYPES[UA_TYPES_NODEID], lastIndex);
  125. return retval;
  126. }
  127. *referenceTypes = typeArray;
  128. *referenceTypesSize = lastIndex + 1;
  129. return UA_STATUSCODE_GOOD;
  130. }
  131. /* Results for a single browsedescription. */
  132. static void getBrowseResult(UA_NodeStore *ns, const UA_BrowseDescription *browseDescription,
  133. UA_UInt32 maxReferences, UA_BrowseResult *browseResult) {
  134. size_t relevantReferenceTypesSize = 0;
  135. UA_NodeId *relevantReferenceTypes = UA_NULL;
  136. // if the referencetype is null, all referencetypes are returned
  137. UA_Boolean returnAll = UA_NodeId_isNull(&browseDescription->referenceTypeId);
  138. if(!returnAll) {
  139. if(browseDescription->includeSubtypes) {
  140. browseResult->statusCode =
  141. findReferenceTypeSubTypes(ns, &browseDescription->referenceTypeId, &relevantReferenceTypes,
  142. &relevantReferenceTypesSize);
  143. if(browseResult->statusCode != UA_STATUSCODE_GOOD)
  144. return;
  145. } else {
  146. relevantReferenceTypes = UA_NodeId_new();
  147. UA_NodeId_copy(&browseDescription->referenceTypeId, relevantReferenceTypes);
  148. relevantReferenceTypesSize = 1;
  149. }
  150. }
  151. const UA_Node *parentNode = UA_NodeStore_get(ns, &browseDescription->nodeId);
  152. if(!parentNode) {
  153. browseResult->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
  154. if(!returnAll)
  155. UA_Array_delete(relevantReferenceTypes, &UA_TYPES[UA_TYPES_NODEID], relevantReferenceTypesSize);
  156. return;
  157. }
  158. if(parentNode->referencesSize <= 0) {
  159. UA_NodeStore_release(parentNode);
  160. browseResult->referencesSize = 0;
  161. if(!returnAll)
  162. UA_Array_delete(relevantReferenceTypes, &UA_TYPES[UA_TYPES_NODEID], relevantReferenceTypesSize);
  163. return;
  164. }
  165. maxReferences = parentNode->referencesSize; // 0 => unlimited references
  166. /* We allocate an array that is probably too big. But since most systems
  167. have more than enough memory, this has zero impact on speed and
  168. performance. Call Array_delete with the actual content size! */
  169. browseResult->references = UA_malloc(sizeof(UA_ReferenceDescription) * maxReferences);
  170. if(!browseResult->references) {
  171. browseResult->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
  172. } else {
  173. size_t currentRefs = 0;
  174. for(UA_Int32 i = 0;i < parentNode->referencesSize && currentRefs < maxReferences;i++) {
  175. // 1) Is the node relevant? If yes, the node is retrieved from the nodestore.
  176. const UA_Node *currentNode =
  177. getRelevantTargetNode(ns, browseDescription, returnAll, &parentNode->references[i],
  178. relevantReferenceTypes, relevantReferenceTypesSize);
  179. if(!currentNode)
  180. continue;
  181. // 2) Fill the reference description. This also releases the current node.
  182. if(fillReferenceDescription(ns, currentNode, &parentNode->references[i],
  183. browseDescription->resultMask,
  184. &browseResult->references[currentRefs]) != UA_STATUSCODE_GOOD) {
  185. UA_Array_delete(browseResult->references, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], currentRefs);
  186. currentRefs = 0;
  187. browseResult->references = UA_NULL;
  188. browseResult->statusCode = UA_STATUSCODE_UNCERTAINNOTALLNODESAVAILABLE;
  189. break;
  190. }
  191. currentRefs++;
  192. }
  193. if(currentRefs != 0)
  194. browseResult->referencesSize = currentRefs;
  195. else {
  196. UA_free(browseResult->references);
  197. browseResult->references = UA_NULL;
  198. }
  199. }
  200. UA_NodeStore_release(parentNode);
  201. if(!returnAll)
  202. UA_Array_delete(relevantReferenceTypes, &UA_TYPES[UA_TYPES_NODEID], relevantReferenceTypesSize);
  203. }
  204. void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request,
  205. UA_BrowseResponse *response) {
  206. if(request->nodesToBrowseSize <= 0) {
  207. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  208. return;
  209. }
  210. size_t size = request->nodesToBrowseSize;
  211. response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSERESULT], size);
  212. if(!response->results) {
  213. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  214. return;
  215. }
  216. /* ### Begin External Namespaces */
  217. UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
  218. UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
  219. UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
  220. for(UA_Int32 j = 0;j<server->externalNamespacesSize;j++) {
  221. size_t indexSize = 0;
  222. for(size_t i = 0;i < size;i++) {
  223. if(request->nodesToBrowse[i].nodeId.namespaceIndex != server->externalNamespaces[j].index)
  224. continue;
  225. isExternal[i] = UA_TRUE;
  226. indices[indexSize] = i;
  227. indexSize++;
  228. }
  229. if(indexSize == 0)
  230. continue;
  231. UA_ExternalNodeStore *ens = &server->externalNamespaces[j].externalNodeStore;
  232. ens->browseNodes(ens->ensHandle, &request->requestHeader, request->nodesToBrowse, indices, indexSize,
  233. request->requestedMaxReferencesPerNode, response->results, response->diagnosticInfos);
  234. }
  235. /* ### End External Namespaces */
  236. response->resultsSize = size;
  237. for(size_t i = 0;i < size;i++){
  238. if(!isExternal[i])
  239. getBrowseResult(server->nodestore, &request->nodesToBrowse[i],
  240. request->requestedMaxReferencesPerNode, &response->results[i]);
  241. }
  242. }
  243. static UA_StatusCode walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *current,
  244. const UA_RelativePath *path, size_t pathindex,
  245. UA_BrowsePathTarget *targets, size_t *targetsSize,
  246. UA_Int32 *currentTargets) {
  247. const UA_RelativePathElement *elem = &path->elements[pathindex];
  248. if(elem->targetName.name.length == -1)
  249. return UA_STATUSCODE_BADBROWSENAMEINVALID;
  250. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  251. UA_NodeId *referenceTypes;
  252. size_t referenceTypesSize;
  253. UA_Boolean allReferences = UA_FALSE;
  254. if(UA_NodeId_isNull(&elem->referenceTypeId)) {
  255. allReferences = UA_TRUE;
  256. referenceTypesSize = 1; // so we enter the inner loop once
  257. } else if(elem->includeSubtypes) {
  258. retval = findReferenceTypeSubTypes(server->nodestore, &elem->referenceTypeId, &referenceTypes,
  259. &referenceTypesSize);
  260. if(retval != UA_STATUSCODE_GOOD)
  261. return UA_STATUSCODE_BADNOMATCH;
  262. } else {
  263. uintptr_t ptr = (uintptr_t)&elem->referenceTypeId; // ptr magic due to const cast
  264. referenceTypes = (UA_NodeId*)ptr;
  265. referenceTypesSize = 1;
  266. }
  267. for(UA_Int32 i=0;i<current->referencesSize && retval == UA_STATUSCODE_GOOD;i++) {
  268. for(size_t j=0;j<referenceTypesSize && retval == UA_STATUSCODE_GOOD;j++) {
  269. if(!allReferences && (!UA_NodeId_equal(&current->references[i].referenceTypeId, &referenceTypes[j])
  270. || current->references[i].isInverse != elem->isInverse))
  271. continue;
  272. // todo: expandednodeid
  273. const UA_Node *next = UA_NodeStore_get(server->nodestore, &current->references[i].targetId.nodeId);
  274. if(!next)
  275. continue;
  276. if(elem->targetName.namespaceIndex == next->browseName.namespaceIndex &&
  277. UA_String_equal(&elem->targetName.name, &next->browseName.name)) {
  278. if((UA_Int32)pathindex + 1 >= path->elementsSize) {
  279. // at the end of the path.. add the node
  280. if(*currentTargets >= (UA_Int32)*targetsSize) {
  281. UA_BrowsePathTarget *newtargets = UA_realloc(targets, sizeof(UA_BrowsePathTarget) *
  282. (*targetsSize) * 2);
  283. if(!newtargets) {
  284. retval = UA_STATUSCODE_BADOUTOFMEMORY;
  285. } else {
  286. targets = newtargets;
  287. *targetsSize *= 2;
  288. }
  289. }
  290. if(retval == UA_STATUSCODE_GOOD) {
  291. UA_ExpandedNodeId_init(&targets[*currentTargets].targetId);
  292. UA_NodeId_copy(&next->nodeId, &targets[*currentTargets].targetId.nodeId);
  293. targets[*currentTargets].remainingPathIndex = UA_UINT32_MAX;
  294. *currentTargets += 1;
  295. UA_NodeStore_release(next);
  296. break; // go to the next node
  297. }
  298. } else {
  299. // recurse deeper into the path
  300. retval = walkBrowsePath(server, session, next, path, pathindex + 1,
  301. targets, targetsSize, currentTargets);
  302. }
  303. }
  304. UA_NodeStore_release(next);
  305. }
  306. }
  307. if(!allReferences && elem->includeSubtypes)
  308. UA_Array_delete(referenceTypes, &UA_TYPES[UA_TYPES_NODEID], (UA_Int32)referenceTypesSize);
  309. return retval;
  310. }
  311. static void translateBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path,
  312. UA_BrowsePathResult *result) {
  313. size_t arraySize = 10;
  314. result->targets = UA_malloc(sizeof(UA_BrowsePathTarget) * arraySize);
  315. if(!result->targets) {
  316. result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
  317. return;
  318. }
  319. result->targetsSize = 0;
  320. const UA_Node *firstNode = UA_NodeStore_get(server->nodestore, &path->startingNode);
  321. if(!firstNode) {
  322. result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
  323. UA_free(result->targets);
  324. return;
  325. }
  326. if(path->relativePath.elementsSize > 0)
  327. result->statusCode = walkBrowsePath(server, session, firstNode, &path->relativePath, 0,
  328. result->targets, &arraySize, &result->targetsSize);
  329. else
  330. result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
  331. UA_NodeStore_release(firstNode);
  332. if(result->statusCode != UA_STATUSCODE_GOOD) {
  333. UA_Array_delete(result->targets, &UA_TYPES[UA_TYPES_BROWSEPATHTARGET], result->targetsSize);
  334. result->targets = UA_NULL;
  335. result->targetsSize = -1;
  336. } else if(result->targetsSize == 0) {
  337. result->statusCode = UA_STATUSCODE_BADNOMATCH;
  338. UA_free(result->targets);
  339. result->targets = UA_NULL;
  340. result->targetsSize = -1;
  341. }
  342. }
  343. void Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
  344. const UA_TranslateBrowsePathsToNodeIdsRequest *request,
  345. UA_TranslateBrowsePathsToNodeIdsResponse *response) {
  346. if(request->browsePathsSize <= 0) {
  347. response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
  348. return;
  349. }
  350. response->results = UA_Array_new(&UA_TYPES[UA_TYPES_BROWSEPATHRESULT], request->browsePathsSize);
  351. if(!response->results) {
  352. response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
  353. return;
  354. }
  355. response->resultsSize = request->browsePathsSize;
  356. for(UA_Int32 i = 0;i < response->resultsSize;i++)
  357. translateBrowsePath(server, session, &request->browsePaths[i], &response->results[i]);
  358. }