ua_history_database_default.c 29 KB

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