ua_client_highlevel.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * Copyright 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  6. * Copyright 2015 (c) Oleksiy Vasylyev
  7. * Copyright 2017 (c) Florian Palm
  8. * Copyright 2016 (c) Chris Iatrou
  9. * Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  10. * Copyright 2018 (c) Fabian Arndt
  11. * Copyright 2018 (c) Peter Rustler, basyskom GmbH
  12. */
  13. #include "ua_client.h"
  14. #include "ua_client_internal.h"
  15. #include "ua_client_highlevel.h"
  16. #include "ua_client_highlevel_async.h"
  17. #include "ua_util.h"
  18. UA_StatusCode
  19. UA_Client_NamespaceGetIndex(UA_Client *client, UA_String *namespaceUri,
  20. UA_UInt16 *namespaceIndex) {
  21. UA_ReadRequest request;
  22. UA_ReadRequest_init(&request);
  23. UA_ReadValueId id;
  24. UA_ReadValueId_init(&id);
  25. id.attributeId = UA_ATTRIBUTEID_VALUE;
  26. id.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY);
  27. request.nodesToRead = &id;
  28. request.nodesToReadSize = 1;
  29. UA_ReadResponse response = UA_Client_Service_read(client, request);
  30. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  31. if(response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
  32. retval = response.responseHeader.serviceResult;
  33. else if(response.resultsSize != 1 || !response.results[0].hasValue)
  34. retval = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
  35. else if(response.results[0].value.type != &UA_TYPES[UA_TYPES_STRING])
  36. retval = UA_STATUSCODE_BADTYPEMISMATCH;
  37. if(retval != UA_STATUSCODE_GOOD) {
  38. UA_ReadResponse_deleteMembers(&response);
  39. return retval;
  40. }
  41. retval = UA_STATUSCODE_BADNOTFOUND;
  42. UA_String *ns = (UA_String *)response.results[0].value.data;
  43. for(size_t i = 0; i < response.results[0].value.arrayLength; ++i) {
  44. if(UA_String_equal(namespaceUri, &ns[i])) {
  45. *namespaceIndex = (UA_UInt16)i;
  46. retval = UA_STATUSCODE_GOOD;
  47. break;
  48. }
  49. }
  50. UA_ReadResponse_deleteMembers(&response);
  51. return retval;
  52. }
  53. UA_StatusCode
  54. UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId,
  55. UA_NodeIteratorCallback callback, void *handle) {
  56. UA_BrowseRequest bReq;
  57. UA_BrowseRequest_init(&bReq);
  58. bReq.requestedMaxReferencesPerNode = 0;
  59. bReq.nodesToBrowse = UA_BrowseDescription_new();
  60. bReq.nodesToBrowseSize = 1;
  61. UA_NodeId_copy(&parentNodeId, &bReq.nodesToBrowse[0].nodeId);
  62. bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; //return everything
  63. bReq.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_BOTH;
  64. UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
  65. UA_StatusCode retval = bResp.responseHeader.serviceResult;
  66. if(retval == UA_STATUSCODE_GOOD) {
  67. for(size_t i = 0; i < bResp.resultsSize; ++i) {
  68. for(size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
  69. UA_ReferenceDescription *ref = &bResp.results[i].references[j];
  70. retval |= callback(ref->nodeId.nodeId, !ref->isForward,
  71. ref->referenceTypeId, handle);
  72. }
  73. }
  74. }
  75. UA_BrowseRequest_deleteMembers(&bReq);
  76. UA_BrowseResponse_deleteMembers(&bResp);
  77. return retval;
  78. }
  79. /*******************/
  80. /* Node Management */
  81. /*******************/
  82. UA_StatusCode
  83. UA_Client_addReference(UA_Client *client, const UA_NodeId sourceNodeId,
  84. const UA_NodeId referenceTypeId, UA_Boolean isForward,
  85. const UA_String targetServerUri,
  86. const UA_ExpandedNodeId targetNodeId,
  87. UA_NodeClass targetNodeClass) {
  88. UA_AddReferencesItem item;
  89. UA_AddReferencesItem_init(&item);
  90. item.sourceNodeId = sourceNodeId;
  91. item.referenceTypeId = referenceTypeId;
  92. item.isForward = isForward;
  93. item.targetServerUri = targetServerUri;
  94. item.targetNodeId = targetNodeId;
  95. item.targetNodeClass = targetNodeClass;
  96. UA_AddReferencesRequest request;
  97. UA_AddReferencesRequest_init(&request);
  98. request.referencesToAdd = &item;
  99. request.referencesToAddSize = 1;
  100. UA_AddReferencesResponse response = UA_Client_Service_addReferences(client, request);
  101. UA_StatusCode retval = response.responseHeader.serviceResult;
  102. if(retval != UA_STATUSCODE_GOOD) {
  103. UA_AddReferencesResponse_deleteMembers(&response);
  104. return retval;
  105. }
  106. if(response.resultsSize != 1) {
  107. UA_AddReferencesResponse_deleteMembers(&response);
  108. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  109. }
  110. retval = response.results[0];
  111. UA_AddReferencesResponse_deleteMembers(&response);
  112. return retval;
  113. }
  114. UA_StatusCode
  115. UA_Client_deleteReference(UA_Client *client, const UA_NodeId sourceNodeId,
  116. const UA_NodeId referenceTypeId, UA_Boolean isForward,
  117. const UA_ExpandedNodeId targetNodeId,
  118. UA_Boolean deleteBidirectional) {
  119. UA_DeleteReferencesItem item;
  120. UA_DeleteReferencesItem_init(&item);
  121. item.sourceNodeId = sourceNodeId;
  122. item.referenceTypeId = referenceTypeId;
  123. item.isForward = isForward;
  124. item.targetNodeId = targetNodeId;
  125. item.deleteBidirectional = deleteBidirectional;
  126. UA_DeleteReferencesRequest request;
  127. UA_DeleteReferencesRequest_init(&request);
  128. request.referencesToDelete = &item;
  129. request.referencesToDeleteSize = 1;
  130. UA_DeleteReferencesResponse response = UA_Client_Service_deleteReferences(client, request);
  131. UA_StatusCode retval = response.responseHeader.serviceResult;
  132. if(retval != UA_STATUSCODE_GOOD) {
  133. UA_DeleteReferencesResponse_deleteMembers(&response);
  134. return retval;
  135. }
  136. if(response.resultsSize != 1) {
  137. UA_DeleteReferencesResponse_deleteMembers(&response);
  138. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  139. }
  140. retval = response.results[0];
  141. UA_DeleteReferencesResponse_deleteMembers(&response);
  142. return retval;
  143. }
  144. UA_StatusCode
  145. UA_Client_deleteNode(UA_Client *client, const UA_NodeId nodeId,
  146. UA_Boolean deleteTargetReferences) {
  147. UA_DeleteNodesItem item;
  148. UA_DeleteNodesItem_init(&item);
  149. item.nodeId = nodeId;
  150. item.deleteTargetReferences = deleteTargetReferences;
  151. UA_DeleteNodesRequest request;
  152. UA_DeleteNodesRequest_init(&request);
  153. request.nodesToDelete = &item;
  154. request.nodesToDeleteSize = 1;
  155. UA_DeleteNodesResponse response = UA_Client_Service_deleteNodes(client, request);
  156. UA_StatusCode retval = response.responseHeader.serviceResult;
  157. if(retval != UA_STATUSCODE_GOOD) {
  158. UA_DeleteNodesResponse_deleteMembers(&response);
  159. return retval;
  160. }
  161. if(response.resultsSize != 1) {
  162. UA_DeleteNodesResponse_deleteMembers(&response);
  163. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  164. }
  165. retval = response.results[0];
  166. UA_DeleteNodesResponse_deleteMembers(&response);
  167. return retval;
  168. }
  169. UA_StatusCode
  170. __UA_Client_addNode(UA_Client *client, const UA_NodeClass nodeClass,
  171. const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
  172. const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
  173. const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
  174. const UA_DataType *attributeType, UA_NodeId *outNewNodeId) {
  175. UA_AddNodesRequest request;
  176. UA_AddNodesRequest_init(&request);
  177. UA_AddNodesItem item;
  178. UA_AddNodesItem_init(&item);
  179. item.parentNodeId.nodeId = parentNodeId;
  180. item.referenceTypeId = referenceTypeId;
  181. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  182. item.browseName = browseName;
  183. item.nodeClass = nodeClass;
  184. item.typeDefinition.nodeId = typeDefinition;
  185. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  186. item.nodeAttributes.content.decoded.type = attributeType;
  187. item.nodeAttributes.content.decoded.data = (void*)(uintptr_t)attr; // hack. is not written into.
  188. request.nodesToAdd = &item;
  189. request.nodesToAddSize = 1;
  190. UA_AddNodesResponse response = UA_Client_Service_addNodes(client, request);
  191. UA_StatusCode retval = response.responseHeader.serviceResult;
  192. if(retval != UA_STATUSCODE_GOOD) {
  193. UA_AddNodesResponse_deleteMembers(&response);
  194. return retval;
  195. }
  196. if(response.resultsSize != 1) {
  197. UA_AddNodesResponse_deleteMembers(&response);
  198. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  199. }
  200. /* Move the id of the created node */
  201. retval = response.results[0].statusCode;
  202. if(retval == UA_STATUSCODE_GOOD && outNewNodeId) {
  203. *outNewNodeId = response.results[0].addedNodeId;
  204. UA_NodeId_init(&response.results[0].addedNodeId);
  205. }
  206. UA_AddNodesResponse_deleteMembers(&response);
  207. return retval;
  208. }
  209. /********/
  210. /* Call */
  211. /********/
  212. #ifdef UA_ENABLE_METHODCALLS
  213. UA_StatusCode
  214. UA_Client_call(UA_Client *client, const UA_NodeId objectId,
  215. const UA_NodeId methodId, size_t inputSize,
  216. const UA_Variant *input, size_t *outputSize,
  217. UA_Variant **output) {
  218. /* Set up the request */
  219. UA_CallRequest request;
  220. UA_CallRequest_init(&request);
  221. UA_CallMethodRequest item;
  222. UA_CallMethodRequest_init(&item);
  223. item.methodId = methodId;
  224. item.objectId = objectId;
  225. item.inputArguments = (UA_Variant *)(void*)(uintptr_t)input; // cast const...
  226. item.inputArgumentsSize = inputSize;
  227. request.methodsToCall = &item;
  228. request.methodsToCallSize = 1;
  229. /* Call the service */
  230. UA_CallResponse response = UA_Client_Service_call(client, request);
  231. UA_StatusCode retval = response.responseHeader.serviceResult;
  232. if(retval == UA_STATUSCODE_GOOD) {
  233. if(response.resultsSize == 1)
  234. retval = response.results[0].statusCode;
  235. else
  236. retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
  237. }
  238. if(retval != UA_STATUSCODE_GOOD) {
  239. UA_CallResponse_deleteMembers(&response);
  240. return retval;
  241. }
  242. /* Move the output arguments */
  243. if(output != NULL && outputSize != NULL) {
  244. *output = response.results[0].outputArguments;
  245. *outputSize = response.results[0].outputArgumentsSize;
  246. response.results[0].outputArguments = NULL;
  247. response.results[0].outputArgumentsSize = 0;
  248. }
  249. UA_CallResponse_deleteMembers(&response);
  250. return retval;
  251. }
  252. #endif
  253. /********************/
  254. /* Write Attributes */
  255. /********************/
  256. UA_StatusCode
  257. __UA_Client_writeAttribute(UA_Client *client, const UA_NodeId *nodeId,
  258. UA_AttributeId attributeId, const void *in,
  259. const UA_DataType *inDataType) {
  260. if(!in)
  261. return UA_STATUSCODE_BADTYPEMISMATCH;
  262. UA_WriteValue wValue;
  263. UA_WriteValue_init(&wValue);
  264. wValue.nodeId = *nodeId;
  265. wValue.attributeId = attributeId;
  266. if(attributeId == UA_ATTRIBUTEID_VALUE)
  267. wValue.value.value = *(const UA_Variant*)in;
  268. else
  269. /* hack. is never written into. */
  270. UA_Variant_setScalar(&wValue.value.value, (void*)(uintptr_t)in, inDataType);
  271. wValue.value.hasValue = true;
  272. UA_WriteRequest wReq;
  273. UA_WriteRequest_init(&wReq);
  274. wReq.nodesToWrite = &wValue;
  275. wReq.nodesToWriteSize = 1;
  276. UA_WriteResponse wResp = UA_Client_Service_write(client, wReq);
  277. UA_StatusCode retval = wResp.responseHeader.serviceResult;
  278. if(retval == UA_STATUSCODE_GOOD) {
  279. if(wResp.resultsSize == 1)
  280. retval = wResp.results[0];
  281. else
  282. retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
  283. }
  284. UA_WriteResponse_deleteMembers(&wResp);
  285. return retval;
  286. }
  287. UA_StatusCode
  288. UA_Client_writeArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId,
  289. size_t newArrayDimensionsSize,
  290. const UA_UInt32 *newArrayDimensions) {
  291. if(!newArrayDimensions)
  292. return UA_STATUSCODE_BADTYPEMISMATCH;
  293. UA_WriteValue wValue;
  294. UA_WriteValue_init(&wValue);
  295. wValue.nodeId = nodeId;
  296. wValue.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
  297. UA_Variant_setArray(&wValue.value.value, (void*)(uintptr_t)newArrayDimensions,
  298. newArrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]);
  299. wValue.value.hasValue = true;
  300. UA_WriteRequest wReq;
  301. UA_WriteRequest_init(&wReq);
  302. wReq.nodesToWrite = &wValue;
  303. wReq.nodesToWriteSize = 1;
  304. UA_WriteResponse wResp = UA_Client_Service_write(client, wReq);
  305. UA_StatusCode retval = wResp.responseHeader.serviceResult;
  306. if(retval == UA_STATUSCODE_GOOD) {
  307. if(wResp.resultsSize == 1)
  308. retval = wResp.results[0];
  309. else
  310. retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
  311. }
  312. UA_WriteResponse_deleteMembers(&wResp);
  313. return retval;
  314. }
  315. /*******************/
  316. /* Read Attributes */
  317. /*******************/
  318. UA_StatusCode
  319. __UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId,
  320. UA_AttributeId attributeId, void *out,
  321. const UA_DataType *outDataType) {
  322. UA_ReadValueId item;
  323. UA_ReadValueId_init(&item);
  324. item.nodeId = *nodeId;
  325. item.attributeId = attributeId;
  326. UA_ReadRequest request;
  327. UA_ReadRequest_init(&request);
  328. request.nodesToRead = &item;
  329. request.nodesToReadSize = 1;
  330. UA_ReadResponse response = UA_Client_Service_read(client, request);
  331. UA_StatusCode retval = response.responseHeader.serviceResult;
  332. if(retval == UA_STATUSCODE_GOOD) {
  333. if(response.resultsSize == 1)
  334. retval = response.results[0].status;
  335. else
  336. retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
  337. }
  338. if(retval != UA_STATUSCODE_GOOD) {
  339. UA_ReadResponse_deleteMembers(&response);
  340. return retval;
  341. }
  342. /* Set the StatusCode */
  343. UA_DataValue *res = response.results;
  344. if(res->hasStatus)
  345. retval = res->status;
  346. /* Return early of no value is given */
  347. if(!res->hasValue) {
  348. if(retval == UA_STATUSCODE_GOOD)
  349. retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
  350. UA_ReadResponse_deleteMembers(&response);
  351. return retval;
  352. }
  353. /* Copy value into out */
  354. if(attributeId == UA_ATTRIBUTEID_VALUE) {
  355. memcpy(out, &res->value, sizeof(UA_Variant));
  356. UA_Variant_init(&res->value);
  357. } else if(attributeId == UA_ATTRIBUTEID_NODECLASS) {
  358. memcpy(out, (UA_NodeClass*)res->value.data, sizeof(UA_NodeClass));
  359. } else if(UA_Variant_isScalar(&res->value) &&
  360. res->value.type == outDataType) {
  361. memcpy(out, res->value.data, res->value.type->memSize);
  362. UA_free(res->value.data);
  363. res->value.data = NULL;
  364. } else {
  365. retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
  366. }
  367. UA_ReadResponse_deleteMembers(&response);
  368. return retval;
  369. }
  370. static UA_StatusCode
  371. processReadArrayDimensionsResult(UA_ReadResponse *response,
  372. UA_UInt32 **outArrayDimensions,
  373. size_t *outArrayDimensionsSize) {
  374. UA_StatusCode retval = response->responseHeader.serviceResult;
  375. if(retval != UA_STATUSCODE_GOOD)
  376. return retval;
  377. if(response->resultsSize != 1)
  378. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  379. retval = response->results[0].status;
  380. if(retval != UA_STATUSCODE_GOOD)
  381. return retval;
  382. UA_DataValue *res = &response->results[0];
  383. if(!res->hasValue ||
  384. UA_Variant_isScalar(&res->value) ||
  385. res->value.type != &UA_TYPES[UA_TYPES_UINT32])
  386. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  387. /* Move results */
  388. *outArrayDimensions = (UA_UInt32*)res->value.data;
  389. *outArrayDimensionsSize = res->value.arrayLength;
  390. res->value.data = NULL;
  391. res->value.arrayLength = 0;
  392. return UA_STATUSCODE_GOOD;
  393. }
  394. UA_StatusCode
  395. UA_Client_readArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId,
  396. size_t *outArrayDimensionsSize,
  397. UA_UInt32 **outArrayDimensions) {
  398. UA_ReadValueId item;
  399. UA_ReadValueId_init(&item);
  400. item.nodeId = nodeId;
  401. item.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
  402. UA_ReadRequest request;
  403. UA_ReadRequest_init(&request);
  404. request.nodesToRead = &item;
  405. request.nodesToReadSize = 1;
  406. UA_ReadResponse response = UA_Client_Service_read(client, request);
  407. UA_StatusCode retval = processReadArrayDimensionsResult(&response, outArrayDimensions,
  408. outArrayDimensionsSize);
  409. UA_ReadResponse_deleteMembers(&response);
  410. return retval;
  411. }
  412. /*********************/
  413. /* Historical Access */
  414. /*********************/
  415. #ifdef UA_ENABLE_HISTORIZING
  416. static UA_HistoryReadResponse
  417. __UA_Client_HistoryRead(UA_Client *client, const UA_NodeId *nodeId,
  418. UA_ExtensionObject* details, UA_String indexRange,
  419. UA_TimestampsToReturn timestampsToReturn,
  420. UA_ByteString continuationPoint, UA_Boolean releaseConti) {
  421. UA_HistoryReadValueId item;
  422. UA_HistoryReadValueId_init(&item);
  423. item.nodeId = *nodeId;
  424. item.indexRange = indexRange;
  425. item.continuationPoint = continuationPoint;
  426. item.dataEncoding = UA_QUALIFIEDNAME(0, "Default Binary");
  427. UA_HistoryReadRequest request;
  428. UA_HistoryReadRequest_init(&request);
  429. request.nodesToRead = &item;
  430. request.nodesToReadSize = 1;
  431. request.timestampsToReturn = timestampsToReturn; // Defaults to Source
  432. request.releaseContinuationPoints = releaseConti; // No values are returned, if true
  433. /* Build ReadDetails */
  434. request.historyReadDetails = *details;
  435. return UA_Client_Service_historyRead(client, request);
  436. }
  437. static UA_StatusCode
  438. __UA_Client_HistoryRead_service(UA_Client *client, const UA_NodeId *nodeId,
  439. const UA_HistoricalIteratorCallback callback,
  440. UA_ExtensionObject *details, UA_String indexRange,
  441. UA_TimestampsToReturn timestampsToReturn,
  442. void *callbackContext) {
  443. UA_ByteString continuationPoint = UA_BYTESTRING_NULL;
  444. UA_Boolean continuationAvail = UA_FALSE;
  445. UA_Boolean fetchMore = UA_FALSE;
  446. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  447. do {
  448. /* We release the continuation point, if no more data is requested by the user */
  449. UA_Boolean cleanup = !fetchMore && continuationAvail;
  450. UA_HistoryReadResponse response =
  451. __UA_Client_HistoryRead(client, nodeId, details, indexRange, timestampsToReturn, continuationPoint, cleanup);
  452. if (cleanup) {
  453. retval = response.responseHeader.serviceResult;
  454. cleanup: UA_HistoryReadResponse_deleteMembers(&response);
  455. UA_ByteString_deleteMembers(&continuationPoint);
  456. return retval;
  457. }
  458. retval = response.responseHeader.serviceResult;
  459. if (retval == UA_STATUSCODE_GOOD) {
  460. if (response.resultsSize == 1)
  461. retval = response.results[0].statusCode;
  462. else
  463. retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
  464. }
  465. if (retval != UA_STATUSCODE_GOOD)
  466. goto cleanup;
  467. UA_HistoryReadResult *res = response.results;
  468. /* Clear old and check / store new continuation point */
  469. UA_ByteString_deleteMembers(&continuationPoint);
  470. UA_ByteString_copy(&res->continuationPoint, &continuationPoint);
  471. continuationAvail = !UA_ByteString_equal(&continuationPoint, &UA_BYTESTRING_NULL);
  472. /* Client callback with possibility to request further values */
  473. fetchMore = callback(client, nodeId, continuationAvail, &res->historyData, callbackContext);
  474. /* Regular cleanup */
  475. UA_HistoryReadResponse_deleteMembers(&response);
  476. } while (continuationAvail);
  477. return retval;
  478. }
  479. #ifdef UA_ENABLE_EXPERIMENTAL_HISTORIZING
  480. UA_StatusCode
  481. UA_Client_HistoryRead_events(UA_Client *client, const UA_NodeId *nodeId,
  482. const UA_HistoricalIteratorCallback callback,
  483. UA_DateTime startTime, UA_DateTime endTime,
  484. UA_String indexRange, const UA_EventFilter filter, UA_UInt32 numValuesPerNode,
  485. UA_TimestampsToReturn timestampsToReturn, void *callbackContext) {
  486. UA_ReadEventDetails details;
  487. UA_ReadEventDetails_init(&details);
  488. details.filter = filter;
  489. // At least two of the following parameters must be set
  490. details.numValuesPerNode = numValuesPerNode; // 0 = return all / max server is capable of
  491. details.startTime = startTime;
  492. details.endTime = endTime;
  493. UA_ExtensionObject detailsExtensionObject;
  494. UA_ExtensionObject_init(&detailsExtensionObject);
  495. detailsExtensionObject.content.decoded.type = &UA_TYPES[UA_TYPES_READEVENTDETAILS];
  496. detailsExtensionObject.content.decoded.data = &details;
  497. detailsExtensionObject.encoding = UA_EXTENSIONOBJECT_DECODED;
  498. return __UA_Client_HistoryRead_service(client, nodeId, callback, &detailsExtensionObject,
  499. indexRange, timestampsToReturn, callbackContext);
  500. }
  501. #endif // UA_ENABLE_EXPERIMENTAL_HISTORIZING
  502. static UA_StatusCode
  503. __UA_Client_HistoryRead_service_rawMod(UA_Client *client, const UA_NodeId *nodeId,
  504. const UA_HistoricalIteratorCallback callback,
  505. UA_DateTime startTime,UA_DateTime endTime,
  506. UA_String indexRange, UA_Boolean returnBounds, UA_UInt32 numValuesPerNode,
  507. UA_Boolean readModified, UA_TimestampsToReturn timestampsToReturn,
  508. void *callbackContext) {
  509. UA_ReadRawModifiedDetails details;
  510. UA_ReadRawModifiedDetails_init(&details);
  511. details.isReadModified = readModified; // Return only modified values
  512. details.returnBounds = returnBounds; // Return values pre / post given range
  513. // At least two of the following parameters must be set
  514. details.numValuesPerNode = numValuesPerNode; // 0 = return all / max server is capable of
  515. details.startTime = startTime;
  516. details.endTime = endTime;
  517. UA_ExtensionObject detailsExtensionObject;
  518. UA_ExtensionObject_init(&detailsExtensionObject);
  519. detailsExtensionObject.content.decoded.type = &UA_TYPES[UA_TYPES_READRAWMODIFIEDDETAILS];
  520. detailsExtensionObject.content.decoded.data = &details;
  521. detailsExtensionObject.encoding = UA_EXTENSIONOBJECT_DECODED;
  522. return __UA_Client_HistoryRead_service(client, nodeId, callback,
  523. &detailsExtensionObject, indexRange,
  524. timestampsToReturn, callbackContext);
  525. }
  526. UA_StatusCode
  527. UA_Client_HistoryRead_raw(UA_Client *client, const UA_NodeId *nodeId,
  528. const UA_HistoricalIteratorCallback callback,
  529. UA_DateTime startTime, UA_DateTime endTime,
  530. UA_String indexRange, UA_Boolean returnBounds, UA_UInt32 numValuesPerNode,
  531. UA_TimestampsToReturn timestampsToReturn, void *callbackContext) {
  532. return __UA_Client_HistoryRead_service_rawMod(client, nodeId, callback, startTime, endTime, indexRange, returnBounds,
  533. numValuesPerNode, UA_FALSE, timestampsToReturn, callbackContext);
  534. }
  535. #ifdef UA_ENABLE_EXPERIMENTAL_HISTORIZING
  536. UA_StatusCode
  537. UA_Client_HistoryRead_modified(UA_Client *client, const UA_NodeId *nodeId,
  538. const UA_HistoricalIteratorCallback callback,
  539. UA_DateTime startTime, UA_DateTime endTime,
  540. UA_String indexRange, UA_Boolean returnBounds, UA_UInt32 maxItems,
  541. UA_TimestampsToReturn timestampsToReturn, void *callbackContext) {
  542. return __UA_Client_HistoryRead_service_rawMod(client, nodeId, callback, startTime, endTime, indexRange, returnBounds,
  543. maxItems, UA_TRUE, timestampsToReturn, callbackContext);
  544. }
  545. #endif // UA_ENABLE_EXPERIMENTAL_HISTORIZING
  546. static UA_HistoryUpdateResponse
  547. __UA_Client_HistoryUpdate(UA_Client *client,
  548. void *details,
  549. size_t typeId)
  550. {
  551. UA_HistoryUpdateRequest request;
  552. UA_HistoryUpdateRequest_init(&request);
  553. UA_ExtensionObject extension;
  554. UA_ExtensionObject_init(&extension);
  555. request.historyUpdateDetailsSize = 1;
  556. request.historyUpdateDetails = &extension;
  557. extension.encoding = UA_EXTENSIONOBJECT_DECODED;
  558. extension.content.decoded.type = &UA_TYPES[typeId];
  559. extension.content.decoded.data = details;
  560. UA_HistoryUpdateResponse response;
  561. response = UA_Client_Service_historyUpdate(client, request);
  562. //UA_HistoryUpdateRequest_deleteMembers(&request);
  563. return response;
  564. }
  565. static UA_StatusCode
  566. __UA_Client_HistoryUpdate_updateData(UA_Client *client,
  567. const UA_NodeId *nodeId,
  568. UA_PerformUpdateType type,
  569. UA_DataValue *value)
  570. {
  571. UA_StatusCode ret = UA_STATUSCODE_GOOD;
  572. UA_UpdateDataDetails details;
  573. UA_UpdateDataDetails_init(&details);
  574. details.performInsertReplace = type;
  575. details.updateValuesSize = 1;
  576. details.updateValues = value;
  577. UA_NodeId_copy(nodeId, &details.nodeId);
  578. UA_HistoryUpdateResponse response;
  579. response = __UA_Client_HistoryUpdate(client, &details, UA_TYPES_UPDATEDATADETAILS);
  580. if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
  581. ret = response.responseHeader.serviceResult;
  582. goto cleanup;
  583. }
  584. if (response.resultsSize != 1 || response.results[0].operationResultsSize != 1) {
  585. ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
  586. goto cleanup;
  587. }
  588. if (response.results[0].statusCode != UA_STATUSCODE_GOOD) {
  589. ret = response.results[0].statusCode;
  590. goto cleanup;
  591. }
  592. ret = response.results[0].operationResults[0];
  593. cleanup:
  594. UA_HistoryUpdateResponse_deleteMembers(&response);
  595. UA_NodeId_deleteMembers(&details.nodeId);
  596. return ret;
  597. }
  598. UA_StatusCode
  599. UA_Client_HistoryUpdate_insert(UA_Client *client,
  600. const UA_NodeId *nodeId,
  601. UA_DataValue *value)
  602. {
  603. return __UA_Client_HistoryUpdate_updateData(client,
  604. nodeId,
  605. UA_PERFORMUPDATETYPE_INSERT,
  606. value);
  607. }
  608. UA_StatusCode
  609. UA_Client_HistoryUpdate_replace(UA_Client *client,
  610. const UA_NodeId *nodeId,
  611. UA_DataValue *value)
  612. {
  613. return __UA_Client_HistoryUpdate_updateData(client,
  614. nodeId,
  615. UA_PERFORMUPDATETYPE_REPLACE,
  616. value);
  617. }
  618. UA_StatusCode
  619. UA_Client_HistoryUpdate_update(UA_Client *client,
  620. const UA_NodeId *nodeId,
  621. UA_DataValue *value)
  622. {
  623. return __UA_Client_HistoryUpdate_updateData(client,
  624. nodeId,
  625. UA_PERFORMUPDATETYPE_UPDATE,
  626. value);
  627. }
  628. UA_StatusCode
  629. UA_Client_HistoryUpdate_deleteRaw(UA_Client *client,
  630. const UA_NodeId *nodeId,
  631. UA_DateTime startTimestamp,
  632. UA_DateTime endTimestamp)
  633. {
  634. UA_StatusCode ret = UA_STATUSCODE_GOOD;
  635. UA_DeleteRawModifiedDetails details;
  636. UA_DeleteRawModifiedDetails_init(&details);
  637. details.isDeleteModified = false;
  638. details.startTime = startTimestamp;
  639. details.endTime = endTimestamp;
  640. UA_NodeId_copy(nodeId, &details.nodeId);
  641. UA_HistoryUpdateResponse response;
  642. response = __UA_Client_HistoryUpdate(client, &details, UA_TYPES_DELETERAWMODIFIEDDETAILS);
  643. if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
  644. ret = response.responseHeader.serviceResult;
  645. goto cleanup;
  646. }
  647. if (response.resultsSize != 1) {
  648. ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
  649. goto cleanup;
  650. }
  651. ret = response.results[0].statusCode;
  652. cleanup:
  653. UA_HistoryUpdateResponse_deleteMembers(&response);
  654. UA_NodeId_deleteMembers(&details.nodeId);
  655. return ret;
  656. }
  657. #endif // UA_ENABLE_HISTORIZING
  658. /* Async Functions */
  659. static
  660. void ValueAttributeRead(UA_Client *client, void *userdata,
  661. UA_UInt32 requestId, void *response) {
  662. if(!response)
  663. return;
  664. /* Find the callback for the response */
  665. CustomCallback *cc;
  666. LIST_FOREACH(cc, &client->customCallbacks, pointers) {
  667. if(cc->callbackId == requestId)
  668. break;
  669. }
  670. if(!cc)
  671. return;
  672. UA_ReadResponse *rr = (UA_ReadResponse *) response;
  673. UA_DataValue *res = rr->results;
  674. UA_Boolean done = false;
  675. if(rr->resultsSize == 1 && res != NULL && res->hasValue) {
  676. if(cc->attributeId == UA_ATTRIBUTEID_VALUE) {
  677. /* Call directly with the variant */
  678. cc->callback(client, userdata, requestId, &res->value);
  679. done = true;
  680. } else if(UA_Variant_isScalar(&res->value) &&
  681. res->value.type == cc->outDataType) {
  682. /* Unpack the value */
  683. UA_STACKARRAY(UA_Byte, value, cc->outDataType->memSize);
  684. memcpy(&value, res->value.data, cc->outDataType->memSize);
  685. cc->callback(client, userdata, requestId, &value);
  686. done = true;
  687. }
  688. }
  689. /* Could not process, delete the callback anyway */
  690. if(!done)
  691. UA_LOG_INFO(&client->config.logger, UA_LOGCATEGORY_CLIENT,
  692. "Cannot process the response to the async read "
  693. "request %u", requestId);
  694. LIST_REMOVE(cc, pointers);
  695. UA_free(cc);
  696. }
  697. /*Read Attributes*/
  698. UA_StatusCode __UA_Client_readAttribute_async(UA_Client *client,
  699. const UA_NodeId *nodeId, UA_AttributeId attributeId,
  700. const UA_DataType *outDataType, UA_ClientAsyncServiceCallback callback,
  701. void *userdata, UA_UInt32 *reqId) {
  702. UA_ReadValueId item;
  703. UA_ReadValueId_init(&item);
  704. item.nodeId = *nodeId;
  705. item.attributeId = attributeId;
  706. UA_ReadRequest request;
  707. UA_ReadRequest_init(&request);
  708. request.nodesToRead = &item;
  709. request.nodesToReadSize = 1;
  710. __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_READREQUEST],
  711. ValueAttributeRead, &UA_TYPES[UA_TYPES_READRESPONSE],
  712. userdata, reqId);
  713. CustomCallback *cc = (CustomCallback*) UA_malloc(sizeof(CustomCallback));
  714. if (!cc)
  715. return UA_STATUSCODE_BADOUTOFMEMORY;
  716. cc->callback = callback;
  717. cc->callbackId = *reqId;
  718. cc->attributeId = attributeId;
  719. cc->outDataType = outDataType;
  720. LIST_INSERT_HEAD(&client->customCallbacks, cc, pointers);
  721. return UA_STATUSCODE_GOOD;
  722. }
  723. /*Write Attributes*/
  724. UA_StatusCode __UA_Client_writeAttribute_async(UA_Client *client,
  725. const UA_NodeId *nodeId, UA_AttributeId attributeId, const void *in,
  726. const UA_DataType *inDataType, UA_ClientAsyncServiceCallback callback,
  727. void *userdata, UA_UInt32 *reqId) {
  728. if (!in)
  729. return UA_STATUSCODE_BADTYPEMISMATCH;
  730. UA_WriteValue wValue;
  731. UA_WriteValue_init(&wValue);
  732. wValue.nodeId = *nodeId;
  733. wValue.attributeId = attributeId;
  734. if (attributeId == UA_ATTRIBUTEID_VALUE)
  735. wValue.value.value = *(const UA_Variant*) in;
  736. else
  737. /* hack. is never written into. */
  738. UA_Variant_setScalar(&wValue.value.value, (void*) (uintptr_t) in,
  739. inDataType);
  740. wValue.value.hasValue = true;
  741. UA_WriteRequest wReq;
  742. UA_WriteRequest_init(&wReq);
  743. wReq.nodesToWrite = &wValue;
  744. wReq.nodesToWriteSize = 1;
  745. return __UA_Client_AsyncService(client, &wReq,
  746. &UA_TYPES[UA_TYPES_WRITEREQUEST], callback,
  747. &UA_TYPES[UA_TYPES_WRITERESPONSE], userdata, reqId);
  748. }
  749. /*Node Management*/
  750. UA_StatusCode UA_EXPORT
  751. __UA_Client_addNode_async(UA_Client *client, const UA_NodeClass nodeClass,
  752. const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
  753. const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
  754. const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
  755. const UA_DataType *attributeType, UA_NodeId *outNewNodeId,
  756. UA_ClientAsyncServiceCallback callback, void *userdata,
  757. UA_UInt32 *reqId) {
  758. UA_AddNodesRequest request;
  759. UA_AddNodesRequest_init(&request);
  760. UA_AddNodesItem item;
  761. UA_AddNodesItem_init(&item);
  762. item.parentNodeId.nodeId = parentNodeId;
  763. item.referenceTypeId = referenceTypeId;
  764. item.requestedNewNodeId.nodeId = requestedNewNodeId;
  765. item.browseName = browseName;
  766. item.nodeClass = nodeClass;
  767. item.typeDefinition.nodeId = typeDefinition;
  768. item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
  769. item.nodeAttributes.content.decoded.type = attributeType;
  770. item.nodeAttributes.content.decoded.data = (void*) (uintptr_t) attr; // hack. is not written into.
  771. request.nodesToAdd = &item;
  772. request.nodesToAddSize = 1;
  773. return __UA_Client_AsyncService(client, &request,
  774. &UA_TYPES[UA_TYPES_ADDNODESREQUEST], callback,
  775. &UA_TYPES[UA_TYPES_ADDNODESRESPONSE], userdata, reqId);
  776. }
  777. /* Misc Highlevel Functions */
  778. #ifdef UA_ENABLE_METHODCALLS
  779. UA_StatusCode __UA_Client_call_async(UA_Client *client,
  780. const UA_NodeId objectId, const UA_NodeId methodId, size_t inputSize,
  781. const UA_Variant *input, UA_ClientAsyncServiceCallback callback,
  782. void *userdata, UA_UInt32 *reqId) {
  783. UA_CallRequest request;
  784. UA_CallRequest_init(&request);
  785. UA_CallMethodRequest item;
  786. UA_CallMethodRequest_init(&item);
  787. item.methodId = methodId;
  788. item.objectId = objectId;
  789. item.inputArguments = (UA_Variant *) (void*) (uintptr_t) input; // cast const...
  790. item.inputArgumentsSize = inputSize;
  791. request.methodsToCall = &item;
  792. request.methodsToCallSize = 1;
  793. return __UA_Client_AsyncService(client, &request,
  794. &UA_TYPES[UA_TYPES_CALLREQUEST], callback,
  795. &UA_TYPES[UA_TYPES_CALLRESPONSE], userdata, reqId);
  796. }
  797. #endif
  798. UA_StatusCode __UA_Client_translateBrowsePathsToNodeIds_async(UA_Client *client,
  799. char *paths[], UA_UInt32 ids[], size_t pathSize,
  800. UA_ClientAsyncServiceCallback callback, void *userdata,
  801. UA_UInt32 *reqId) {
  802. UA_BrowsePath browsePath;
  803. UA_BrowsePath_init(&browsePath);
  804. browsePath.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  805. browsePath.relativePath.elements = (UA_RelativePathElement*) UA_Array_new(
  806. pathSize, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
  807. if (!browsePath.relativePath.elements)
  808. return UA_STATUSCODE_BADOUTOFMEMORY;
  809. browsePath.relativePath.elementsSize = pathSize;
  810. UA_TranslateBrowsePathsToNodeIdsRequest request;
  811. UA_TranslateBrowsePathsToNodeIdsRequest_init(&request);
  812. request.browsePaths = &browsePath;
  813. request.browsePathsSize = 1;
  814. UA_StatusCode retval = __UA_Client_AsyncService(client, &request,
  815. &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST], callback,
  816. &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE], userdata,
  817. reqId);
  818. if (retval != UA_STATUSCODE_GOOD) {
  819. UA_Array_delete(browsePath.relativePath.elements,
  820. browsePath.relativePath.elementsSize,
  821. &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
  822. return retval;
  823. }
  824. UA_BrowsePath_deleteMembers(&browsePath);
  825. return retval;
  826. }