check_server_historical_data.c 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055
  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 <open62541/client.h>
  8. #include <open62541/client_config_default.h>
  9. #include <open62541/client_highlevel.h>
  10. #include <open62541/plugin/historydata/history_data_backend.h>
  11. #include <open62541/plugin/historydata/history_data_backend_memory.h>
  12. #include <open62541/plugin/historydata/history_data_gathering_default.h>
  13. #include <open62541/plugin/historydata/history_database_default.h>
  14. #include <open62541/plugin/historydatabase.h>
  15. #include <open62541/server.h>
  16. #include <open62541/server_config_default.h>
  17. #include "client/ua_client_internal.h"
  18. #include "server/ua_server_internal.h"
  19. #include <check.h>
  20. #include "testing_clock.h"
  21. #include "testing_networklayers.h"
  22. #include "thread_wrapper.h"
  23. #ifdef UA_ENABLE_HISTORIZING
  24. #include "historical_read_test_data.h"
  25. #include "randomindextest_backend.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 UA_DateTime *testDataSorted;
  41. static void serverMutexLock(void) {
  42. if (!(MUTEX_LOCK(serverMutex))) {
  43. fprintf(stderr, "Mutex cannot be locked.\n");
  44. exit(1);
  45. }
  46. }
  47. static void serverMutexUnlock(void) {
  48. if (!(MUTEX_UNLOCK(serverMutex))) {
  49. fprintf(stderr, "Mutex cannot be unlocked.\n");
  50. exit(1);
  51. }
  52. }
  53. THREAD_CALLBACK(serverloop)
  54. {
  55. while(running) {
  56. serverMutexLock();
  57. UA_Server_run_iterate(server, false);
  58. serverMutexUnlock();
  59. }
  60. return 0;
  61. }
  62. static void
  63. setup(void)
  64. {
  65. if (!(MUTEX_INIT(serverMutex))) {
  66. fprintf(stderr, "Server mutex was not created correctly.\n");
  67. exit(1);
  68. }
  69. running = true;
  70. config = UA_ServerConfig_new_default();
  71. #ifdef UA_ENABLE_HISTORIZING
  72. gathering = (UA_HistoryDataGathering*)UA_calloc(1, sizeof(UA_HistoryDataGathering));
  73. *gathering = UA_HistoryDataGathering_Default(1);
  74. config->historyDatabase = UA_HistoryDatabase_default(*gathering);
  75. #endif
  76. server = UA_Server_new(config);
  77. UA_StatusCode retval = UA_Server_run_startup(server);
  78. if (retval != UA_STATUSCODE_GOOD)
  79. {
  80. fprintf(stderr, "Error while calling Server_run_startup. %s\n", UA_StatusCode_name(retval));
  81. exit(1);
  82. }
  83. THREAD_CREATE(server_thread, serverloop);
  84. /* Define the attribute of the uint32 variable node */
  85. UA_VariableAttributes attr = UA_VariableAttributes_default;
  86. UA_UInt32 myUint32 = 40;
  87. UA_Variant_setScalar(&attr.value, &myUint32, &UA_TYPES[UA_TYPES_UINT32]);
  88. attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
  89. attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
  90. attr.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
  91. attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_HISTORYREAD | UA_ACCESSLEVELMASK_HISTORYWRITE;
  92. attr.historizing = true;
  93. /* Add the variable node to the information model */
  94. UA_NodeId uint32NodeId = UA_NODEID_STRING(1, "the.answer");
  95. UA_QualifiedName uint32Name = UA_QUALIFIEDNAME(1, "the answer");
  96. parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  97. parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
  98. UA_NodeId_init(&outNodeId);
  99. retval = UA_Server_addVariableNode(server,
  100. uint32NodeId,
  101. parentNodeId,
  102. parentReferenceNodeId,
  103. uint32Name,
  104. UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
  105. attr,
  106. NULL,
  107. &outNodeId);
  108. if (retval != UA_STATUSCODE_GOOD)
  109. {
  110. fprintf(stderr, "Error adding variable node. %s\n", UA_StatusCode_name(retval));
  111. exit(1);
  112. }
  113. client = UA_Client_new();
  114. UA_ClientConfig_setDefault(UA_Client_getConfig(client));
  115. retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
  116. if (retval != UA_STATUSCODE_GOOD)
  117. {
  118. fprintf(stderr, "Client can not connect to opc.tcp://localhost:4840. %s\n", UA_StatusCode_name(retval));
  119. exit(1);
  120. }
  121. UA_Client_recv = client->connection.recv;
  122. client->connection.recv = UA_Client_recvTesting;
  123. }
  124. static void
  125. teardown(void)
  126. {
  127. /* cleanup */
  128. UA_Client_disconnect(client);
  129. UA_Client_delete(client);
  130. running = false;
  131. THREAD_JOIN(server_thread);
  132. UA_NodeId_deleteMembers(&parentNodeId);
  133. UA_NodeId_deleteMembers(&parentReferenceNodeId);
  134. UA_NodeId_deleteMembers(&outNodeId);
  135. UA_Server_run_shutdown(server);
  136. UA_Server_delete(server);
  137. UA_ServerConfig_delete(config);
  138. #ifdef UA_ENABLE_HISTORIZING
  139. UA_free(gathering);
  140. #endif
  141. if (!MUTEX_DESTROY(serverMutex)) {
  142. fprintf(stderr, "Server mutex was not destroyed correctly.\n");
  143. exit(1);
  144. }
  145. }
  146. #ifdef UA_ENABLE_HISTORIZING
  147. #include <stdio.h>
  148. #include "ua_session.h"
  149. static UA_StatusCode
  150. setUInt32(UA_Client *thisClient, UA_NodeId node, UA_UInt32 value)
  151. {
  152. UA_Variant variant;
  153. UA_Variant_setScalar(&variant, &value, &UA_TYPES[UA_TYPES_UINT32]);
  154. return UA_Client_writeValueAttribute(thisClient, node, &variant);
  155. }
  156. static UA_DateTime* sortDateTimes(UA_DateTime *data) {
  157. size_t count = 0;
  158. while(data[count++]);
  159. UA_DateTime* ret;
  160. if (UA_Array_copy(data, count, (void**)&ret, &UA_TYPES[UA_TYPES_DATETIME]) != UA_STATUSCODE_GOOD)
  161. return NULL;
  162. --count;
  163. // sort it
  164. for (size_t i = 1; i < count; i++) {
  165. for (size_t j = 0; j < count - i; j++) {
  166. if (ret[j] > ret[j+1]) {
  167. UA_DateTime tmp = ret[j];
  168. ret[j] = ret[j+1];
  169. ret[j+1] = tmp;
  170. }
  171. }
  172. }
  173. return ret;
  174. }
  175. static void
  176. printTimestamp(UA_DateTime timestamp)
  177. {
  178. if (timestamp == TIMESTAMP_FIRST) {
  179. fprintf(stderr, "FIRST,");
  180. } else if (timestamp == TIMESTAMP_LAST) {
  181. fprintf(stderr, "LAST,");
  182. } else {
  183. fprintf(stderr, "%3lld,", timestamp / UA_DATETIME_SEC);
  184. }
  185. }
  186. static void
  187. printResult(UA_DataValue * value)
  188. {
  189. if (value->status != UA_STATUSCODE_GOOD)
  190. fprintf(stderr, "%s:", UA_StatusCode_name(value->status));
  191. printTimestamp(value->sourceTimestamp);
  192. }
  193. static UA_Boolean
  194. resultIsEqual(const UA_DataValue * result, const testTuple * tuple, size_t index)
  195. {
  196. switch (tuple->result[index]) {
  197. case TIMESTAMP_FIRST:
  198. if (result->status != UA_STATUSCODE_BADBOUNDNOTFOUND
  199. || !UA_Variant_isEmpty(&result->value))
  200. return false;
  201. /* we do not test timestamp if TIMESTAMP_UNSPECIFIED is given for start.
  202. * See OPC UA Part 11, Version 1.03, Page 5-6, Table 1, Mark b for details.*/
  203. if (tuple->start != TIMESTAMP_UNSPECIFIED
  204. && tuple->start != result->sourceTimestamp)
  205. return false;
  206. break;
  207. case TIMESTAMP_LAST:
  208. if (result->status != UA_STATUSCODE_BADBOUNDNOTFOUND
  209. || !UA_Variant_isEmpty(&result->value))
  210. return false;
  211. /* we do not test timestamp if TIMESTAMP_UNSPECIFIED is given for end.
  212. * See OPC UA Part 11, Version 1.03, Page 5-6, Table 1, Mark a for details.*/
  213. if (tuple->end != TIMESTAMP_UNSPECIFIED
  214. && tuple->end != result->sourceTimestamp)
  215. return false;
  216. break;
  217. default:
  218. if (result->sourceTimestamp != tuple->result[index]
  219. || result->value.type != &UA_TYPES[UA_TYPES_INT64]
  220. || *((UA_Int64*)result->value.data) != tuple->result[index])
  221. return false;
  222. }
  223. return true;
  224. }
  225. static UA_Boolean
  226. fillHistoricalDataBackend(UA_HistoryDataBackend backend)
  227. {
  228. int i = 0;
  229. UA_DateTime currentDateTime = testData[i];
  230. fprintf(stderr, "Adding to historical data backend: ");
  231. while (currentDateTime) {
  232. fprintf(stderr, "%lld, ", currentDateTime / UA_DATETIME_SEC);
  233. UA_DataValue value;
  234. UA_DataValue_init(&value);
  235. value.hasValue = true;
  236. UA_Int64 d = currentDateTime;
  237. UA_Variant_setScalarCopy(&value.value, &d, &UA_TYPES[UA_TYPES_INT64]);
  238. value.hasSourceTimestamp = true;
  239. value.sourceTimestamp = currentDateTime;
  240. value.hasServerTimestamp = true;
  241. value.serverTimestamp = currentDateTime;
  242. value.hasStatus = true;
  243. value.status = UA_STATUSCODE_GOOD;
  244. if (backend.serverSetHistoryData(server, backend.context, NULL, NULL, &outNodeId, UA_FALSE, &value) != UA_STATUSCODE_GOOD) {
  245. fprintf(stderr, "\n");
  246. return false;
  247. }
  248. UA_DataValue_deleteMembers(&value);
  249. currentDateTime = testData[++i];
  250. }
  251. fprintf(stderr, "\n");
  252. return true;
  253. }
  254. void
  255. Service_HistoryRead(UA_Server *server, UA_Session *session,
  256. const UA_HistoryReadRequest *request,
  257. UA_HistoryReadResponse *response);
  258. static void
  259. requestHistory(UA_DateTime start,
  260. UA_DateTime end,
  261. UA_HistoryReadResponse * response,
  262. UA_UInt32 numValuesPerNode,
  263. UA_Boolean returnBounds,
  264. UA_ByteString *continuationPoint)
  265. {
  266. UA_ReadRawModifiedDetails *details = UA_ReadRawModifiedDetails_new();
  267. details->startTime = start;
  268. details->endTime = end;
  269. details->isReadModified = false;
  270. details->numValuesPerNode = numValuesPerNode;
  271. details->returnBounds = returnBounds;
  272. UA_HistoryReadValueId *valueId = UA_HistoryReadValueId_new();
  273. UA_NodeId_copy(&outNodeId, &valueId->nodeId);
  274. if (continuationPoint)
  275. UA_ByteString_copy(continuationPoint, &valueId->continuationPoint);
  276. UA_HistoryReadRequest request;
  277. UA_HistoryReadRequest_init(&request);
  278. request.historyReadDetails.encoding = UA_EXTENSIONOBJECT_DECODED;
  279. request.historyReadDetails.content.decoded.type = &UA_TYPES[UA_TYPES_READRAWMODIFIEDDETAILS];
  280. request.historyReadDetails.content.decoded.data = details;
  281. request.timestampsToReturn = UA_TIMESTAMPSTORETURN_BOTH;
  282. request.nodesToReadSize = 1;
  283. request.nodesToRead = valueId;
  284. Service_HistoryRead(server, &server->adminSession, &request, response);
  285. UA_HistoryReadRequest_deleteMembers(&request);
  286. }
  287. static UA_UInt32
  288. testHistoricalDataBackend(size_t maxResponseSize)
  289. {
  290. const UA_HistorizingNodeIdSettings* setting = gathering->getHistorizingSetting(server, gathering->context, &outNodeId);
  291. UA_HistorizingNodeIdSettings newSetting = *setting;
  292. newSetting.maxHistoryDataResponseSize = maxResponseSize;
  293. gathering->updateNodeIdSetting(server, gathering->context, &outNodeId, newSetting);
  294. UA_UInt32 retval = 0;
  295. size_t i = 0;
  296. testTuple *current = &testRequests[i];
  297. fprintf(stderr, "Testing with maxResponseSize of %lu\n", maxResponseSize);
  298. fprintf(stderr, "Start | End | numValuesPerNode | returnBounds |ContPoint| {Expected}{Result} Result\n");
  299. fprintf(stderr, "------+------+------------------+--------------+---------+----------------\n");
  300. size_t j;
  301. while (current->start || current->end) {
  302. j = 0;
  303. if (current->start == TIMESTAMP_UNSPECIFIED) {
  304. fprintf(stderr, "UNSPEC|");
  305. } else {
  306. fprintf(stderr, " %3lld |", current->start / UA_DATETIME_SEC);
  307. }
  308. if (current->end == TIMESTAMP_UNSPECIFIED) {
  309. fprintf(stderr, "UNSPEC|");
  310. } else {
  311. fprintf(stderr, " %3lld |", current->end / UA_DATETIME_SEC);
  312. }
  313. fprintf(stderr, " %2u | %s | %s | {", current->numValuesPerNode, (current->returnBounds ? "Yes" : " No"), (current->returnContinuationPoint ? "Yes" : " No"));
  314. while (current->result[j]) {
  315. printTimestamp(current->result[j]);
  316. ++j;
  317. }
  318. fprintf(stderr, "}");
  319. UA_DataValue *result = NULL;
  320. size_t resultSize = 0;
  321. UA_ByteString continuous;
  322. UA_ByteString_init(&continuous);
  323. UA_Boolean readOk = true;
  324. size_t reseivedValues = 0;
  325. fprintf(stderr, "{");
  326. size_t counter = 0;
  327. do {
  328. UA_HistoryReadResponse response;
  329. UA_HistoryReadResponse_init(&response);
  330. UA_UInt32 numValuesPerNode = current->numValuesPerNode;
  331. if (numValuesPerNode > 0 && numValuesPerNode + (UA_UInt32)reseivedValues > current->numValuesPerNode)
  332. numValuesPerNode = current->numValuesPerNode - (UA_UInt32)reseivedValues;
  333. requestHistory(current->start,
  334. current->end,
  335. &response,
  336. numValuesPerNode,
  337. current->returnBounds,
  338. &continuous);
  339. ++counter;
  340. if(response.resultsSize != 1) {
  341. fprintf(stderr, "ResultError:Size %lu %s", response.resultsSize, UA_StatusCode_name(response.responseHeader.serviceResult));
  342. readOk = false;
  343. UA_HistoryReadResponse_deleteMembers(&response);
  344. break;
  345. }
  346. UA_StatusCode stat = response.results[0].statusCode;
  347. if (stat == UA_STATUSCODE_BADBOUNDNOTSUPPORTED && current->returnBounds) {
  348. fprintf(stderr, "%s", UA_StatusCode_name(stat));
  349. UA_HistoryReadResponse_deleteMembers(&response);
  350. break;
  351. }
  352. if(response.results[0].historyData.encoding != UA_EXTENSIONOBJECT_DECODED
  353. || response.results[0].historyData.content.decoded.type != &UA_TYPES[UA_TYPES_HISTORYDATA]) {
  354. fprintf(stderr, "ResultError:HistoryData");
  355. readOk = false;
  356. UA_HistoryReadResponse_deleteMembers(&response);
  357. break;
  358. }
  359. UA_HistoryData * data = (UA_HistoryData *)response.results[0].historyData.content.decoded.data;
  360. resultSize = data->dataValuesSize;
  361. result = data->dataValues;
  362. if (resultSize == 0 && continuous.length > 0) {
  363. fprintf(stderr, "continuousResultEmpty");
  364. readOk = false;
  365. UA_HistoryReadResponse_deleteMembers(&response);
  366. break;
  367. }
  368. if (resultSize > maxResponseSize) {
  369. fprintf(stderr, "resultToBig");
  370. readOk = false;
  371. UA_HistoryReadResponse_deleteMembers(&response);
  372. break;
  373. }
  374. if (stat != UA_STATUSCODE_GOOD) {
  375. fprintf(stderr, "%s", UA_StatusCode_name(stat));
  376. } else {
  377. for (size_t k = 0; k < resultSize; ++k)
  378. printResult(&result[k]);
  379. }
  380. if (stat == UA_STATUSCODE_GOOD && j >= resultSize + reseivedValues) {
  381. for (size_t l = 0; l < resultSize; ++l) {
  382. /* See OPC UA Part 11, Version 1.03, Page 5-6, Table 1, Mark a for details.*/
  383. if (current->result[l + reseivedValues] == TIMESTAMP_LAST && current->end == TIMESTAMP_UNSPECIFIED) {
  384. // This test will work on not continous read, only
  385. if (reseivedValues == 0 && !(l > 0 && result[l].sourceTimestamp == result[l-1].sourceTimestamp + UA_DATETIME_SEC))
  386. readOk = false;
  387. }
  388. /* See OPC UA Part 11, Version 1.03, Page 5-6, Table 1, Mark b for details.*/
  389. if (current->result[l + reseivedValues] == TIMESTAMP_FIRST && current->start == TIMESTAMP_UNSPECIFIED) {
  390. // This test will work on not continous read, only
  391. if (reseivedValues == 0 && !(l > 0 && result[l].sourceTimestamp == result[l-1].sourceTimestamp - UA_DATETIME_SEC))
  392. readOk = false;
  393. }
  394. if (!resultIsEqual(&result[l], current, l + reseivedValues))
  395. readOk = false;
  396. }
  397. if (response.results[0].continuationPoint.length > 0)
  398. fprintf(stderr, "C,");
  399. reseivedValues += resultSize;
  400. if (reseivedValues == j) {
  401. if (current->returnContinuationPoint && response.results[0].continuationPoint.length == 0) {
  402. readOk = false;
  403. fprintf(stderr, "missingContinuationPoint");
  404. }
  405. if (!current->returnContinuationPoint && response.results[0].continuationPoint.length > 0) {
  406. readOk = false;
  407. fprintf(stderr, "unexpectedContinuationPoint");
  408. }
  409. UA_HistoryReadResponse_deleteMembers(&response);
  410. break;
  411. }
  412. UA_ByteString_deleteMembers(&continuous);
  413. UA_ByteString_copy(&response.results[0].continuationPoint, &continuous);
  414. } else {
  415. readOk = false;
  416. UA_HistoryReadResponse_deleteMembers(&response);
  417. break;
  418. }
  419. UA_HistoryReadResponse_deleteMembers(&response);
  420. } while (continuous.length > 0);
  421. if (j != reseivedValues) {
  422. readOk = false;
  423. }
  424. UA_ByteString_deleteMembers(&continuous);
  425. if (!readOk) {
  426. fprintf(stderr, "} Fail (%lu requests)\n", counter);
  427. ++retval;
  428. } else {
  429. fprintf(stderr, "} OK (%lu requests)\n", counter);
  430. }
  431. current = &testRequests[++i];
  432. }
  433. return retval;
  434. }
  435. void
  436. Service_HistoryUpdate(UA_Server *server, UA_Session *session,
  437. const UA_HistoryUpdateRequest *request,
  438. UA_HistoryUpdateResponse *response);
  439. static UA_StatusCode
  440. deleteHistory(UA_DateTime start,
  441. UA_DateTime end)
  442. {
  443. UA_DeleteRawModifiedDetails *details = UA_DeleteRawModifiedDetails_new();
  444. details->startTime = start;
  445. details->endTime = end;
  446. details->isDeleteModified = false;
  447. UA_NodeId_copy(&outNodeId, &details->nodeId);
  448. UA_HistoryUpdateRequest request;
  449. UA_HistoryUpdateRequest_init(&request);
  450. request.historyUpdateDetailsSize = 1;
  451. request.historyUpdateDetails = UA_ExtensionObject_new();
  452. UA_ExtensionObject_init(request.historyUpdateDetails);
  453. request.historyUpdateDetails[0].encoding = UA_EXTENSIONOBJECT_DECODED;
  454. request.historyUpdateDetails[0].content.decoded.type = &UA_TYPES[UA_TYPES_DELETERAWMODIFIEDDETAILS];
  455. request.historyUpdateDetails[0].content.decoded.data = details;
  456. UA_HistoryUpdateResponse response;
  457. UA_HistoryUpdateResponse_init(&response);
  458. Service_HistoryUpdate(server, &server->adminSession, &request, &response);
  459. UA_HistoryUpdateRequest_deleteMembers(&request);
  460. UA_StatusCode ret = UA_STATUSCODE_GOOD;
  461. if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
  462. ret = response.responseHeader.serviceResult;
  463. else if (response.resultsSize != 1)
  464. ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
  465. else if (response.results[0].statusCode != UA_STATUSCODE_GOOD)
  466. ret = response.results[0].statusCode;
  467. else if (response.results[0].operationResultsSize != 0)
  468. ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
  469. UA_HistoryUpdateResponse_deleteMembers(&response);
  470. return ret;
  471. }
  472. static UA_StatusCode
  473. updateHistory(UA_PerformUpdateType updateType, UA_DateTime *updateData, UA_StatusCode ** operationResults, size_t *operationResultsSize)
  474. {
  475. UA_UpdateDataDetails *details = UA_UpdateDataDetails_new();
  476. details->performInsertReplace = updateType;
  477. UA_NodeId_copy(&outNodeId, &details->nodeId);
  478. int updateDataSize = -1;
  479. while(updateData[++updateDataSize]);
  480. fprintf(stderr, "updateHistory for %d values.\n", updateDataSize);
  481. details->updateValuesSize = (size_t)updateDataSize;
  482. details->updateValues = (UA_DataValue*)UA_Array_new(details->updateValuesSize, &UA_TYPES[UA_TYPES_DATAVALUE]);
  483. for (size_t i = 0; i < details->updateValuesSize; ++i) {
  484. UA_DataValue_init(&details->updateValues[i]);
  485. details->updateValues[i].hasValue = true;
  486. UA_Int64 d = updateType;
  487. UA_Variant_setScalarCopy(&details->updateValues[i].value, &d, &UA_TYPES[UA_TYPES_INT64]);
  488. details->updateValues[i].hasSourceTimestamp = true;
  489. details->updateValues[i].sourceTimestamp = updateData[i];
  490. details->updateValues[i].hasServerTimestamp = true;
  491. details->updateValues[i].serverTimestamp = updateData[i];
  492. details->updateValues[i].hasStatus = true;
  493. details->updateValues[i].status = UA_STATUSCODE_GOOD;
  494. }
  495. UA_HistoryUpdateRequest request;
  496. UA_HistoryUpdateRequest_init(&request);
  497. request.historyUpdateDetailsSize = 1;
  498. request.historyUpdateDetails = UA_ExtensionObject_new();
  499. UA_ExtensionObject_init(request.historyUpdateDetails);
  500. request.historyUpdateDetails[0].encoding = UA_EXTENSIONOBJECT_DECODED;
  501. request.historyUpdateDetails[0].content.decoded.type = &UA_TYPES[UA_TYPES_UPDATEDATADETAILS];
  502. request.historyUpdateDetails[0].content.decoded.data = details;
  503. UA_HistoryUpdateResponse response;
  504. UA_HistoryUpdateResponse_init(&response);
  505. Service_HistoryUpdate(server, &server->adminSession, &request, &response);
  506. UA_HistoryUpdateRequest_deleteMembers(&request);
  507. UA_StatusCode ret = UA_STATUSCODE_GOOD;
  508. if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
  509. ret = response.responseHeader.serviceResult;
  510. else if (response.resultsSize != 1)
  511. ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
  512. else if (response.results[0].statusCode != UA_STATUSCODE_GOOD)
  513. ret = response.results[0].statusCode;
  514. else if (response.results[0].operationResultsSize != (size_t)updateDataSize)
  515. ret = UA_STATUSCODE_BADUNEXPECTEDERROR;
  516. else {
  517. if (operationResults) {
  518. *operationResultsSize = response.results[0].operationResultsSize;
  519. ret = UA_Array_copy(response.results[0].operationResults, *operationResultsSize, (void**)operationResults, &UA_TYPES[UA_TYPES_STATUSCODE]);
  520. } else {
  521. for (size_t i = 0; i < response.results[0].operationResultsSize; ++i) {
  522. if (response.results[0].operationResults[i] != UA_STATUSCODE_GOOD) {
  523. ret = response.results[0].operationResults[i];
  524. break;
  525. }
  526. }
  527. }
  528. }
  529. UA_HistoryUpdateResponse_deleteMembers(&response);
  530. return ret;
  531. }
  532. static void
  533. testResult(UA_DateTime *resultData, UA_HistoryData * historyData) {
  534. // request
  535. UA_HistoryReadResponse localResponse;
  536. UA_HistoryReadResponse_init(&localResponse);
  537. requestHistory(TIMESTAMP_FIRST, TIMESTAMP_LAST, &localResponse, 0, false, NULL);
  538. // test the response
  539. ck_assert_str_eq(UA_StatusCode_name(localResponse.responseHeader.serviceResult), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  540. ck_assert_uint_eq(localResponse.resultsSize, 1);
  541. ck_assert_str_eq(UA_StatusCode_name(localResponse.results[0].statusCode), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  542. ck_assert_uint_eq(localResponse.results[0].historyData.encoding, UA_EXTENSIONOBJECT_DECODED);
  543. ck_assert(localResponse.results[0].historyData.content.decoded.type == &UA_TYPES[UA_TYPES_HISTORYDATA]);
  544. UA_HistoryData * data = (UA_HistoryData *)localResponse.results[0].historyData.content.decoded.data;
  545. if (historyData)
  546. UA_HistoryData_copy(data, historyData);
  547. for (size_t j = 0; j < data->dataValuesSize; ++j) {
  548. ck_assert(resultData[j] != 0);
  549. ck_assert_uint_eq(data->dataValues[j].hasSourceTimestamp, true);
  550. ck_assert_uint_eq(data->dataValues[j].sourceTimestamp, resultData[j]);
  551. }
  552. UA_HistoryReadResponse_deleteMembers(&localResponse);
  553. }
  554. START_TEST(Server_HistorizingUpdateDelete)
  555. {
  556. UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
  557. UA_HistorizingNodeIdSettings setting;
  558. setting.historizingBackend = backend;
  559. setting.maxHistoryDataResponseSize = 1000;
  560. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
  561. serverMutexLock();
  562. UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  563. serverMutexUnlock();
  564. ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  565. // fill backend
  566. ck_assert_uint_eq(fillHistoricalDataBackend(backend), true);
  567. // delete some values
  568. ck_assert_str_eq(UA_StatusCode_name(deleteHistory(DELETE_START_TIME, DELETE_STOP_TIME)),
  569. UA_StatusCode_name(UA_STATUSCODE_GOOD));
  570. testResult(testDataAfterDelete, NULL);
  571. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  572. }
  573. END_TEST
  574. START_TEST(Server_HistorizingUpdateInsert)
  575. {
  576. UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
  577. UA_HistorizingNodeIdSettings setting;
  578. setting.historizingBackend = backend;
  579. setting.maxHistoryDataResponseSize = 1000;
  580. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
  581. serverMutexLock();
  582. UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  583. serverMutexUnlock();
  584. ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  585. // fill backend with insert
  586. ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_INSERT, testData, NULL, NULL))
  587. , UA_StatusCode_name(UA_STATUSCODE_GOOD));
  588. UA_HistoryData data;
  589. UA_HistoryData_init(&data);
  590. testResult(testDataSorted, &data);
  591. for (size_t i = 0; i < data.dataValuesSize; ++i) {
  592. ck_assert_uint_eq(data.dataValues[i].hasValue, true);
  593. ck_assert(data.dataValues[i].value.type == &UA_TYPES[UA_TYPES_INT64]);
  594. ck_assert_uint_eq(*((UA_Int64*)data.dataValues[i].value.data), UA_PERFORMUPDATETYPE_INSERT);
  595. }
  596. UA_HistoryData_deleteMembers(&data);
  597. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  598. }
  599. END_TEST
  600. START_TEST(Server_HistorizingUpdateReplace)
  601. {
  602. UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
  603. UA_HistorizingNodeIdSettings setting;
  604. setting.historizingBackend = backend;
  605. setting.maxHistoryDataResponseSize = 1000;
  606. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
  607. serverMutexLock();
  608. UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  609. serverMutexUnlock();
  610. ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  611. // fill backend with insert
  612. ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_INSERT, testData, NULL, NULL))
  613. , UA_StatusCode_name(UA_STATUSCODE_GOOD));
  614. // replace all
  615. ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_REPLACE, testData, NULL, NULL))
  616. , UA_StatusCode_name(UA_STATUSCODE_GOOD));
  617. UA_HistoryData data;
  618. UA_HistoryData_init(&data);
  619. testResult(testDataSorted, &data);
  620. for (size_t i = 0; i < data.dataValuesSize; ++i) {
  621. ck_assert_uint_eq(data.dataValues[i].hasValue, true);
  622. ck_assert(data.dataValues[i].value.type == &UA_TYPES[UA_TYPES_INT64]);
  623. ck_assert_uint_eq(*((UA_Int64*)data.dataValues[i].value.data), UA_PERFORMUPDATETYPE_REPLACE);
  624. }
  625. UA_HistoryData_deleteMembers(&data);
  626. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  627. }
  628. END_TEST
  629. START_TEST(Server_HistorizingUpdateUpdate)
  630. {
  631. UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
  632. UA_HistorizingNodeIdSettings setting;
  633. setting.historizingBackend = backend;
  634. setting.maxHistoryDataResponseSize = 1000;
  635. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
  636. serverMutexLock();
  637. UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  638. serverMutexUnlock();
  639. ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  640. // fill backend with insert
  641. ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_INSERT, testData, NULL, NULL))
  642. , UA_StatusCode_name(UA_STATUSCODE_GOOD));
  643. testResult(testDataSorted, NULL);
  644. // delete some values
  645. ck_assert_str_eq(UA_StatusCode_name(deleteHistory(DELETE_START_TIME, DELETE_STOP_TIME)),
  646. UA_StatusCode_name(UA_STATUSCODE_GOOD));
  647. testResult(testDataAfterDelete, NULL);
  648. // update all and insert some
  649. UA_StatusCode *result;
  650. size_t resultSize = 0;
  651. ck_assert_str_eq(UA_StatusCode_name(updateHistory(UA_PERFORMUPDATETYPE_UPDATE, testDataSorted, &result, &resultSize))
  652. , UA_StatusCode_name(UA_STATUSCODE_GOOD));
  653. for (size_t i = 0; i < resultSize; ++i) {
  654. ck_assert_str_eq(UA_StatusCode_name(result[i]), UA_StatusCode_name(testDataUpdateResult[i]));
  655. }
  656. UA_Array_delete(result, resultSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
  657. UA_HistoryData data;
  658. UA_HistoryData_init(&data);
  659. testResult(testDataSorted, &data);
  660. for (size_t i = 0; i < data.dataValuesSize; ++i) {
  661. ck_assert_uint_eq(data.dataValues[i].hasValue, true);
  662. ck_assert(data.dataValues[i].value.type == &UA_TYPES[UA_TYPES_INT64]);
  663. ck_assert_uint_eq(*((UA_Int64*)data.dataValues[i].value.data), UA_PERFORMUPDATETYPE_UPDATE);
  664. }
  665. UA_HistoryData_deleteMembers(&data);
  666. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  667. }
  668. END_TEST
  669. START_TEST(Server_HistorizingStrategyUser)
  670. {
  671. // set a data backend
  672. UA_HistorizingNodeIdSettings setting;
  673. setting.historizingBackend = UA_HistoryDataBackend_Memory(3, 100);
  674. setting.maxHistoryDataResponseSize = 100;
  675. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
  676. UA_StatusCode retval = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  677. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  678. // fill the data
  679. UA_DateTime start = UA_DateTime_now();
  680. UA_DateTime end = start + (10 * UA_DATETIME_SEC);
  681. for (UA_UInt32 i = 0; i < 10; ++i) {
  682. UA_DataValue value;
  683. UA_DataValue_init(&value);
  684. value.hasValue = true;
  685. value.hasStatus = true;
  686. value.status = UA_STATUSCODE_GOOD;
  687. UA_Variant_setScalarCopy(&value.value, &i, &UA_TYPES[UA_TYPES_UINT32]);
  688. value.hasSourceTimestamp = true;
  689. value.sourceTimestamp = start + (i * UA_DATETIME_SEC);
  690. value.hasServerTimestamp = true;
  691. value.serverTimestamp = value.sourceTimestamp;
  692. retval = setting.historizingBackend.serverSetHistoryData(server,
  693. setting.historizingBackend.context,
  694. NULL,
  695. NULL,
  696. &outNodeId,
  697. UA_FALSE,
  698. &value);
  699. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  700. UA_DataValue_deleteMembers(&value);
  701. }
  702. // request
  703. UA_HistoryReadResponse response;
  704. UA_HistoryReadResponse_init(&response);
  705. requestHistory(start, end, &response, 0, false, NULL);
  706. // test the response
  707. ck_assert_str_eq(UA_StatusCode_name(response.responseHeader.serviceResult), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  708. ck_assert_uint_eq(response.resultsSize, 1);
  709. for (size_t i = 0; i < response.resultsSize; ++i) {
  710. ck_assert_str_eq(UA_StatusCode_name(response.results[i].statusCode), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  711. ck_assert_uint_eq(response.results[i].historyData.encoding, UA_EXTENSIONOBJECT_DECODED);
  712. ck_assert(response.results[i].historyData.content.decoded.type == &UA_TYPES[UA_TYPES_HISTORYDATA]);
  713. UA_HistoryData * data = (UA_HistoryData *)response.results[i].historyData.content.decoded.data;
  714. ck_assert_uint_eq(data->dataValuesSize, 10);
  715. for (size_t j = 0; j < data->dataValuesSize; ++j) {
  716. ck_assert_uint_eq(data->dataValues[j].hasSourceTimestamp, true);
  717. ck_assert_uint_eq(data->dataValues[j].sourceTimestamp, start + (j * UA_DATETIME_SEC));
  718. ck_assert_uint_eq(data->dataValues[j].hasStatus, true);
  719. ck_assert_str_eq(UA_StatusCode_name(data->dataValues[j].status), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  720. ck_assert_uint_eq(data->dataValues[j].hasValue, true);
  721. ck_assert(data->dataValues[j].value.type == &UA_TYPES[UA_TYPES_UINT32]);
  722. UA_UInt32 * value = (UA_UInt32 *)data->dataValues[j].value.data;
  723. ck_assert_uint_eq(*value, j);
  724. }
  725. }
  726. UA_HistoryReadResponse_deleteMembers(&response);
  727. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  728. }
  729. END_TEST
  730. START_TEST(Server_HistorizingStrategyPoll)
  731. {
  732. // init to a defined value
  733. UA_StatusCode retval = setUInt32(client, outNodeId, 43);
  734. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  735. // set a data backend
  736. UA_HistorizingNodeIdSettings setting;
  737. setting.historizingBackend = UA_HistoryDataBackend_Memory(3, 100);
  738. setting.maxHistoryDataResponseSize = 100;
  739. setting.pollingInterval = 100;
  740. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_POLL;
  741. serverMutexLock();
  742. retval = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  743. serverMutexUnlock();
  744. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  745. // fill the data
  746. UA_DateTime start = UA_DateTime_now();
  747. serverMutexLock();
  748. retval = gathering->startPoll(server, gathering->context, &outNodeId);
  749. serverMutexUnlock();
  750. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  751. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  752. for (size_t k = 0; k < 10; ++k) {
  753. UA_fakeSleep(50);
  754. UA_realSleep(50);
  755. if (k == 5) {
  756. serverMutexLock();
  757. gathering->stopPoll(server, gathering->context, &outNodeId);
  758. serverMutexUnlock();
  759. }
  760. setUInt32(client, outNodeId, (unsigned int)k);
  761. }
  762. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  763. UA_DateTime end = UA_DateTime_now();
  764. // request
  765. UA_HistoryReadResponse response;
  766. UA_HistoryReadResponse_init(&response);
  767. requestHistory(start, end, &response, 0, false, NULL);
  768. // test the response
  769. ck_assert_str_eq(UA_StatusCode_name(response.responseHeader.serviceResult), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  770. ck_assert_uint_eq(response.resultsSize, 1);
  771. for (size_t i = 0; i < response.resultsSize; ++i) {
  772. ck_assert_str_eq(UA_StatusCode_name(response.results[i].statusCode), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  773. ck_assert_uint_eq(response.results[i].historyData.encoding, UA_EXTENSIONOBJECT_DECODED);
  774. ck_assert(response.results[i].historyData.content.decoded.type == &UA_TYPES[UA_TYPES_HISTORYDATA]);
  775. UA_HistoryData * data = (UA_HistoryData *)response.results[i].historyData.content.decoded.data;
  776. ck_assert(data->dataValuesSize > 1);
  777. for (size_t j = 0; j < data->dataValuesSize; ++j) {
  778. ck_assert_uint_eq(data->dataValues[j].hasSourceTimestamp, true);
  779. ck_assert(data->dataValues[j].sourceTimestamp >= start);
  780. ck_assert(data->dataValues[j].sourceTimestamp < end);
  781. ck_assert_uint_eq(data->dataValues[j].hasValue, true);
  782. ck_assert(data->dataValues[j].value.type == &UA_TYPES[UA_TYPES_UINT32]);
  783. UA_UInt32 * value = (UA_UInt32 *)data->dataValues[j].value.data;
  784. // first need to be 43
  785. if (j == 0) {
  786. ck_assert(*value == 43);
  787. } else {
  788. ck_assert(*value < 5);
  789. }
  790. }
  791. }
  792. UA_HistoryReadResponse_deleteMembers(&response);
  793. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  794. }
  795. END_TEST
  796. START_TEST(Server_HistorizingStrategyValueSet)
  797. {
  798. // init to a defined value
  799. UA_StatusCode retval = setUInt32(client, outNodeId, 43);
  800. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  801. // set a data backend
  802. UA_HistorizingNodeIdSettings setting;
  803. setting.historizingBackend = UA_HistoryDataBackend_Memory(3, 100);
  804. setting.maxHistoryDataResponseSize = 100;
  805. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_VALUESET;
  806. serverMutexLock();
  807. retval = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  808. serverMutexUnlock();
  809. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  810. // fill the data
  811. UA_fakeSleep(100);
  812. UA_DateTime start = UA_DateTime_now();
  813. UA_fakeSleep(100);
  814. for (UA_UInt32 i = 0; i < 10; ++i) {
  815. retval = setUInt32(client, outNodeId, i);
  816. ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  817. UA_fakeSleep(100);
  818. }
  819. UA_DateTime end = UA_DateTime_now();
  820. // request
  821. UA_HistoryReadResponse response;
  822. UA_HistoryReadResponse_init(&response);
  823. requestHistory(start, end, &response, 0, false, NULL);
  824. // test the response
  825. ck_assert_str_eq(UA_StatusCode_name(response.responseHeader.serviceResult), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  826. ck_assert_uint_eq(response.resultsSize, 1);
  827. for (size_t i = 0; i < response.resultsSize; ++i) {
  828. ck_assert_str_eq(UA_StatusCode_name(response.results[i].statusCode), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  829. ck_assert_uint_eq(response.results[i].historyData.encoding, UA_EXTENSIONOBJECT_DECODED);
  830. ck_assert(response.results[i].historyData.content.decoded.type == &UA_TYPES[UA_TYPES_HISTORYDATA]);
  831. UA_HistoryData * data = (UA_HistoryData *)response.results[i].historyData.content.decoded.data;
  832. ck_assert(data->dataValuesSize > 0);
  833. for (size_t j = 0; j < data->dataValuesSize; ++j) {
  834. ck_assert(data->dataValues[j].sourceTimestamp >= start && data->dataValues[j].sourceTimestamp < end);
  835. ck_assert_uint_eq(data->dataValues[j].hasSourceTimestamp, true);
  836. ck_assert_str_eq(UA_StatusCode_name(data->dataValues[j].status), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  837. ck_assert_uint_eq(data->dataValues[j].hasValue, true);
  838. ck_assert(data->dataValues[j].value.type == &UA_TYPES[UA_TYPES_UINT32]);
  839. UA_UInt32 * value = (UA_UInt32 *)data->dataValues[j].value.data;
  840. ck_assert_uint_eq(*value, j);
  841. }
  842. }
  843. UA_HistoryReadResponse_deleteMembers(&response);
  844. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  845. }
  846. END_TEST
  847. START_TEST(Server_HistorizingBackendMemory)
  848. {
  849. UA_HistoryDataBackend backend = UA_HistoryDataBackend_Memory(1, 1);
  850. UA_HistorizingNodeIdSettings setting;
  851. setting.historizingBackend = backend;
  852. setting.maxHistoryDataResponseSize = 1000;
  853. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
  854. serverMutexLock();
  855. UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  856. serverMutexUnlock();
  857. ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  858. // empty backend should not crash
  859. UA_UInt32 retval = testHistoricalDataBackend(100);
  860. fprintf(stderr, "%d tests expected failed.\n", retval);
  861. // fill backend
  862. ck_assert_uint_eq(fillHistoricalDataBackend(backend), true);
  863. // read all in one
  864. retval = testHistoricalDataBackend(100);
  865. fprintf(stderr, "%d tests failed.\n", retval);
  866. ck_assert_uint_eq(retval, 0);
  867. // read continuous one at one request
  868. retval = testHistoricalDataBackend(1);
  869. fprintf(stderr, "%d tests failed.\n", retval);
  870. ck_assert_uint_eq(retval, 0);
  871. // read continuous two at one request
  872. retval = testHistoricalDataBackend(2);
  873. fprintf(stderr, "%d tests failed.\n", retval);
  874. ck_assert_uint_eq(retval, 0);
  875. UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend);
  876. }
  877. END_TEST
  878. START_TEST(Server_HistorizingRandomIndexBackend)
  879. {
  880. UA_HistoryDataBackend backend = UA_HistoryDataBackend_randomindextest(testData);
  881. UA_HistorizingNodeIdSettings setting;
  882. setting.historizingBackend = backend;
  883. setting.maxHistoryDataResponseSize = 1000;
  884. setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;
  885. serverMutexLock();
  886. UA_StatusCode ret = gathering->registerNodeId(server, gathering->context, &outNodeId, setting);
  887. serverMutexUnlock();
  888. ck_assert_str_eq(UA_StatusCode_name(ret), UA_StatusCode_name(UA_STATUSCODE_GOOD));
  889. // read all in one
  890. UA_UInt32 retval = testHistoricalDataBackend(100);
  891. fprintf(stderr, "%d tests failed.\n", retval);
  892. ck_assert_uint_eq(retval, 0);
  893. // read continuous one at one request
  894. retval = testHistoricalDataBackend(1);
  895. fprintf(stderr, "%d tests failed.\n", retval);
  896. ck_assert_uint_eq(retval, 0);
  897. // read continuous two at one request
  898. retval = testHistoricalDataBackend(2);
  899. fprintf(stderr, "%d tests failed.\n", retval);
  900. ck_assert_uint_eq(retval, 0);
  901. UA_HistoryDataBackend_randomindextest_deleteMembers(&backend);
  902. }
  903. END_TEST
  904. #endif /*UA_ENABLE_HISTORIZING*/
  905. static Suite* testSuite_Client(void)
  906. {
  907. Suite *s = suite_create("Server Historical Data");
  908. TCase *tc_server = tcase_create("Server Historical Data Basic");
  909. tcase_add_checked_fixture(tc_server, setup, teardown);
  910. #ifdef UA_ENABLE_HISTORIZING
  911. tcase_add_test(tc_server, Server_HistorizingStrategyPoll);
  912. tcase_add_test(tc_server, Server_HistorizingStrategyUser);
  913. tcase_add_test(tc_server, Server_HistorizingStrategyValueSet);
  914. tcase_add_test(tc_server, Server_HistorizingBackendMemory);
  915. tcase_add_test(tc_server, Server_HistorizingRandomIndexBackend);
  916. tcase_add_test(tc_server, Server_HistorizingUpdateDelete);
  917. tcase_add_test(tc_server, Server_HistorizingUpdateInsert);
  918. tcase_add_test(tc_server, Server_HistorizingUpdateReplace);
  919. tcase_add_test(tc_server, Server_HistorizingUpdateUpdate);
  920. #endif /* UA_ENABLE_HISTORIZING */
  921. suite_add_tcase(s, tc_server);
  922. return s;
  923. }
  924. int main(void)
  925. {
  926. #ifdef UA_ENABLE_HISTORIZING
  927. testDataSorted = sortDateTimes(testData);
  928. #endif /* UA_ENABLE_HISTORIZING */
  929. Suite *s = testSuite_Client();
  930. SRunner *sr = srunner_create(s);
  931. srunner_set_fork_status(sr, CK_NOFORK);
  932. srunner_run_all(sr,CK_NORMAL);
  933. int number_failed = srunner_ntests_failed(sr);
  934. srunner_free(sr);
  935. #ifdef UA_ENABLE_HISTORIZING
  936. UA_free(testDataSorted);
  937. #endif /* UA_ENABLE_HISTORIZING */
  938. return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
  939. }