ua_historydatabase_default.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  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. readRaw_service_default(UA_Server *server,
  282. void *context,
  283. const UA_NodeId *sessionId,
  284. void *sessionContext,
  285. const UA_RequestHeader *requestHeader,
  286. const UA_ReadRawModifiedDetails *historyReadDetails,
  287. UA_TimestampsToReturn timestampsToReturn,
  288. UA_Boolean releaseContinuationPoints,
  289. size_t nodesToReadSize,
  290. const UA_HistoryReadValueId *nodesToRead,
  291. UA_HistoryReadResponse *response,
  292. UA_HistoryData * const * const historyData)
  293. {
  294. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context;
  295. for (size_t i = 0; i < nodesToReadSize; ++i) {
  296. UA_Byte accessLevel = 0;
  297. UA_Server_readAccessLevel(server,
  298. nodesToRead[i].nodeId,
  299. &accessLevel);
  300. if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYREAD)) {
  301. response->results[i].statusCode = UA_STATUSCODE_BADUSERACCESSDENIED;
  302. continue;
  303. }
  304. UA_Boolean historizing = false;
  305. UA_Server_readHistorizing(server,
  306. nodesToRead[i].nodeId,
  307. &historizing);
  308. if (!historizing) {
  309. response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  310. continue;
  311. }
  312. const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting(
  313. server,
  314. ctx->gathering.context,
  315. &nodesToRead[i].nodeId);
  316. if (!setting) {
  317. response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
  318. continue;
  319. }
  320. if (historyReadDetails->returnBounds && !setting->historizingBackend.boundSupported(
  321. server,
  322. setting->historizingBackend.context,
  323. sessionId,
  324. sessionContext,
  325. &nodesToRead[i].nodeId)) {
  326. response->results[i].statusCode = UA_STATUSCODE_BADBOUNDNOTSUPPORTED;
  327. continue;
  328. }
  329. if (!setting->historizingBackend.timestampsToReturnSupported(
  330. server,
  331. setting->historizingBackend.context,
  332. sessionId,
  333. sessionContext,
  334. &nodesToRead[i].nodeId,
  335. timestampsToReturn)) {
  336. response->results[i].statusCode = UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED;
  337. continue;
  338. }
  339. UA_NumericRange range;
  340. range.dimensionsSize = 0;
  341. range.dimensions = NULL;
  342. if (nodesToRead[i].indexRange.length > 0) {
  343. UA_StatusCode rangeParseResult = UA_NumericRange_parseFromString(&range, &nodesToRead[i].indexRange);
  344. if (rangeParseResult != UA_STATUSCODE_GOOD) {
  345. response->results[i].statusCode = rangeParseResult;
  346. continue;
  347. }
  348. }
  349. UA_StatusCode getHistoryDataStatusCode;
  350. if (setting->historizingBackend.getHistoryData) {
  351. getHistoryDataStatusCode = setting->historizingBackend.getHistoryData(
  352. server,
  353. sessionId,
  354. sessionContext,
  355. &setting->historizingBackend,
  356. historyReadDetails->startTime,
  357. historyReadDetails->endTime,
  358. &nodesToRead[i].nodeId,
  359. setting->maxHistoryDataResponseSize,
  360. historyReadDetails->numValuesPerNode,
  361. historyReadDetails->returnBounds,
  362. timestampsToReturn,
  363. range,
  364. releaseContinuationPoints,
  365. &nodesToRead[i].continuationPoint,
  366. &response->results[i].continuationPoint,
  367. historyData[i]);
  368. } else {
  369. getHistoryDataStatusCode = getHistoryData_service_default(
  370. &setting->historizingBackend,
  371. historyReadDetails->startTime,
  372. historyReadDetails->endTime,
  373. server,
  374. sessionId,
  375. sessionContext,
  376. &nodesToRead[i].nodeId,
  377. setting->maxHistoryDataResponseSize,
  378. historyReadDetails->numValuesPerNode,
  379. historyReadDetails->returnBounds,
  380. timestampsToReturn,
  381. range,
  382. releaseContinuationPoints,
  383. &nodesToRead[i].continuationPoint,
  384. &response->results[i].continuationPoint,
  385. &historyData[i]->dataValuesSize,
  386. &historyData[i]->dataValues);
  387. }
  388. if (getHistoryDataStatusCode != UA_STATUSCODE_GOOD) {
  389. response->results[i].statusCode = getHistoryDataStatusCode;
  390. continue;
  391. }
  392. }
  393. response->responseHeader.serviceResult = UA_STATUSCODE_GOOD;
  394. return;
  395. }
  396. static void
  397. setValue_service_default(UA_Server *server,
  398. void *context,
  399. const UA_NodeId *sessionId,
  400. void *sessionContext,
  401. const UA_NodeId *nodeId,
  402. UA_Boolean historizing,
  403. const UA_DataValue *value)
  404. {
  405. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context;
  406. if (ctx->gathering.setValue)
  407. ctx->gathering.setValue(server,
  408. ctx->gathering.context,
  409. sessionId,
  410. sessionContext,
  411. nodeId,
  412. historizing,
  413. value);
  414. }
  415. static void
  416. deleteMembers_service_default(UA_HistoryDatabase *hdb)
  417. {
  418. if (hdb == NULL || hdb->context == NULL)
  419. return;
  420. UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdb->context;
  421. ctx->gathering.deleteMembers(&ctx->gathering);
  422. UA_free(ctx);
  423. }
  424. UA_HistoryDatabase
  425. UA_HistoryDatabase_default(UA_HistoryDataGathering gathering)
  426. {
  427. UA_HistoryDatabase hdb;
  428. UA_HistoryDatabaseContext_default *context =
  429. (UA_HistoryDatabaseContext_default*)
  430. UA_calloc(1, sizeof(UA_HistoryDatabaseContext_default));
  431. context->gathering = gathering;
  432. hdb.context = context;
  433. hdb.readRaw = &readRaw_service_default;
  434. hdb.setValue = &setValue_service_default;
  435. hdb.deleteMembers = &deleteMembers_service_default;
  436. return hdb;
  437. }