check_historical_data.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  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 2018 (c) basysKom GmbH <opensource@basyskom.com> (Author: Peter Rustler)
  6. */
  7. #include "ua_types.h"
  8. #include "ua_server.h"
  9. #include "server/ua_server_internal.h"
  10. #include "ua_client.h"
  11. #include "client/ua_client_internal.h"
  12. #include "ua_client_highlevel.h"
  13. #include "ua_config_default.h"
  14. #include "ua_network_tcp.h"
  15. #include "check.h"
  16. #include "testing_clock.h"
  17. #include "testing_networklayers.h"
  18. #include "thread_wrapper.h"
  19. #include "ua_plugin_historydatabase.h"
  20. #include "ua_historydatabase_default.h"
  21. #include "ua_plugin_history_data_gathering.h"
  22. #include "ua_historydatabackend_memory.h"
  23. #include "ua_historydatagathering_default.h"
  24. #include "historical_read_test_data.h"
  25. #include <stddef.h>
  26. UA_Server *server;
  27. UA_ServerConfig *config;
  28. UA_HistoryDataGathering *gathering;
  29. UA_Boolean running;
  30. THREAD_HANDLE server_thread;
  31. UA_Client *client;
  32. UA_NodeId parentNodeId;
  33. UA_NodeId parentReferenceNodeId;
  34. UA_NodeId outNodeId;
  35. THREAD_CALLBACK(serverloop)
  36. {
  37. while(running)
  38. UA_Server_run_iterate(server, true);
  39. return 0;
  40. }
  41. static void
  42. setup(void)
  43. {
  44. running = true;
  45. config = UA_ServerConfig_new_default();
  46. gathering = (UA_HistoryDataGathering*)UA_calloc(1, sizeof(UA_HistoryDataGathering));
  47. *gathering = UA_HistoryDataGathering_Default(1);
  48. config->historyDatabase = UA_HistoryDatabase_default(*gathering);
  49. server = UA_Server_new(config);
  50. UA_StatusCode retval = UA_Server_run_startup(server);
  51. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  52. THREAD_CREATE(server_thread, serverloop);
  53. /* Define the attribute of the uint32 variable node */
  54. UA_VariableAttributes attr = UA_VariableAttributes_default;
  55. UA_UInt32 myUint32 = 40;
  56. UA_Variant_setScalar(&attr.value, &myUint32, &UA_TYPES[UA_TYPES_UINT32]);
  57. attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
  58. attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
  59. attr.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
  60. attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_HISTORYREAD;
  61. attr.historizing = true;
  62. /* Add the variable node to the information model */
  63. UA_NodeId uint32NodeId = UA_NODEID_STRING(1, "the.answer");
  64. UA_QualifiedName uint32Name = UA_QUALIFIEDNAME(1, "the answer");
  65. parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  66. parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
  67. UA_NodeId_init(&outNodeId);
  68. ck_assert_uint_eq(UA_Server_addVariableNode(server,
  69. uint32NodeId,
  70. parentNodeId,
  71. parentReferenceNodeId,
  72. uint32Name,
  73. UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
  74. attr,
  75. NULL,
  76. &outNodeId)
  77. , UA_STATUSCODE_GOOD);
  78. client = UA_Client_new(UA_ClientConfig_default);
  79. retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
  80. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  81. UA_Client_recv = client->connection.recv;
  82. client->connection.recv = UA_Client_recvTesting;
  83. }
  84. static void
  85. teardown(void)
  86. {
  87. /* cleanup */
  88. UA_Client_disconnect(client);
  89. UA_Client_delete(client);
  90. UA_NodeId_deleteMembers(&parentNodeId);
  91. UA_NodeId_deleteMembers(&parentReferenceNodeId);
  92. UA_NodeId_deleteMembers(&outNodeId);
  93. running = false;
  94. THREAD_JOIN(server_thread);
  95. UA_Server_run_shutdown(server);
  96. UA_Server_delete(server);
  97. UA_ServerConfig_delete(config);
  98. UA_free(gathering);
  99. }
  100. #ifdef UA_ENABLE_HISTORIZING
  101. #include <stdio.h>
  102. #include "ua_session.h"
  103. static UA_StatusCode
  104. setUInt32(UA_Client *thisClient, UA_NodeId node, UA_UInt32 value)
  105. {
  106. UA_Variant variant;
  107. UA_Variant_setScalar(&variant, &value, &UA_TYPES[UA_TYPES_UINT32]);
  108. return UA_Client_writeValueAttribute(thisClient, node, &variant);
  109. }
  110. static void
  111. printTimestamp(UA_DateTime timestamp)
  112. {
  113. if (timestamp == TIMESTAMP_FIRST) {
  114. fprintf(stderr, "FIRST,");
  115. } else if (timestamp == TIMESTAMP_LAST) {
  116. fprintf(stderr, "LAST,");
  117. } else {
  118. fprintf(stderr, "%3lld,", timestamp / UA_DATETIME_SEC);
  119. }
  120. }
  121. static void
  122. printResult(UA_DataValue * value)
  123. {
  124. if (value->status != UA_STATUSCODE_GOOD)
  125. fprintf(stderr, "%s:", UA_StatusCode_name(value->status));
  126. printTimestamp(value->sourceTimestamp);
  127. }
  128. static UA_Boolean
  129. resultIsEqual(const UA_DataValue * result, const testTuple * tuple, size_t index)
  130. {
  131. switch (tuple->result[index]) {
  132. case TIMESTAMP_FIRST:
  133. if (result->status != UA_STATUSCODE_BADBOUNDNOTFOUND
  134. || !UA_Variant_isEmpty(&result->value))
  135. return false;
  136. /* we do not test timestamp if TIMESTAMP_UNSPECIFIED is given for start.
  137. * See OPC UA Part 11, Version 1.03, Page 5-6, Table 1, Mark b for details.*/
  138. if (tuple->start != TIMESTAMP_UNSPECIFIED
  139. && tuple->start != result->sourceTimestamp)
  140. return false;
  141. break;
  142. case TIMESTAMP_LAST:
  143. if (result->status != UA_STATUSCODE_BADBOUNDNOTFOUND
  144. || !UA_Variant_isEmpty(&result->value))
  145. return false;
  146. /* we do not test timestamp if TIMESTAMP_UNSPECIFIED is given for end.
  147. * See OPC UA Part 11, Version 1.03, Page 5-6, Table 1, Mark a for details.*/
  148. if (tuple->end != TIMESTAMP_UNSPECIFIED
  149. && tuple->end != result->sourceTimestamp)
  150. return false;
  151. break;
  152. default:
  153. if (result->sourceTimestamp != tuple->result[index]
  154. || result->value.type != &UA_TYPES[UA_TYPES_INT64]
  155. || *((UA_Int64*)result->value.data) != tuple->result[index])
  156. return false;
  157. }
  158. return true;
  159. }
  160. static UA_Boolean
  161. fillHistoricalDataBackend(UA_HistoryDataBackend backend)
  162. {
  163. int i = 0;
  164. UA_DateTime currentDateTime = testData[i];
  165. fprintf(stderr, "Adding to historical data backend: ");
  166. while (currentDateTime) {
  167. fprintf(stderr, "%lld, ", currentDateTime / UA_DATETIME_SEC);
  168. UA_DataValue value;
  169. UA_DataValue_init(&value);
  170. value.hasValue = true;
  171. UA_Int64 d = currentDateTime;
  172. UA_Variant_setScalarCopy(&value.value, &d, &UA_TYPES[UA_TYPES_INT64]);
  173. value.hasSourceTimestamp = true;
  174. value.sourceTimestamp = currentDateTime;
  175. value.hasStatus = true;
  176. value.status = UA_STATUSCODE_GOOD;
  177. if (backend.serverSetHistoryData(server, backend.context, NULL, NULL, &outNodeId, UA_FALSE, &value) != UA_STATUSCODE_GOOD) {
  178. fprintf(stderr, "\n");
  179. return false;
  180. }
  181. UA_DataValue_deleteMembers(&value);
  182. currentDateTime = testData[++i];
  183. }
  184. fprintf(stderr, "\n");
  185. return true;
  186. }
  187. void
  188. Service_HistoryRead(UA_Server *server, UA_Session *session,
  189. const UA_HistoryReadRequest *request,
  190. UA_HistoryReadResponse *response);
  191. static void
  192. requestHistory(UA_DateTime start,
  193. UA_DateTime end,
  194. UA_HistoryReadResponse * response,
  195. UA_UInt32 numValuesPerNode,
  196. UA_Boolean returnBounds,
  197. UA_ByteString *continuationPoint)
  198. {
  199. UA_ReadRawModifiedDetails *details = UA_ReadRawModifiedDetails_new();
  200. details->startTime = start;
  201. details->endTime = end;
  202. details->isReadModified = false;
  203. details->numValuesPerNode = numValuesPerNode;
  204. details->returnBounds = returnBounds;
  205. UA_HistoryReadValueId *valueId = UA_HistoryReadValueId_new();
  206. UA_NodeId_copy(&outNodeId, &valueId->nodeId);
  207. if (continuationPoint)
  208. UA_ByteString_copy(continuationPoint, &valueId->continuationPoint);
  209. UA_HistoryReadRequest request;
  210. UA_HistoryReadRequest_init(&request);
  211. request.historyReadDetails.encoding = UA_EXTENSIONOBJECT_DECODED;
  212. request.historyReadDetails.content.decoded.type = &UA_TYPES[UA_TYPES_READRAWMODIFIEDDETAILS];
  213. request.historyReadDetails.content.decoded.data = details;
  214. request.timestampsToReturn = UA_TIMESTAMPSTORETURN_SOURCE;
  215. request.nodesToReadSize = 1;
  216. request.nodesToRead = valueId;
  217. Service_HistoryRead(server, &server->adminSession, &request, response);
  218. UA_HistoryReadRequest_deleteMembers(&request);
  219. }
  220. static UA_UInt32
  221. testHistoricalDataBackend(size_t maxResponseSize)
  222. {
  223. const UA_HistorizingNodeIdSettings* setting = gathering->getHistorizingSetting(server, gathering->context, &outNodeId);
  224. UA_HistorizingNodeIdSettings newSetting = *setting;
  225. newSetting.maxHistoryDataResponseSize = maxResponseSize;
  226. gathering->updateNodeIdSetting(server, gathering->context, &outNodeId, newSetting);
  227. UA_UInt32 retval = 0;
  228. size_t i = 0;
  229. testTuple *current = &testRequests[i];
  230. fprintf(stderr, "Testing with maxResponseSize of %lu\n", maxResponseSize);
  231. fprintf(stderr, "Start | End | numValuesPerNode | returnBounds |ContPoint| {Expected}{Result} Result\n");
  232. fprintf(stderr, "------+------+------------------+--------------+---------+----------------\n");
  233. size_t j;
  234. while (current->start || current->end) {
  235. j = 0;
  236. if (current->start == TIMESTAMP_UNSPECIFIED) {
  237. fprintf(stderr, "UNSPEC|");
  238. } else {
  239. fprintf(stderr, " %3lld |", current->start / UA_DATETIME_SEC);
  240. }
  241. if (current->end == TIMESTAMP_UNSPECIFIED) {
  242. fprintf(stderr, "UNSPEC|");
  243. } else {
  244. fprintf(stderr, " %3lld |", current->end / UA_DATETIME_SEC);
  245. }
  246. fprintf(stderr, " %2u | %s | %s | {", current->numValuesPerNode, (current->returnBounds ? "Yes" : " No"), (current->returnContinuationPoint ? "Yes" : " No"));
  247. while (current->result[j]) {
  248. printTimestamp(current->result[j]);
  249. ++j;
  250. }
  251. fprintf(stderr, "}");
  252. UA_DataValue *result = NULL;
  253. size_t resultSize = 0;
  254. UA_ByteString continuous;
  255. UA_ByteString_init(&continuous);
  256. UA_Boolean readOk = true;
  257. size_t reseivedValues = 0;
  258. fprintf(stderr, "{");
  259. size_t counter = 0;
  260. do {
  261. UA_HistoryReadResponse response;
  262. UA_HistoryReadResponse_init(&response);
  263. UA_UInt32 numValuesPerNode = current->numValuesPerNode;
  264. if (numValuesPerNode > 0 && numValuesPerNode + (UA_UInt32)reseivedValues > current->numValuesPerNode)
  265. numValuesPerNode = current->numValuesPerNode - (UA_UInt32)reseivedValues;
  266. requestHistory(current->start,
  267. current->end,
  268. &response,
  269. numValuesPerNode,
  270. current->returnBounds,
  271. &continuous);
  272. ++counter;
  273. if(response.resultsSize != 1) {
  274. fprintf(stderr, "ResultError:Size %lu %s", response.resultsSize, UA_StatusCode_name(response.responseHeader.serviceResult));
  275. readOk = false;
  276. UA_HistoryReadResponse_deleteMembers(&response);
  277. break;
  278. }
  279. UA_StatusCode stat = response.results[0].statusCode;
  280. if (stat == UA_STATUSCODE_BADBOUNDNOTSUPPORTED && current->returnBounds) {
  281. fprintf(stderr, "%s", UA_StatusCode_name(stat));
  282. UA_HistoryReadResponse_deleteMembers(&response);
  283. break;
  284. }
  285. if(response.results[0].historyData.encoding != UA_EXTENSIONOBJECT_DECODED
  286. || response.results[0].historyData.content.decoded.type != &UA_TYPES[UA_TYPES_HISTORYDATA]) {
  287. fprintf(stderr, "ResultError:HistoryData");
  288. readOk = false;
  289. UA_HistoryReadResponse_deleteMembers(&response);
  290. break;
  291. }
  292. UA_HistoryData * data = (UA_HistoryData *)response.results[0].historyData.content.decoded.data;
  293. resultSize = data->dataValuesSize;
  294. result = data->dataValues;
  295. if (resultSize == 0 && continuous.length > 0) {
  296. fprintf(stderr, "continuousResultEmpty");
  297. readOk = false;
  298. UA_HistoryReadResponse_deleteMembers(&response);
  299. break;
  300. }
  301. if (resultSize > maxResponseSize) {
  302. fprintf(stderr, "resultToBig");
  303. readOk = false;
  304. UA_HistoryReadResponse_deleteMembers(&response);
  305. break;
  306. }
  307. if (stat != UA_STATUSCODE_GOOD) {
  308. fprintf(stderr, "%s", UA_StatusCode_name(stat));
  309. } else {
  310. for (size_t k = 0; k < resultSize; ++k)
  311. printResult(&result[k]);
  312. }
  313. if (stat == UA_STATUSCODE_GOOD && j >= resultSize + reseivedValues) {
  314. for (size_t l = 0; l < resultSize; ++l) {
  315. /* See OPC UA Part 11, Version 1.03, Page 5-6, Table 1, Mark a for details.*/
  316. if (current->result[l + reseivedValues] == TIMESTAMP_LAST && current->end == TIMESTAMP_UNSPECIFIED) {
  317. // This test will work on not continous read, only
  318. if (reseivedValues == 0 && !(l > 0 && result[l].sourceTimestamp == result[l-1].sourceTimestamp + UA_DATETIME_SEC))
  319. readOk = false;
  320. }
  321. /* See OPC UA Part 11, Version 1.03, Page 5-6, Table 1, Mark b for details.*/
  322. if (current->result[l + reseivedValues] == TIMESTAMP_FIRST && current->start == TIMESTAMP_UNSPECIFIED) {
  323. // This test will work on not continous read, only
  324. if (reseivedValues == 0 && !(l > 0 && result[l].sourceTimestamp == result[l-1].sourceTimestamp - UA_DATETIME_SEC))
  325. readOk = false;
  326. }
  327. if (!resultIsEqual(&result[l], current, l + reseivedValues))
  328. readOk = false;
  329. }
  330. if (response.results[0].continuationPoint.length > 0)
  331. fprintf(stderr, "C,");
  332. reseivedValues += resultSize;
  333. if (reseivedValues == j) {
  334. if (current->returnContinuationPoint && response.results[0].continuationPoint.length == 0) {
  335. readOk = false;
  336. fprintf(stderr, "missingContinuationPoint");
  337. }
  338. if (!current->returnContinuationPoint && response.results[0].continuationPoint.length > 0) {
  339. readOk = false;
  340. fprintf(stderr, "unexpectedContinuationPoint");
  341. }
  342. UA_HistoryReadResponse_deleteMembers(&response);
  343. break;
  344. }
  345. UA_ByteString_deleteMembers(&continuous);
  346. UA_ByteString_copy(&response.results[0].continuationPoint, &continuous);
  347. } else {
  348. readOk = false;
  349. UA_HistoryReadResponse_deleteMembers(&response);
  350. break;
  351. }
  352. UA_HistoryReadResponse_deleteMembers(&response);
  353. } while (continuous.length > 0);
  354. if (j != reseivedValues) {
  355. readOk = false;
  356. }
  357. UA_ByteString_deleteMembers(&continuous);
  358. if (!readOk) {
  359. fprintf(stderr, "} Fail (%lu requests)\n", counter);
  360. ++retval;
  361. } else {
  362. fprintf(stderr, "} OK (%lu requests)\n", counter);
  363. }
  364. current = &testRequests[++i];
  365. }
  366. return retval;
  367. }
  368. START_TEST(Server_HistorizingStrategyUser)
  369. {
  370. // set a data backend
  371. UA_HistorizingNodeIdSettings setting;
  372. setting.historizingBackend = UA_HistoryDataBackend_Memory(3, 100);
  373. setting.maxHistoryDataResponseSize = 100;
  374. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
  375. UA_StatusCode retval = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  376. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  377. // fill the data
  378. UA_DateTime start = UA_DateTime_now();
  379. UA_DateTime end = start + (10 * UA_DATETIME_SEC);
  380. for (UA_UInt32 i = 0; i < 10; ++i) {
  381. UA_DataValue value;
  382. UA_DataValue_init(&value);
  383. value.hasValue = true;
  384. value.hasStatus = true;
  385. value.status = UA_STATUSCODE_GOOD;
  386. UA_Variant_setScalarCopy(&value.value, &i, &UA_TYPES[UA_TYPES_UINT32]);
  387. value.hasSourceTimestamp = true;
  388. value.sourceTimestamp = start + (i * UA_DATETIME_SEC);
  389. retval = setting.historizingBackend.serverSetHistoryData(server,
  390. setting.historizingBackend.context,
  391. NULL,
  392. NULL,
  393. &outNodeId,
  394. UA_FALSE,
  395. &value);
  396. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  397. UA_DataValue_deleteMembers(&value);
  398. }
  399. // request
  400. UA_HistoryReadResponse response;
  401. UA_HistoryReadResponse_init(&response);
  402. requestHistory(start, end, &response, 0, false, NULL);
  403. // test the response
  404. ck_assert_str_eq(UA_StatusCode_name(response.responseHeader.serviceResult), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  405. ck_assert_uint_eq(response.resultsSize, 1);
  406. for (size_t i = 0; i < response.resultsSize; ++i) {
  407. ck_assert_str_eq(UA_StatusCode_name(response.results[i].statusCode), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  408. ck_assert_uint_eq(response.results[i].historyData.encoding, UA_EXTENSIONOBJECT_DECODED);
  409. ck_assert(response.results[i].historyData.content.decoded.type == &UA_TYPES[UA_TYPES_HISTORYDATA]);
  410. UA_HistoryData * data = (UA_HistoryData *)response.results[i].historyData.content.decoded.data;
  411. ck_assert_uint_eq(data->dataValuesSize, 10);
  412. for (size_t j = 0; j < data->dataValuesSize; ++j) {
  413. ck_assert_uint_eq(data->dataValues[j].hasSourceTimestamp, true);
  414. ck_assert_uint_eq(data->dataValues[j].sourceTimestamp, start + (j * UA_DATETIME_SEC));
  415. ck_assert_uint_eq(data->dataValues[j].hasStatus, true);
  416. ck_assert_str_eq(UA_StatusCode_name(data->dataValues[j].status), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  417. ck_assert_uint_eq(data->dataValues[j].hasValue, true);
  418. ck_assert(data->dataValues[j].value.type == &UA_TYPES[UA_TYPES_UINT32]);
  419. UA_UInt32 * value = (UA_UInt32 *)data->dataValues[j].value.data;
  420. ck_assert_uint_eq(*value, j);
  421. }
  422. }
  423. UA_HistoryReadResponse_deleteMembers(&response);
  424. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  425. }
  426. END_TEST
  427. START_TEST(Server_HistorizingStrategyPoll)
  428. {
  429. // init to a defined value
  430. UA_StatusCode retval = setUInt32(client, outNodeId, 43);
  431. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  432. // set a data backend
  433. UA_HistorizingNodeIdSettings setting;
  434. setting.historizingBackend = UA_HistoryDataBackend_Memory(3, 100);
  435. setting.maxHistoryDataResponseSize = 100;
  436. setting.pollingInterval = 100;
  437. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_POLL;
  438. retval = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  439. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  440. // fill the data
  441. UA_DateTime start = UA_DateTime_now();
  442. retval = gathering->startPoll(server, gathering->context, &outNodeId);
  443. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  444. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  445. for (size_t k = 0; k < 10; ++k) {
  446. UA_fakeSleep(50);
  447. UA_realSleep(50);
  448. if (k == 5) {
  449. gathering->stopPoll(server, gathering->context, &outNodeId);
  450. }
  451. setUInt32(client, outNodeId, (unsigned int)k);
  452. }
  453. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  454. UA_DateTime end = UA_DateTime_now();
  455. // request
  456. UA_HistoryReadResponse response;
  457. UA_HistoryReadResponse_init(&response);
  458. requestHistory(start, end, &response, 0, false, NULL);
  459. // test the response
  460. ck_assert_str_eq(UA_StatusCode_name(response.responseHeader.serviceResult), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  461. ck_assert_uint_eq(response.resultsSize, 1);
  462. for (size_t i = 0; i < response.resultsSize; ++i) {
  463. ck_assert_str_eq(UA_StatusCode_name(response.results[i].statusCode), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  464. ck_assert_uint_eq(response.results[i].historyData.encoding, UA_EXTENSIONOBJECT_DECODED);
  465. ck_assert(response.results[i].historyData.content.decoded.type == &UA_TYPES[UA_TYPES_HISTORYDATA]);
  466. UA_HistoryData * data = (UA_HistoryData *)response.results[i].historyData.content.decoded.data;
  467. ck_assert(data->dataValuesSize > 1);
  468. for (size_t j = 0; j < data->dataValuesSize; ++j) {
  469. ck_assert_uint_eq(data->dataValues[j].hasSourceTimestamp, true);
  470. ck_assert(data->dataValues[j].sourceTimestamp >= start);
  471. ck_assert(data->dataValues[j].sourceTimestamp < end);
  472. ck_assert_uint_eq(data->dataValues[j].hasValue, true);
  473. ck_assert(data->dataValues[j].value.type == &UA_TYPES[UA_TYPES_UINT32]);
  474. UA_UInt32 * value = (UA_UInt32 *)data->dataValues[j].value.data;
  475. // first need to be 43
  476. if (j == 0) {
  477. ck_assert(*value == 43);
  478. } else {
  479. ck_assert(*value < 5);
  480. }
  481. }
  482. }
  483. UA_HistoryReadResponse_deleteMembers(&response);
  484. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  485. }
  486. END_TEST
  487. START_TEST(Server_HistorizingStrategyValueSet)
  488. {
  489. // init to a defined value
  490. UA_StatusCode retval = setUInt32(client, outNodeId, 43);
  491. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  492. // set a data backend
  493. UA_HistorizingNodeIdSettings setting;
  494. setting.historizingBackend = UA_HistoryDataBackend_Memory(3, 100);
  495. setting.maxHistoryDataResponseSize = 100;
  496. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_VALUESET;
  497. retval = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  498. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  499. // fill the data
  500. UA_fakeSleep(100);
  501. UA_DateTime start = UA_DateTime_now();
  502. UA_fakeSleep(100);
  503. for (UA_UInt32 i = 0; i < 10; ++i) {
  504. retval = setUInt32(client, outNodeId, i);
  505. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  506. UA_fakeSleep(100);
  507. }
  508. UA_DateTime end = UA_DateTime_now();
  509. // request
  510. UA_HistoryReadResponse response;
  511. UA_HistoryReadResponse_init(&response);
  512. requestHistory(start, end, &response, 0, false, NULL);
  513. // test the response
  514. ck_assert_str_eq(UA_StatusCode_name(response.responseHeader.serviceResult), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  515. ck_assert_uint_eq(response.resultsSize, 1);
  516. for (size_t i = 0; i < response.resultsSize; ++i) {
  517. ck_assert_str_eq(UA_StatusCode_name(response.results[i].statusCode), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  518. ck_assert_uint_eq(response.results[i].historyData.encoding, UA_EXTENSIONOBJECT_DECODED);
  519. ck_assert(response.results[i].historyData.content.decoded.type == &UA_TYPES[UA_TYPES_HISTORYDATA]);
  520. UA_HistoryData * data = (UA_HistoryData *)response.results[i].historyData.content.decoded.data;
  521. ck_assert(data->dataValuesSize > 0);
  522. for (size_t j = 0; j < data->dataValuesSize; ++j) {
  523. ck_assert(data->dataValues[j].sourceTimestamp >= start && data->dataValues[j].sourceTimestamp < end);
  524. ck_assert_uint_eq(data->dataValues[j].hasSourceTimestamp, true);
  525. ck_assert_str_eq(UA_StatusCode_name(data->dataValues[j].status), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  526. ck_assert_uint_eq(data->dataValues[j].hasValue, true);
  527. ck_assert(data->dataValues[j].value.type == &UA_TYPES[UA_TYPES_UINT32]);
  528. UA_UInt32 * value = (UA_UInt32 *)data->dataValues[j].value.data;
  529. ck_assert_uint_eq(*value, j);
  530. }
  531. }
  532. UA_HistoryReadResponse_deleteMembers(&response);
  533. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  534. }
  535. END_TEST
  536. START_TEST(Server_HistorizingBackendMemory)
  537. {
  538. UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
  539. UA_HistorizingNodeIdSettings setting;
  540. setting.historizingBackend = backend;
  541. setting.maxHistoryDataResponseSize = 1000;
  542. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
  543. UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  544. ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  545. // empty backend should not crash
  546. UA_UInt32 retval = testHistoricalDataBackend(100);
  547. fprintf(stderr, "%d tests expected failed.\n", retval);
  548. // fill backend
  549. ck_assert_uint_eq(fillHistoricalDataBackend(backend), true);
  550. // read all in one
  551. retval = testHistoricalDataBackend(100);
  552. fprintf(stderr, "%d tests failed.\n", retval);
  553. ck_assert_uint_eq(retval, 0);
  554. // read continuous one at one request
  555. retval = testHistoricalDataBackend(1);
  556. fprintf(stderr, "%d tests failed.\n", retval);
  557. ck_assert_uint_eq(retval, 0);
  558. // read continuous two at one request
  559. retval = testHistoricalDataBackend(2);
  560. fprintf(stderr, "%d tests failed.\n", retval);
  561. ck_assert_uint_eq(retval, 0);
  562. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  563. }
  564. END_TEST
  565. #endif /*UA_ENABLE_HISTORIZING*/
  566. static Suite* testSuite_Client(void)
  567. {
  568. Suite *s = suite_create("Server Historical Data");
  569. TCase *tc_server = tcase_create("Server Historical Data Basic");
  570. tcase_add_checked_fixture(tc_server, setup, teardown);
  571. #ifdef UA_ENABLE_HISTORIZING
  572. tcase_add_test(tc_server, Server_HistorizingStrategyPoll);
  573. tcase_add_test(tc_server, Server_HistorizingStrategyUser);
  574. tcase_add_test(tc_server, Server_HistorizingStrategyValueSet);
  575. tcase_add_test(tc_server, Server_HistorizingBackendMemory);
  576. #endif /* UA_ENABLE_HISTORIZING */
  577. suite_add_tcase(s, tc_server);
  578. return s;
  579. }
  580. int main(void)
  581. {
  582. Suite *s = testSuite_Client();
  583. SRunner *sr = srunner_create(s);
  584. srunner_set_fork_status(sr, CK_NOFORK);
  585. srunner_run_all(sr,CK_NORMAL);
  586. int number_failed = srunner_ntests_failed(sr);
  587. srunner_free(sr);
  588. return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
  589. }