check_historical_data.c 27 KB

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