ua_client_highlevel.c 35 KB

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