check_server_historical_data.c 29 KB

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