ua_historydatabase_default.c 29 KB

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