ua_historydatabase_default.c 29 KB

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