ua_historydatabase_default.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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_historydatabase_default.h"
  8. #include <limits.h>
  9. typedef struct {
  10. UA_HistoryDataGathering gathering;
  11. } UA_HistoryDatabaseContext_default;
  12. static size_t
  13. getResultSize_service_default(const UA_HistoryDataBackend* backend,
  14. UA_Server *server,
  15. const UA_NodeId *sessionId,
  16. void* sessionContext,
  17. const UA_NodeId *nodeId,
  18. UA_DateTime start,
  19. UA_DateTime end,
  20. UA_UInt32 numValuesPerNode,
  21. UA_Boolean returnBounds,
  22. size_t *startIndex,
  23. size_t *endIndex,
  24. UA_Boolean *addFirst,
  25. UA_Boolean *addLast,
  26. UA_Boolean *reverse)
  27. {
  28. size_t storeEnd = backend->getEnd(server, backend->context, sessionId, sessionContext, nodeId);
  29. *startIndex = storeEnd;
  30. *endIndex = storeEnd;
  31. *addFirst = false;
  32. *addLast = false;
  33. if (end == LLONG_MIN) {
  34. *reverse = false;
  35. } else if (start == LLONG_MIN) {
  36. *reverse = true;
  37. } else {
  38. *reverse = end < start;
  39. }
  40. UA_Boolean equal = start == end;
  41. size_t size = 0;
  42. if (storeEnd > 0) {
  43. if (equal) {
  44. if (returnBounds) {
  45. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE);
  46. if (*startIndex == storeEnd) {
  47. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER);
  48. *addFirst = true;
  49. }
  50. *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER);
  51. size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *startIndex, *endIndex);
  52. } else {
  53. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL);
  54. *endIndex = *startIndex;
  55. if (*startIndex == storeEnd)
  56. size = 0;
  57. else
  58. size = 1;
  59. }
  60. } else if (start == LLONG_MIN) {
  61. *endIndex = 0;
  62. if (returnBounds) {
  63. *addLast = true;
  64. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_AFTER);
  65. if (*startIndex == storeEnd) {
  66. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_BEFORE);
  67. *addFirst = true;
  68. }
  69. } else {
  70. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_BEFORE);
  71. }
  72. size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *endIndex, *startIndex);
  73. } else if (end == LLONG_MIN) {
  74. *endIndex = storeEnd - 1;
  75. if (returnBounds) {
  76. *addLast = true;
  77. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE);
  78. if (*startIndex == storeEnd) {
  79. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER);
  80. *addFirst = true;
  81. }
  82. } else {
  83. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_AFTER);
  84. }
  85. size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *startIndex, *endIndex);
  86. } else if (*reverse) {
  87. if (returnBounds) {
  88. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_AFTER);
  89. if (*startIndex == storeEnd) {
  90. *addFirst = true;
  91. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_BEFORE);
  92. }
  93. *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_BEFORE);
  94. if (*endIndex == storeEnd) {
  95. *addLast = true;
  96. *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_AFTER);
  97. }
  98. } else {
  99. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE);
  100. *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_AFTER);
  101. }
  102. size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *endIndex, *startIndex);
  103. } else {
  104. if (returnBounds) {
  105. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE);
  106. if (*startIndex == storeEnd) {
  107. *addFirst = true;
  108. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER);
  109. }
  110. *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_AFTER);
  111. if (*endIndex == storeEnd) {
  112. *addLast = true;
  113. *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_BEFORE);
  114. }
  115. } else {
  116. *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_AFTER);
  117. *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_BEFORE);
  118. }
  119. size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *startIndex, *endIndex);
  120. }
  121. } else if (returnBounds) {
  122. *addLast = true;
  123. *addFirst = true;
  124. }
  125. if (*addLast)
  126. ++size;
  127. if (*addFirst)
  128. ++size;
  129. if (numValuesPerNode > 0 && size > numValuesPerNode) {
  130. size = numValuesPerNode;
  131. *addLast = false;
  132. }
  133. return size;
  134. }
  135. static UA_StatusCode
  136. getHistoryData_service_default(const UA_HistoryDataBackend* backend,
  137. const UA_DateTime start,
  138. const UA_DateTime end,
  139. UA_Server *server,
  140. const UA_NodeId *sessionId,
  141. void *sessionContext,
  142. const UA_NodeId* nodeId,
  143. size_t maxSize,
  144. UA_UInt32 numValuesPerNode,
  145. UA_Boolean returnBounds,
  146. UA_TimestampsToReturn timestampsToReturn,
  147. UA_NumericRange range,
  148. UA_Boolean releaseContinuationPoints,
  149. const UA_ByteString *continuationPoint,
  150. UA_ByteString *outContinuationPoint,
  151. size_t *resultSize,
  152. UA_DataValue ** result)
  153. {
  154. size_t skip = 0;
  155. UA_ByteString backendContinuationPoint;
  156. UA_ByteString_init(&backendContinuationPoint);
  157. if (continuationPoint->length > 0) {
  158. if (continuationPoint->length >= sizeof(size_t)) {
  159. skip = *((size_t*)(continuationPoint->data));
  160. if (continuationPoint->length > 0) {
  161. backendContinuationPoint.length = continuationPoint->length - sizeof(size_t);
  162. backendContinuationPoint.data = continuationPoint->data + sizeof(size_t);
  163. }
  164. } else {
  165. return UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
  166. }
  167. }
  168. size_t storeEnd = backend->getEnd(server, backend->context, sessionId, sessionContext, nodeId);
  169. size_t startIndex;
  170. size_t endIndex;
  171. UA_Boolean addFirst;
  172. UA_Boolean addLast;
  173. UA_Boolean reverse;
  174. size_t _resultSize = getResultSize_service_default(backend,
  175. server,
  176. sessionId,
  177. sessionContext,
  178. nodeId,
  179. start,
  180. end,
  181. numValuesPerNode == 0 ? 0 : numValuesPerNode + (UA_UInt32)skip,
  182. returnBounds,
  183. &startIndex,
  184. &endIndex,
  185. &addFirst,
  186. &addLast,
  187. &reverse);
  188. *resultSize = _resultSize - skip;
  189. if (*resultSize > maxSize) {
  190. *resultSize = maxSize;
  191. }
  192. UA_DataValue *outResult= (UA_DataValue*)UA_Array_new(*resultSize, &UA_TYPES[UA_TYPES_DATAVALUE]);
  193. if (!outResult) {
  194. *resultSize = 0;
  195. return UA_STATUSCODE_BADOUTOFMEMORY;
  196. }
  197. *result = outResult;
  198. size_t counter = 0;
  199. if (addFirst) {
  200. if (skip == 0) {
  201. outResult[counter].hasStatus = true;
  202. outResult[counter].status = UA_STATUSCODE_BADBOUNDNOTFOUND;
  203. outResult[counter].hasSourceTimestamp = true;
  204. if (start == LLONG_MIN) {
  205. outResult[counter].sourceTimestamp = end;
  206. } else {
  207. outResult[counter].sourceTimestamp = start;
  208. }
  209. ++counter;
  210. }
  211. }
  212. UA_ByteString backendOutContinuationPoint;
  213. UA_ByteString_init(&backendOutContinuationPoint);
  214. if (endIndex != storeEnd && startIndex != storeEnd) {
  215. size_t retval = 0;
  216. size_t valueSize = *resultSize - counter;
  217. if (valueSize + skip > _resultSize - addFirst - addLast) {
  218. if (skip == 0) {
  219. valueSize = _resultSize - addFirst - addLast;
  220. } else {
  221. valueSize = _resultSize - skip - addLast;
  222. }
  223. }
  224. UA_StatusCode ret = UA_STATUSCODE_GOOD;
  225. if (valueSize > 0)
  226. ret = backend->copyDataValues(server,
  227. backend->context,
  228. sessionId,
  229. sessionContext,
  230. nodeId,
  231. startIndex,
  232. endIndex,
  233. reverse,
  234. valueSize,
  235. range,
  236. releaseContinuationPoints,
  237. &backendContinuationPoint,
  238. &backendOutContinuationPoint,
  239. &retval,
  240. &outResult[counter]);
  241. if (ret != UA_STATUSCODE_GOOD) {
  242. UA_Array_delete(outResult, *resultSize, &UA_TYPES[UA_TYPES_DATAVALUE]);
  243. *result = NULL;
  244. *resultSize = 0;
  245. return ret;
  246. }
  247. counter += retval;
  248. }
  249. if (addLast && counter < *resultSize) {
  250. outResult[counter].hasStatus = true;
  251. outResult[counter].status = UA_STATUSCODE_BADBOUNDNOTFOUND;
  252. outResult[counter].hasSourceTimestamp = true;
  253. if (start == LLONG_MIN && storeEnd != backend->firstIndex(server, backend->context, sessionId, sessionContext, nodeId)) {
  254. outResult[counter].sourceTimestamp = backend->getDataValue(server, backend->context, sessionId, sessionContext, nodeId, endIndex)->sourceTimestamp - UA_DATETIME_SEC;
  255. } else if (end == LLONG_MIN && storeEnd != backend->firstIndex(server, backend->context, sessionId, sessionContext, nodeId)) {
  256. outResult[counter].sourceTimestamp = backend->getDataValue(server, backend->context, sessionId, sessionContext, nodeId, endIndex)->sourceTimestamp + UA_DATETIME_SEC;
  257. } else {
  258. outResult[counter].sourceTimestamp = end;
  259. }
  260. }
  261. // there are more values
  262. if (skip + *resultSize < _resultSize
  263. // there are not more values for this request, but there are more values in database
  264. || (backendOutContinuationPoint.length > 0
  265. && numValuesPerNode != 0)
  266. // we deliver just one value which is a FIRST/LAST value
  267. || (skip == 0
  268. && addFirst == true
  269. && *resultSize == 1)) {
  270. if(UA_ByteString_allocBuffer(outContinuationPoint, backendOutContinuationPoint.length + sizeof(size_t))
  271. != UA_STATUSCODE_GOOD) {
  272. return UA_STATUSCODE_BADOUTOFMEMORY;
  273. }
  274. *((size_t*)(outContinuationPoint->data)) = skip + *resultSize;
  275. memcpy(outContinuationPoint->data + sizeof(size_t), backendOutContinuationPoint.data, backendOutContinuationPoint.length);
  276. }
  277. UA_ByteString_deleteMembers(&backendOutContinuationPoint);
  278. return UA_STATUSCODE_GOOD;
  279. }
  280. static void
  281. updateData_service_default(UA_Server *server,
  282. void *hdbContext,
  283. const UA_NodeId *sessionId,
  284. void *sessionContext,
  285. const UA_RequestHeader *requestHeader,
  286. const UA_UpdateDataDetails *details,
  287. UA_HistoryUpdateResult *result)
  288. {
  289. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdbContext;
  290. UA_Byte accessLevel = 0;
  291. UA_Server_readAccessLevel(server,
  292. details->nodeId,
  293. &accessLevel);
  294. if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYWRITE)) {
  295. result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED;
  296. return;
  297. }
  298. UA_Boolean historizing = false;
  299. UA_Server_readHistorizing(server,
  300. details->nodeId,
  301. &historizing);
  302. if (!historizing) {
  303. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  304. return;
  305. }
  306. const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting(
  307. server,
  308. ctx->gathering.context,
  309. &details->nodeId);
  310. if (!setting) {
  311. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  312. return;
  313. }
  314. result->operationResultsSize = details->updateValuesSize;
  315. result->operationResults = (UA_StatusCode*)UA_Array_new(result->operationResultsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
  316. for (size_t i = 0; i < details->updateValuesSize; ++i) {
  317. switch (details->performInsertReplace) {
  318. case UA_PERFORMUPDATETYPE_INSERT:
  319. if (!setting->historizingBackend.insertDataValue) {
  320. result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  321. continue;
  322. }
  323. result->operationResults[i]
  324. = setting->historizingBackend.insertDataValue(server,
  325. setting->historizingBackend.context,
  326. sessionId,
  327. sessionContext,
  328. &details->nodeId,
  329. &details->updateValues[i]);
  330. continue;
  331. case UA_PERFORMUPDATETYPE_REPLACE:
  332. if (!setting->historizingBackend.replaceDataValue) {
  333. result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  334. continue;
  335. }
  336. result->operationResults[i]
  337. = setting->historizingBackend.replaceDataValue(server,
  338. setting->historizingBackend.context,
  339. sessionId,
  340. sessionContext,
  341. &details->nodeId,
  342. &details->updateValues[i]);
  343. continue;
  344. case UA_PERFORMUPDATETYPE_UPDATE:
  345. if (!setting->historizingBackend.updateDataValue) {
  346. result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  347. continue;
  348. }
  349. result->operationResults[i]
  350. = setting->historizingBackend.updateDataValue(server,
  351. setting->historizingBackend.context,
  352. sessionId,
  353. sessionContext,
  354. &details->nodeId,
  355. &details->updateValues[i]);
  356. continue;
  357. default:
  358. result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  359. continue;
  360. }
  361. }
  362. }
  363. static void
  364. deleteRawModified_service_default(UA_Server *server,
  365. void *hdbContext,
  366. const UA_NodeId *sessionId,
  367. void *sessionContext,
  368. const UA_RequestHeader *requestHeader,
  369. const UA_DeleteRawModifiedDetails *details,
  370. UA_HistoryUpdateResult *result)
  371. {
  372. if (details->isDeleteModified) {
  373. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  374. return;
  375. }
  376. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdbContext;
  377. UA_Byte accessLevel = 0;
  378. UA_Server_readAccessLevel(server,
  379. details->nodeId,
  380. &accessLevel);
  381. if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYWRITE)) {
  382. result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED;
  383. return;
  384. }
  385. UA_Boolean historizing = false;
  386. UA_Server_readHistorizing(server,
  387. details->nodeId,
  388. &historizing);
  389. if (!historizing) {
  390. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  391. return;
  392. }
  393. const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting(
  394. server,
  395. ctx->gathering.context,
  396. &details->nodeId);
  397. if (!setting) {
  398. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  399. return;
  400. }
  401. if (!setting->historizingBackend.removeDataValue) {
  402. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  403. return;
  404. }
  405. result->statusCode
  406. = setting->historizingBackend.removeDataValue(server,
  407. setting->historizingBackend.context,
  408. sessionId,
  409. sessionContext,
  410. &details->nodeId,
  411. details->startTime,
  412. details->endTime);
  413. }
  414. static void
  415. readRaw_service_default(UA_Server *server,
  416. void *context,
  417. const UA_NodeId *sessionId,
  418. void *sessionContext,
  419. const UA_RequestHeader *requestHeader,
  420. const UA_ReadRawModifiedDetails *historyReadDetails,
  421. UA_TimestampsToReturn timestampsToReturn,
  422. UA_Boolean releaseContinuationPoints,
  423. size_t nodesToReadSize,
  424. const UA_HistoryReadValueId *nodesToRead,
  425. UA_HistoryReadResponse *response,
  426. UA_HistoryData * const * const historyData)
  427. {
  428. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context;
  429. for (size_t i = 0; i < nodesToReadSize; ++i) {
  430. UA_Byte accessLevel = 0;
  431. UA_Server_readAccessLevel(server,
  432. nodesToRead[i].nodeId,
  433. &accessLevel);
  434. if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYREAD)) {
  435. response->results[i].statusCode = UA_STATUSCODE_BADUSERACCESSDENIED;
  436. continue;
  437. }
  438. UA_Boolean historizing = false;
  439. UA_Server_readHistorizing(server,
  440. nodesToRead[i].nodeId,
  441. &historizing);
  442. if (!historizing) {
  443. response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  444. continue;
  445. }
  446. const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting(
  447. server,
  448. ctx->gathering.context,
  449. &nodesToRead[i].nodeId);
  450. if (!setting) {
  451. response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  452. continue;
  453. }
  454. if (historyReadDetails->returnBounds && !setting->historizingBackend.boundSupported(
  455. server,
  456. setting->historizingBackend.context,
  457. sessionId,
  458. sessionContext,
  459. &nodesToRead[i].nodeId)) {
  460. response->results[i].statusCode = UA_STATUSCODE_BADBOUNDNOTSUPPORTED;
  461. continue;
  462. }
  463. if (!setting->historizingBackend.timestampsToReturnSupported(
  464. server,
  465. setting->historizingBackend.context,
  466. sessionId,
  467. sessionContext,
  468. &nodesToRead[i].nodeId,
  469. timestampsToReturn)) {
  470. response->results[i].statusCode = UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED;
  471. continue;
  472. }
  473. UA_NumericRange range;
  474. range.dimensionsSize = 0;
  475. range.dimensions = NULL;
  476. if (nodesToRead[i].indexRange.length > 0) {
  477. UA_StatusCode rangeParseResult = UA_NumericRange_parseFromString(&range, &nodesToRead[i].indexRange);
  478. if (rangeParseResult != UA_STATUSCODE_GOOD) {
  479. response->results[i].statusCode = rangeParseResult;
  480. continue;
  481. }
  482. }
  483. UA_StatusCode getHistoryDataStatusCode;
  484. if (setting->historizingBackend.getHistoryData) {
  485. getHistoryDataStatusCode = setting->historizingBackend.getHistoryData(
  486. server,
  487. sessionId,
  488. sessionContext,
  489. &setting->historizingBackend,
  490. historyReadDetails->startTime,
  491. historyReadDetails->endTime,
  492. &nodesToRead[i].nodeId,
  493. setting->maxHistoryDataResponseSize,
  494. historyReadDetails->numValuesPerNode,
  495. historyReadDetails->returnBounds,
  496. timestampsToReturn,
  497. range,
  498. releaseContinuationPoints,
  499. &nodesToRead[i].continuationPoint,
  500. &response->results[i].continuationPoint,
  501. historyData[i]);
  502. } else {
  503. getHistoryDataStatusCode = getHistoryData_service_default(
  504. &setting->historizingBackend,
  505. historyReadDetails->startTime,
  506. historyReadDetails->endTime,
  507. server,
  508. sessionId,
  509. sessionContext,
  510. &nodesToRead[i].nodeId,
  511. setting->maxHistoryDataResponseSize,
  512. historyReadDetails->numValuesPerNode,
  513. historyReadDetails->returnBounds,
  514. timestampsToReturn,
  515. range,
  516. releaseContinuationPoints,
  517. &nodesToRead[i].continuationPoint,
  518. &response->results[i].continuationPoint,
  519. &historyData[i]->dataValuesSize,
  520. &historyData[i]->dataValues);
  521. }
  522. if (getHistoryDataStatusCode != UA_STATUSCODE_GOOD) {
  523. response->results[i].statusCode = getHistoryDataStatusCode;
  524. continue;
  525. }
  526. }
  527. response->responseHeader.serviceResult = UA_STATUSCODE_GOOD;
  528. return;
  529. }
  530. static void
  531. setValue_service_default(UA_Server *server,
  532. void *context,
  533. const UA_NodeId *sessionId,
  534. void *sessionContext,
  535. const UA_NodeId *nodeId,
  536. UA_Boolean historizing,
  537. const UA_DataValue *value)
  538. {
  539. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context;
  540. if (ctx->gathering.setValue)
  541. ctx->gathering.setValue(server,
  542. ctx->gathering.context,
  543. sessionId,
  544. sessionContext,
  545. nodeId,
  546. historizing,
  547. value);
  548. }
  549. static void
  550. deleteMembers_service_default(UA_HistoryDatabase *hdb)
  551. {
  552. if (hdb == NULL || hdb->context == NULL)
  553. return;
  554. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdb->context;
  555. ctx->gathering.deleteMembers(&ctx->gathering);
  556. UA_free(ctx);
  557. }
  558. UA_HistoryDatabase
  559. UA_HistoryDatabase_default(UA_HistoryDataGathering gathering)
  560. {
  561. UA_HistoryDatabase hdb;
  562. UA_HistoryDatabaseContext_default *context =
  563. (UA_HistoryDatabaseContext_default*)
  564. UA_calloc(1, sizeof(UA_HistoryDatabaseContext_default));
  565. context->gathering = gathering;
  566. hdb.context = context;
  567. hdb.readRaw = &readRaw_service_default;
  568. hdb.setValue = &setValue_service_default;
  569. hdb.updateData = &updateData_service_default;
  570. hdb.deleteRawModified = &deleteRawModified_service_default;
  571. hdb.deleteMembers = &deleteMembers_service_default;
  572. return hdb;
  573. }