ua_historydatabase_default.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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. if (!UA_Server_AccessControl_allowHistoryUpdateUpdateData(server,
  318. sessionId,
  319. sessionContext,
  320. &details->nodeId,
  321. details->performInsertReplace,
  322. &details->updateValues[i])) {
  323. result->operationResults[i] = UA_STATUSCODE_BADUSERACCESSDENIED;
  324. continue;
  325. }
  326. switch (details->performInsertReplace) {
  327. case UA_PERFORMUPDATETYPE_INSERT:
  328. if (!setting->historizingBackend.insertDataValue) {
  329. result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  330. continue;
  331. }
  332. result->operationResults[i]
  333. = setting->historizingBackend.insertDataValue(server,
  334. setting->historizingBackend.context,
  335. sessionId,
  336. sessionContext,
  337. &details->nodeId,
  338. &details->updateValues[i]);
  339. continue;
  340. case UA_PERFORMUPDATETYPE_REPLACE:
  341. if (!setting->historizingBackend.replaceDataValue) {
  342. result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  343. continue;
  344. }
  345. result->operationResults[i]
  346. = setting->historizingBackend.replaceDataValue(server,
  347. setting->historizingBackend.context,
  348. sessionId,
  349. sessionContext,
  350. &details->nodeId,
  351. &details->updateValues[i]);
  352. continue;
  353. case UA_PERFORMUPDATETYPE_UPDATE:
  354. if (!setting->historizingBackend.updateDataValue) {
  355. result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  356. continue;
  357. }
  358. result->operationResults[i]
  359. = setting->historizingBackend.updateDataValue(server,
  360. setting->historizingBackend.context,
  361. sessionId,
  362. sessionContext,
  363. &details->nodeId,
  364. &details->updateValues[i]);
  365. continue;
  366. default:
  367. result->operationResults[i] = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  368. continue;
  369. }
  370. }
  371. }
  372. static void
  373. deleteRawModified_service_default(UA_Server *server,
  374. void *hdbContext,
  375. const UA_NodeId *sessionId,
  376. void *sessionContext,
  377. const UA_RequestHeader *requestHeader,
  378. const UA_DeleteRawModifiedDetails *details,
  379. UA_HistoryUpdateResult *result)
  380. {
  381. if (details->isDeleteModified) {
  382. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  383. return;
  384. }
  385. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdbContext;
  386. UA_Byte accessLevel = 0;
  387. UA_Server_readAccessLevel(server,
  388. details->nodeId,
  389. &accessLevel);
  390. if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYWRITE)) {
  391. result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED;
  392. return;
  393. }
  394. UA_Boolean historizing = false;
  395. UA_Server_readHistorizing(server,
  396. details->nodeId,
  397. &historizing);
  398. if (!historizing) {
  399. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  400. return;
  401. }
  402. const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting(
  403. server,
  404. ctx->gathering.context,
  405. &details->nodeId);
  406. if (!setting) {
  407. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  408. return;
  409. }
  410. if (!setting->historizingBackend.removeDataValue) {
  411. result->statusCode = UA_STATUSCODE_BADHISTORYOPERATIONUNSUPPORTED;
  412. return;
  413. }
  414. if (!UA_Server_AccessControl_allowHistoryUpdateDeleteRawModified(server,
  415. sessionId,
  416. sessionContext,
  417. &details->nodeId,
  418. details->startTime,
  419. details->endTime,
  420. details->isDeleteModified)) {
  421. result->statusCode = UA_STATUSCODE_BADUSERACCESSDENIED;
  422. return;
  423. }
  424. result->statusCode
  425. = setting->historizingBackend.removeDataValue(server,
  426. setting->historizingBackend.context,
  427. sessionId,
  428. sessionContext,
  429. &details->nodeId,
  430. details->startTime,
  431. details->endTime);
  432. }
  433. static void
  434. readRaw_service_default(UA_Server *server,
  435. void *context,
  436. const UA_NodeId *sessionId,
  437. void *sessionContext,
  438. const UA_RequestHeader *requestHeader,
  439. const UA_ReadRawModifiedDetails *historyReadDetails,
  440. UA_TimestampsToReturn timestampsToReturn,
  441. UA_Boolean releaseContinuationPoints,
  442. size_t nodesToReadSize,
  443. const UA_HistoryReadValueId *nodesToRead,
  444. UA_HistoryReadResponse *response,
  445. UA_HistoryData * const * const historyData)
  446. {
  447. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context;
  448. for (size_t i = 0; i < nodesToReadSize; ++i) {
  449. UA_Byte accessLevel = 0;
  450. UA_Server_readAccessLevel(server,
  451. nodesToRead[i].nodeId,
  452. &accessLevel);
  453. if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYREAD)) {
  454. response->results[i].statusCode = UA_STATUSCODE_BADUSERACCESSDENIED;
  455. continue;
  456. }
  457. UA_Boolean historizing = false;
  458. UA_Server_readHistorizing(server,
  459. nodesToRead[i].nodeId,
  460. &historizing);
  461. if (!historizing) {
  462. response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  463. continue;
  464. }
  465. const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting(
  466. server,
  467. ctx->gathering.context,
  468. &nodesToRead[i].nodeId);
  469. if (!setting) {
  470. response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  471. continue;
  472. }
  473. if (historyReadDetails->returnBounds && !setting->historizingBackend.boundSupported(
  474. server,
  475. setting->historizingBackend.context,
  476. sessionId,
  477. sessionContext,
  478. &nodesToRead[i].nodeId)) {
  479. response->results[i].statusCode = UA_STATUSCODE_BADBOUNDNOTSUPPORTED;
  480. continue;
  481. }
  482. if (!setting->historizingBackend.timestampsToReturnSupported(
  483. server,
  484. setting->historizingBackend.context,
  485. sessionId,
  486. sessionContext,
  487. &nodesToRead[i].nodeId,
  488. timestampsToReturn)) {
  489. response->results[i].statusCode = UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED;
  490. continue;
  491. }
  492. UA_NumericRange range;
  493. range.dimensionsSize = 0;
  494. range.dimensions = NULL;
  495. if (nodesToRead[i].indexRange.length > 0) {
  496. UA_StatusCode rangeParseResult = UA_NumericRange_parseFromString(&range, &nodesToRead[i].indexRange);
  497. if (rangeParseResult != UA_STATUSCODE_GOOD) {
  498. response->results[i].statusCode = rangeParseResult;
  499. continue;
  500. }
  501. }
  502. UA_StatusCode getHistoryDataStatusCode;
  503. if (setting->historizingBackend.getHistoryData) {
  504. getHistoryDataStatusCode = setting->historizingBackend.getHistoryData(
  505. server,
  506. sessionId,
  507. sessionContext,
  508. &setting->historizingBackend,
  509. historyReadDetails->startTime,
  510. historyReadDetails->endTime,
  511. &nodesToRead[i].nodeId,
  512. setting->maxHistoryDataResponseSize,
  513. historyReadDetails->numValuesPerNode,
  514. historyReadDetails->returnBounds,
  515. timestampsToReturn,
  516. range,
  517. releaseContinuationPoints,
  518. &nodesToRead[i].continuationPoint,
  519. &response->results[i].continuationPoint,
  520. historyData[i]);
  521. } else {
  522. getHistoryDataStatusCode = getHistoryData_service_default(
  523. &setting->historizingBackend,
  524. historyReadDetails->startTime,
  525. historyReadDetails->endTime,
  526. server,
  527. sessionId,
  528. sessionContext,
  529. &nodesToRead[i].nodeId,
  530. setting->maxHistoryDataResponseSize,
  531. historyReadDetails->numValuesPerNode,
  532. historyReadDetails->returnBounds,
  533. timestampsToReturn,
  534. range,
  535. releaseContinuationPoints,
  536. &nodesToRead[i].continuationPoint,
  537. &response->results[i].continuationPoint,
  538. &historyData[i]->dataValuesSize,
  539. &historyData[i]->dataValues);
  540. }
  541. if (getHistoryDataStatusCode != UA_STATUSCODE_GOOD) {
  542. response->results[i].statusCode = getHistoryDataStatusCode;
  543. continue;
  544. }
  545. }
  546. response->responseHeader.serviceResult = UA_STATUSCODE_GOOD;
  547. return;
  548. }
  549. static void
  550. setValue_service_default(UA_Server *server,
  551. void *context,
  552. const UA_NodeId *sessionId,
  553. void *sessionContext,
  554. const UA_NodeId *nodeId,
  555. UA_Boolean historizing,
  556. const UA_DataValue *value)
  557. {
  558. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context;
  559. if (ctx->gathering.setValue)
  560. ctx->gathering.setValue(server,
  561. ctx->gathering.context,
  562. sessionId,
  563. sessionContext,
  564. nodeId,
  565. historizing,
  566. value);
  567. }
  568. static void
  569. deleteMembers_service_default(UA_HistoryDatabase *hdb)
  570. {
  571. if (hdb == NULL || hdb->context == NULL)
  572. return;
  573. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdb->context;
  574. ctx->gathering.deleteMembers(&ctx->gathering);
  575. UA_free(ctx);
  576. }
  577. UA_HistoryDatabase
  578. UA_HistoryDatabase_default(UA_HistoryDataGathering gathering)
  579. {
  580. UA_HistoryDatabase hdb;
  581. UA_HistoryDatabaseContext_default *context =
  582. (UA_HistoryDatabaseContext_default*)
  583. UA_calloc(1, sizeof(UA_HistoryDatabaseContext_default));
  584. context->gathering = gathering;
  585. hdb.context = context;
  586. hdb.readRaw = &readRaw_service_default;
  587. hdb.setValue = &setValue_service_default;
  588. hdb.updateData = &updateData_service_default;
  589. hdb.deleteRawModified = &deleteRawModified_service_default;
  590. hdb.deleteMembers = &deleteMembers_service_default;
  591. return hdb;
  592. }