ua_services_view.c 18 KB

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