123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * Copyright 2018 (c) basysKom GmbH <opensource@basyskom.com> (Author: Peter Rustler)
- */
- #include "ua_historydatabase_default.h"
- #include <limits.h>
- typedef struct {
- UA_HistoryDataGathering gathering;
- } UA_HistoryDatabaseContext_default;
- static size_t
- getResultSize_service_default(const UA_HistoryDataBackend* backend,
- UA_Server *server,
- const UA_NodeId *sessionId,
- void* sessionContext,
- const UA_NodeId *nodeId,
- UA_DateTime start,
- UA_DateTime end,
- UA_UInt32 numValuesPerNode,
- UA_Boolean returnBounds,
- size_t *startIndex,
- size_t *endIndex,
- UA_Boolean *addFirst,
- UA_Boolean *addLast,
- UA_Boolean *reverse)
- {
- size_t storeEnd = backend->getEnd(server, backend->context, sessionId, sessionContext, nodeId);
- *startIndex = storeEnd;
- *endIndex = storeEnd;
- *addFirst = false;
- *addLast = false;
- if (end == LLONG_MIN) {
- *reverse = false;
- } else if (start == LLONG_MIN) {
- *reverse = true;
- } else {
- *reverse = end < start;
- }
- UA_Boolean equal = start == end;
- size_t size = 0;
- if (storeEnd > 0) {
- if (equal) {
- if (returnBounds) {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE);
- if (*startIndex == storeEnd) {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER);
- *addFirst = true;
- }
- *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER);
- size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *startIndex, *endIndex);
- } else {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL);
- *endIndex = *startIndex;
- if (*startIndex == storeEnd)
- size = 0;
- else
- size = 1;
- }
- } else if (start == LLONG_MIN) {
- *endIndex = 0;
- if (returnBounds) {
- *addLast = true;
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_AFTER);
- if (*startIndex == storeEnd) {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_BEFORE);
- *addFirst = true;
- }
- } else {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_BEFORE);
- }
- size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *endIndex, *startIndex);
- } else if (end == LLONG_MIN) {
- *endIndex = storeEnd - 1;
- if (returnBounds) {
- *addLast = true;
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE);
- if (*startIndex == storeEnd) {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER);
- *addFirst = true;
- }
- } else {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_AFTER);
- }
- size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *startIndex, *endIndex);
- } else if (*reverse) {
- if (returnBounds) {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_AFTER);
- if (*startIndex == storeEnd) {
- *addFirst = true;
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_BEFORE);
- }
- *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_BEFORE);
- if (*endIndex == storeEnd) {
- *addLast = true;
- *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_AFTER);
- }
- } else {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE);
- *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_AFTER);
- }
- size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *endIndex, *startIndex);
- } else {
- if (returnBounds) {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_BEFORE);
- if (*startIndex == storeEnd) {
- *addFirst = true;
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_AFTER);
- }
- *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_EQUAL_OR_AFTER);
- if (*endIndex == storeEnd) {
- *addLast = true;
- *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_BEFORE);
- }
- } else {
- *startIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, start, MATCH_EQUAL_OR_AFTER);
- *endIndex = backend->getDateTimeMatch(server, backend->context, sessionId, sessionContext, nodeId, end, MATCH_BEFORE);
- }
- size = backend->resultSize(server, backend->context, sessionId, sessionContext, nodeId, *startIndex, *endIndex);
- }
- } else if (returnBounds) {
- *addLast = true;
- *addFirst = true;
- }
- if (*addLast)
- ++size;
- if (*addFirst)
- ++size;
- if (numValuesPerNode > 0 && size > numValuesPerNode) {
- size = numValuesPerNode;
- *addLast = false;
- }
- return size;
- }
- static UA_StatusCode
- getHistoryData_service_default(const UA_HistoryDataBackend* backend,
- const UA_DateTime start,
- const UA_DateTime end,
- UA_Server *server,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId* nodeId,
- size_t maxSize,
- UA_UInt32 numValuesPerNode,
- UA_Boolean returnBounds,
- UA_TimestampsToReturn timestampsToReturn,
- UA_NumericRange range,
- UA_Boolean releaseContinuationPoints,
- const UA_ByteString *continuationPoint,
- UA_ByteString *outContinuationPoint,
- size_t *resultSize,
- UA_DataValue ** result)
- {
- size_t skip = 0;
- UA_ByteString backendContinuationPoint;
- UA_ByteString_init(&backendContinuationPoint);
- if (continuationPoint->length > 0) {
- if (continuationPoint->length >= sizeof(size_t)) {
- skip = *((size_t*)(continuationPoint->data));
- if (continuationPoint->length > 0) {
- backendContinuationPoint.length = continuationPoint->length - sizeof(size_t);
- backendContinuationPoint.data = continuationPoint->data + sizeof(size_t);
- }
- } else {
- return UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
- }
- }
- size_t storeEnd = backend->getEnd(server, backend->context, sessionId, sessionContext, nodeId);
- size_t startIndex;
- size_t endIndex;
- UA_Boolean addFirst;
- UA_Boolean addLast;
- UA_Boolean reverse;
- size_t _resultSize = getResultSize_service_default(backend,
- server,
- sessionId,
- sessionContext,
- nodeId,
- start,
- end,
- numValuesPerNode == 0 ? 0 : numValuesPerNode + (UA_UInt32)skip,
- returnBounds,
- &startIndex,
- &endIndex,
- &addFirst,
- &addLast,
- &reverse);
- *resultSize = _resultSize - skip;
- if (*resultSize > maxSize) {
- *resultSize = maxSize;
- }
- UA_DataValue *outResult= (UA_DataValue*)UA_Array_new(*resultSize, &UA_TYPES[UA_TYPES_DATAVALUE]);
- if (!outResult) {
- *resultSize = 0;
- return UA_STATUSCODE_BADOUTOFMEMORY;
- }
- *result = outResult;
- size_t counter = 0;
- if (addFirst) {
- if (skip == 0) {
- outResult[counter].hasStatus = true;
- outResult[counter].status = UA_STATUSCODE_BADBOUNDNOTFOUND;
- outResult[counter].hasSourceTimestamp = true;
- if (start == LLONG_MIN) {
- outResult[counter].sourceTimestamp = end;
- } else {
- outResult[counter].sourceTimestamp = start;
- }
- ++counter;
- }
- }
- UA_ByteString backendOutContinuationPoint;
- UA_ByteString_init(&backendOutContinuationPoint);
- if (endIndex != storeEnd && startIndex != storeEnd) {
- size_t retval = 0;
- size_t valueSize = *resultSize - counter;
- if (valueSize + skip > _resultSize - addFirst - addLast) {
- if (skip == 0) {
- valueSize = _resultSize - addFirst - addLast;
- } else {
- valueSize = _resultSize - skip - addLast;
- }
- }
- UA_StatusCode ret = UA_STATUSCODE_GOOD;
- if (valueSize > 0)
- ret = backend->copyDataValues(server,
- backend->context,
- sessionId,
- sessionContext,
- nodeId,
- startIndex,
- endIndex,
- reverse,
- valueSize,
- range,
- releaseContinuationPoints,
- &backendContinuationPoint,
- &backendOutContinuationPoint,
- &retval,
- &outResult[counter]);
- if (ret != UA_STATUSCODE_GOOD) {
- UA_Array_delete(outResult, *resultSize, &UA_TYPES[UA_TYPES_DATAVALUE]);
- *result = NULL;
- *resultSize = 0;
- return ret;
- }
- counter += retval;
- }
- if (addLast && counter < *resultSize) {
- outResult[counter].hasStatus = true;
- outResult[counter].status = UA_STATUSCODE_BADBOUNDNOTFOUND;
- outResult[counter].hasSourceTimestamp = true;
- if (start == LLONG_MIN && storeEnd != backend->firstIndex(server, backend->context, sessionId, sessionContext, nodeId)) {
- outResult[counter].sourceTimestamp = backend->getDataValue(server, backend->context, sessionId, sessionContext, nodeId, endIndex)->sourceTimestamp - UA_DATETIME_SEC;
- } else if (end == LLONG_MIN && storeEnd != backend->firstIndex(server, backend->context, sessionId, sessionContext, nodeId)) {
- outResult[counter].sourceTimestamp = backend->getDataValue(server, backend->context, sessionId, sessionContext, nodeId, endIndex)->sourceTimestamp + UA_DATETIME_SEC;
- } else {
- outResult[counter].sourceTimestamp = end;
- }
- }
- // there are more values
- if (skip + *resultSize < _resultSize
- // there are not more values for this request, but there are more values in database
- || (backendOutContinuationPoint.length > 0
- && numValuesPerNode != 0)
- // we deliver just one value which is a FIRST/LAST value
- || (skip == 0
- && addFirst == true
- && *resultSize == 1)) {
- if(UA_ByteString_allocBuffer(outContinuationPoint, backendOutContinuationPoint.length + sizeof(size_t))
- != UA_STATUSCODE_GOOD) {
- return UA_STATUSCODE_BADOUTOFMEMORY;
- }
- *((size_t*)(outContinuationPoint->data)) = skip + *resultSize;
- memcpy(outContinuationPoint->data + sizeof(size_t), backendOutContinuationPoint.data, backendOutContinuationPoint.length);
- }
- UA_ByteString_deleteMembers(&backendOutContinuationPoint);
- return UA_STATUSCODE_GOOD;
- }
- static void
- readRaw_service_default(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_RequestHeader *requestHeader,
- const UA_ReadRawModifiedDetails *historyReadDetails,
- UA_TimestampsToReturn timestampsToReturn,
- UA_Boolean releaseContinuationPoints,
- size_t nodesToReadSize,
- const UA_HistoryReadValueId *nodesToRead,
- UA_HistoryReadResponse *response,
- UA_HistoryData * const * const historyData)
- {
- UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context;
- for (size_t i = 0; i < nodesToReadSize; ++i) {
- UA_Byte accessLevel = 0;
- UA_Server_readAccessLevel(server,
- nodesToRead[i].nodeId,
- &accessLevel);
- if (!(accessLevel & UA_ACCESSLEVELMASK_HISTORYREAD)) {
- response->results[i].statusCode = UA_STATUSCODE_BADUSERACCESSDENIED;
- continue;
- }
- UA_Boolean historizing = false;
- UA_Server_readHistorizing(server,
- nodesToRead[i].nodeId,
- &historizing);
- if (!historizing) {
- response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
- continue;
- }
- const UA_HistorizingNodeIdSettings *setting = ctx->gathering.getHistorizingSetting(
- server,
- ctx->gathering.context,
- &nodesToRead[i].nodeId);
- if (!setting) {
- response->results[i].statusCode = UA_STATUSCODE_BADHISTORYOPERATIONINVALID;
- continue;
- }
- if (historyReadDetails->returnBounds && !setting->historizingBackend.boundSupported(
- server,
- setting->historizingBackend.context,
- sessionId,
- sessionContext,
- &nodesToRead[i].nodeId)) {
- response->results[i].statusCode = UA_STATUSCODE_BADBOUNDNOTSUPPORTED;
- continue;
- }
- if (!setting->historizingBackend.timestampsToReturnSupported(
- server,
- setting->historizingBackend.context,
- sessionId,
- sessionContext,
- &nodesToRead[i].nodeId,
- timestampsToReturn)) {
- response->results[i].statusCode = UA_STATUSCODE_BADTIMESTAMPNOTSUPPORTED;
- continue;
- }
- UA_NumericRange range;
- range.dimensionsSize = 0;
- range.dimensions = NULL;
- if (nodesToRead[i].indexRange.length > 0) {
- UA_StatusCode rangeParseResult = UA_NumericRange_parseFromString(&range, &nodesToRead[i].indexRange);
- if (rangeParseResult != UA_STATUSCODE_GOOD) {
- response->results[i].statusCode = rangeParseResult;
- continue;
- }
- }
- UA_StatusCode getHistoryDataStatusCode;
- if (setting->historizingBackend.getHistoryData) {
- getHistoryDataStatusCode = setting->historizingBackend.getHistoryData(
- server,
- sessionId,
- sessionContext,
- &setting->historizingBackend,
- historyReadDetails->startTime,
- historyReadDetails->endTime,
- &nodesToRead[i].nodeId,
- setting->maxHistoryDataResponseSize,
- historyReadDetails->numValuesPerNode,
- historyReadDetails->returnBounds,
- timestampsToReturn,
- range,
- releaseContinuationPoints,
- &nodesToRead[i].continuationPoint,
- &response->results[i].continuationPoint,
- historyData[i]);
- } else {
- getHistoryDataStatusCode = getHistoryData_service_default(
- &setting->historizingBackend,
- historyReadDetails->startTime,
- historyReadDetails->endTime,
- server,
- sessionId,
- sessionContext,
- &nodesToRead[i].nodeId,
- setting->maxHistoryDataResponseSize,
- historyReadDetails->numValuesPerNode,
- historyReadDetails->returnBounds,
- timestampsToReturn,
- range,
- releaseContinuationPoints,
- &nodesToRead[i].continuationPoint,
- &response->results[i].continuationPoint,
- &historyData[i]->dataValuesSize,
- &historyData[i]->dataValues);
- }
- if (getHistoryDataStatusCode != UA_STATUSCODE_GOOD) {
- response->results[i].statusCode = getHistoryDataStatusCode;
- continue;
- }
- }
- response->responseHeader.serviceResult = UA_STATUSCODE_GOOD;
- return;
- }
- static void
- setValue_service_default(UA_Server *server,
- void *context,
- const UA_NodeId *sessionId,
- void *sessionContext,
- const UA_NodeId *nodeId,
- UA_Boolean historizing,
- const UA_DataValue *value)
- {
- UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)context;
- if (ctx->gathering.setValue)
- ctx->gathering.setValue(server,
- ctx->gathering.context,
- sessionId,
- sessionContext,
- nodeId,
- historizing,
- value);
- }
- static void
- deleteMembers_service_default(UA_HistoryDatabase *hdb)
- {
- if (hdb == NULL || hdb->context == NULL)
- return;
- UA_HistoryDatabaseContext_default *ctx = (UA_HistoryDatabaseContext_default*)hdb->context;
- ctx->gathering.deleteMembers(&ctx->gathering);
- UA_free(ctx);
- }
- UA_HistoryDatabase
- UA_HistoryDatabase_default(UA_HistoryDataGathering gathering)
- {
- UA_HistoryDatabase hdb;
- UA_HistoryDatabaseContext_default *context =
- (UA_HistoryDatabaseContext_default*)
- UA_calloc(1, sizeof(UA_HistoryDatabaseContext_default));
- context->gathering = gathering;
- hdb.context = context;
- hdb.readRaw = &readRaw_service_default;
- hdb.setValue = &setValue_service_default;
- hdb.deleteMembers = &deleteMembers_service_default;
- return hdb;
- }
|