#ifndef BACKEND_H #define BACKEND_H #include 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