|
@@ -0,0 +1,345 @@
|
|
|
+#ifndef BACKEND_H
|
|
|
+#define BACKEND_H
|
|
|
+
|
|
|
+#include "ua_plugin_history_data_backend.h"
|
|
|
+
|
|
|
+struct tupel {
|
|
|
+ size_t index;
|
|
|
+ UA_DataValue value;
|
|
|
+};
|
|
|
+
|
|
|
+// null terminated array
|
|
|
+struct context_randomindextest {
|
|
|
+ // null terminated array
|
|
|
+ struct tupel *tupels;
|
|
|
+ size_t tupelSize;
|
|
|
+};
|
|
|
+
|
|
|
+static size_t
|
|
|
+indexByIndex_randomindextest(struct context_randomindextest *context, size_t index)
|
|
|
+{
|
|
|
+ for (size_t i = 0; i < context->tupelSize; ++i) {
|
|
|
+ if (context->tupels[i].index == index)
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ return context->tupelSize;
|
|
|
+}
|
|
|
+
|
|
|
+// last value should be NULL, all values must be sorted
|
|
|
+static struct context_randomindextest*
|
|
|
+generateTestContext_randomindextest(const UA_DateTime *data) {
|
|
|
+ size_t count = 0;
|
|
|
+ while(data[count++]);
|
|
|
+ struct context_randomindextest* ret = (struct context_randomindextest*)UA_calloc(1, sizeof(struct context_randomindextest));
|
|
|
+ ret->tupels = (struct tupel*)UA_calloc(count, sizeof(struct tupel));
|
|
|
+ ret->tupelSize = count;
|
|
|
+ UA_DateTime *sortData;
|
|
|
+ UA_StatusCode retval = UA_Array_copy(data, count, (void**)&sortData, &UA_TYPES[UA_TYPES_DATETIME]);
|
|
|
+ if (retval != UA_STATUSCODE_GOOD)
|
|
|
+ return NULL;
|
|
|
+ for (size_t i = 0; i < count; ++i) {
|
|
|
+ size_t current = 0;
|
|
|
+ for (size_t j = 1; j < count-1; ++j){
|
|
|
+ UA_DateTime currentDate = sortData[current];
|
|
|
+ UA_DateTime jDate = sortData[j];
|
|
|
+ if (currentDate > jDate) {
|
|
|
+ current = j;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ UA_DateTime nextValue = i == count-1 ? 0 : sortData[current];
|
|
|
+ sortData[current] = LLONG_MAX;
|
|
|
+ bool unique;
|
|
|
+ do {
|
|
|
+ unique = true;
|
|
|
+ ret->tupels[i].index = UA_UInt32_random();
|
|
|
+ for (size_t j = 0; j < i; ++j)
|
|
|
+ if (i != j && ret->tupels[i].index == ret->tupels[j].index)
|
|
|
+ unique = false;
|
|
|
+ } while (!unique);
|
|
|
+ UA_DataValue_init(&ret->tupels[i].value);
|
|
|
+ ret->tupels[i].value.hasValue = true;
|
|
|
+ UA_Variant_setScalarCopy(&ret->tupels[i].value.value, &nextValue, &UA_TYPES[UA_TYPES_INT64]);
|
|
|
+ ret->tupels[i].value.hasStatus = true;
|
|
|
+ ret->tupels[i].value.status = UA_STATUSCODE_GOOD;
|
|
|
+ ret->tupels[i].value.hasServerTimestamp = true;
|
|
|
+ ret->tupels[i].value.serverTimestamp = nextValue;
|
|
|
+ ret->tupels[i].value.hasSourceTimestamp = true;
|
|
|
+ ret->tupels[i].value.sourceTimestamp = nextValue;
|
|
|
+ }
|
|
|
+ UA_free(sortData);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+deleteMembers_randomindextest(UA_HistoryDataBackend *backend)
|
|
|
+{
|
|
|
+ struct context_randomindextest* context = (struct context_randomindextest*)backend->context;
|
|
|
+ for (size_t i = 0; i < context->tupelSize; ++i) {
|
|
|
+ UA_DataValue_deleteMembers(&context->tupels[i].value);
|
|
|
+ }
|
|
|
+ UA_free(context->tupels);
|
|
|
+ UA_free(context);
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode
|
|
|
+serverSetHistoryData_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId,
|
|
|
+ UA_Boolean historizing,
|
|
|
+ const UA_DataValue *value)
|
|
|
+{
|
|
|
+ // we do not add data to the test data
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static size_t
|
|
|
+getEnd_private(struct context_randomindextest* context)
|
|
|
+{
|
|
|
+ return context->tupels[context->tupelSize-1].index;
|
|
|
+}
|
|
|
+
|
|
|
+static size_t
|
|
|
+getEnd_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId)
|
|
|
+{
|
|
|
+ struct context_randomindextest* context = (struct context_randomindextest*)hdbContext;
|
|
|
+ return getEnd_private(context);
|
|
|
+}
|
|
|
+
|
|
|
+static size_t
|
|
|
+lastIndex_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId)
|
|
|
+{
|
|
|
+ struct context_randomindextest* context = (struct context_randomindextest*)hdbContext;
|
|
|
+ return context->tupels[context->tupelSize-2].index;
|
|
|
+}
|
|
|
+
|
|
|
+static size_t
|
|
|
+firstIndex_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId)
|
|
|
+{
|
|
|
+ struct context_randomindextest* context = (struct context_randomindextest*)hdbContext;
|
|
|
+ return context->tupels[0].index;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_Boolean
|
|
|
+search_randomindextest(struct context_randomindextest* ctx,
|
|
|
+ const UA_DateTime timestamp,
|
|
|
+ size_t *index) {
|
|
|
+ for (size_t i = 0; i < ctx->tupelSize; ++i) {
|
|
|
+ if (ctx->tupels[i].value.sourceTimestamp == timestamp) {
|
|
|
+ *index = i;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (ctx->tupels[i].value.sourceTimestamp > timestamp) {
|
|
|
+ *index = i;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ *index = ctx->tupelSize-1;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static size_t
|
|
|
+getDateTimeMatch_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId,
|
|
|
+ const UA_DateTime timestamp,
|
|
|
+ const MatchStrategy strategy)
|
|
|
+{
|
|
|
+ struct context_randomindextest* context = (struct context_randomindextest*)hdbContext;
|
|
|
+ size_t current;
|
|
|
+
|
|
|
+ UA_Boolean retval = search_randomindextest(context, timestamp, ¤t);
|
|
|
+
|
|
|
+ if ((strategy == MATCH_EQUAL
|
|
|
+ || strategy == MATCH_EQUAL_OR_AFTER
|
|
|
+ || strategy == MATCH_EQUAL_OR_BEFORE)
|
|
|
+ && retval)
|
|
|
+ return context->tupels[current].index;
|
|
|
+ switch (strategy) {
|
|
|
+ case MATCH_AFTER:
|
|
|
+ if (retval)
|
|
|
+ return context->tupels[current+1].index;
|
|
|
+ return context->tupels[current].index;
|
|
|
+ case MATCH_EQUAL_OR_AFTER:
|
|
|
+ return context->tupels[current].index;
|
|
|
+ case MATCH_EQUAL_OR_BEFORE:
|
|
|
+ // retval == true aka "equal" is handled before
|
|
|
+ // Fall through if !retval
|
|
|
+ case MATCH_BEFORE:
|
|
|
+ if (current > 0)
|
|
|
+ return context->tupels[current-1].index;
|
|
|
+ else
|
|
|
+ return context->tupels[context->tupelSize-1].index;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return context->tupels[context->tupelSize-1].index;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#define MYABSSUB(a,b) ((a)<(b)?((b)-(a)):(a)-(b))
|
|
|
+static size_t
|
|
|
+resultSize_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId,
|
|
|
+ size_t startIndex,
|
|
|
+ size_t endIndex)
|
|
|
+{
|
|
|
+ struct context_randomindextest* context = (struct context_randomindextest*)hdbContext;
|
|
|
+ if (startIndex == getEnd_private(context)
|
|
|
+ || endIndex == getEnd_private(context))
|
|
|
+ return 0;
|
|
|
+ size_t realEndIndex = indexByIndex_randomindextest(context, endIndex);
|
|
|
+ size_t realStartIndex = indexByIndex_randomindextest(context, startIndex);
|
|
|
+ size_t result = MYABSSUB(realEndIndex,realStartIndex);
|
|
|
+ return result+1;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_StatusCode
|
|
|
+copyDataValues_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId,
|
|
|
+ size_t startIndex,
|
|
|
+ size_t endIndex,
|
|
|
+ UA_Boolean reverse,
|
|
|
+ size_t maxValues,
|
|
|
+ UA_NumericRange range,
|
|
|
+ UA_Boolean releaseContinuationPoints,
|
|
|
+ const UA_ByteString *continuationPoint,
|
|
|
+ UA_ByteString *outContinuationPoint,
|
|
|
+ size_t *providedValues,
|
|
|
+ UA_DataValue *values)
|
|
|
+{
|
|
|
+ size_t skip = 0;
|
|
|
+ if (continuationPoint->length > 0) {
|
|
|
+ if (continuationPoint->length == sizeof(size_t)) {
|
|
|
+ skip = *((size_t*)(continuationPoint->data));
|
|
|
+ } else {
|
|
|
+ return UA_STATUSCODE_BADCONTINUATIONPOINTINVALID;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ struct context_randomindextest* context = (struct context_randomindextest*)hdbContext;
|
|
|
+ size_t index = indexByIndex_randomindextest(context,startIndex);
|
|
|
+ size_t counter = 0;
|
|
|
+ size_t skipedValues = 0;
|
|
|
+ if (reverse) {
|
|
|
+ while (index >= indexByIndex_randomindextest(context,endIndex)
|
|
|
+ && index < context->tupelSize-1
|
|
|
+ && counter < maxValues) {
|
|
|
+ if (skipedValues++ >= skip) {
|
|
|
+ UA_DataValue_copy(&context->tupels[index].value, &values[counter]);
|
|
|
+ ++counter;
|
|
|
+ }
|
|
|
+ --index;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ while (index <= indexByIndex_randomindextest(context,endIndex) && counter < maxValues) {
|
|
|
+ if (skipedValues++ >= skip) {
|
|
|
+ UA_DataValue_copy(&context->tupels[index].value, &values[counter]);
|
|
|
+ ++counter;
|
|
|
+ }
|
|
|
+ ++index;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (providedValues)
|
|
|
+ *providedValues = counter;
|
|
|
+
|
|
|
+ if ((!reverse && (indexByIndex_randomindextest(context,endIndex)-indexByIndex_randomindextest(context,startIndex)-skip+1) > counter)
|
|
|
+ || (reverse && (indexByIndex_randomindextest(context,startIndex)-indexByIndex_randomindextest(context,endIndex)-skip+1) > counter)) {
|
|
|
+ outContinuationPoint->length = sizeof(size_t);
|
|
|
+ size_t t = sizeof(size_t);
|
|
|
+ outContinuationPoint->data = (UA_Byte*)UA_malloc(t);
|
|
|
+ *((size_t*)(outContinuationPoint->data)) = skip + counter;
|
|
|
+ }
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
+static const UA_DataValue*
|
|
|
+getDataValue_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId,
|
|
|
+ size_t index)
|
|
|
+{
|
|
|
+ struct context_randomindextest* context = (struct context_randomindextest*)hdbContext;
|
|
|
+ size_t realIndex = indexByIndex_randomindextest(context, index);
|
|
|
+ return &context->tupels[realIndex].value;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_Boolean
|
|
|
+boundSupported_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId)
|
|
|
+{
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static UA_Boolean
|
|
|
+timestampsToReturnSupported_randomindextest(UA_Server *server,
|
|
|
+ void *hdbContext,
|
|
|
+ const UA_NodeId *sessionId,
|
|
|
+ void *sessionContext,
|
|
|
+ const UA_NodeId *nodeId,
|
|
|
+ const UA_TimestampsToReturn timestampsToReturn)
|
|
|
+{
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+UA_HistoryDataBackend
|
|
|
+UA_HistoryDataBackend_randomindextest(UA_DateTime *data);
|
|
|
+
|
|
|
+UA_HistoryDataBackend
|
|
|
+UA_HistoryDataBackend_randomindextest(UA_DateTime *data)
|
|
|
+{
|
|
|
+ UA_HistoryDataBackend result;
|
|
|
+ memset(&result, 0, sizeof(UA_HistoryDataBackend));
|
|
|
+ result.serverSetHistoryData = &serverSetHistoryData_randomindextest;
|
|
|
+ result.resultSize = &resultSize_randomindextest;
|
|
|
+ result.getEnd = &getEnd_randomindextest;
|
|
|
+ result.lastIndex = &lastIndex_randomindextest;
|
|
|
+ result.firstIndex = &firstIndex_randomindextest;
|
|
|
+ result.getDateTimeMatch = &getDateTimeMatch_randomindextest;
|
|
|
+ result.copyDataValues = ©DataValues_randomindextest;
|
|
|
+ result.getDataValue = &getDataValue_randomindextest;
|
|
|
+ result.boundSupported = &boundSupported_randomindextest;
|
|
|
+ result.timestampsToReturnSupported = ×tampsToReturnSupported_randomindextest;
|
|
|
+ result.deleteMembers = &deleteMembers_randomindextest;
|
|
|
+ result.getHistoryData = NULL;
|
|
|
+ result.context = generateTestContext_randomindextest(data);
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+UA_HistoryDataBackend_randomindextest_deleteMembers(UA_HistoryDataBackend *backend);
|
|
|
+
|
|
|
+void
|
|
|
+UA_HistoryDataBackend_randomindextest_deleteMembers(UA_HistoryDataBackend *backend)
|
|
|
+{
|
|
|
+ deleteMembers_randomindextest(backend);
|
|
|
+}
|
|
|
+
|
|
|
+#endif // BACKEND_H
|