/* 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 (Author: Peter Rustler) */ #include #include #include #include typedef struct { UA_NodeId nodeId; UA_HistorizingNodeIdSettings setting; UA_MonitoredItemCreateResult monitoredResult; } UA_NodeIdStoreContextItem_gathering_default; typedef struct { UA_NodeIdStoreContextItem_gathering_default *dataStore; size_t storeEnd; size_t storeSize; } UA_NodeIdStoreContext; static void dataChangeCallback_gathering_default(UA_Server *server, UA_UInt32 monitoredItemId, void *monitoredItemContext, const UA_NodeId *nodeId, void *nodeContext, UA_UInt32 attributeId, const UA_DataValue *value) { UA_NodeIdStoreContextItem_gathering_default *context = (UA_NodeIdStoreContextItem_gathering_default*)monitoredItemContext; context->setting.historizingBackend.serverSetHistoryData(server, context->setting.historizingBackend.context, NULL, NULL, nodeId, UA_TRUE, value); } static UA_NodeIdStoreContextItem_gathering_default* getNodeIdStoreContextItem_gathering_default(UA_NodeIdStoreContext *context, const UA_NodeId *nodeId) { for (size_t i = 0; i < context->storeEnd; ++i) { if (UA_NodeId_equal(&context->dataStore[i].nodeId, nodeId)) { return &context->dataStore[i]; } } return NULL; } static UA_StatusCode startPoll(UA_Server *server, UA_NodeIdStoreContextItem_gathering_default *item) { UA_MonitoredItemCreateRequest monitorRequest = UA_MonitoredItemCreateRequest_default(item->nodeId); monitorRequest.requestedParameters.samplingInterval = (double)item->setting.pollingInterval; monitorRequest.monitoringMode = UA_MONITORINGMODE_REPORTING; item->monitoredResult = UA_Server_createDataChangeMonitoredItem(server, UA_TIMESTAMPSTORETURN_BOTH, monitorRequest, item, &dataChangeCallback_gathering_default); return item->monitoredResult.statusCode; } static UA_StatusCode stopPoll(UA_Server *server, UA_NodeIdStoreContextItem_gathering_default *item) { UA_StatusCode retval = UA_Server_deleteMonitoredItem(server, item->monitoredResult.monitoredItemId); UA_MonitoredItemCreateResult_init(&item->monitoredResult); return retval; } static UA_StatusCode stopPoll_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext *)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (!item) { return UA_STATUSCODE_BADNODEIDUNKNOWN; } if (item->setting.historizingUpdateStrategy != UA_HISTORIZINGUPDATESTRATEGY_POLL) return UA_STATUSCODE_BADNODEIDINVALID; if (item->monitoredResult.monitoredItemId == 0) return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; return stopPoll(server, item); } static UA_StatusCode startPoll_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext *)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (!item) { return UA_STATUSCODE_BADNODEIDUNKNOWN; } if (item->setting.historizingUpdateStrategy != UA_HISTORIZINGUPDATESTRATEGY_POLL) return UA_STATUSCODE_BADNODEIDINVALID; if (item->monitoredResult.monitoredItemId > 0) return UA_STATUSCODE_BADMONITOREDITEMIDINVALID; return startPoll(server, item); } static UA_StatusCode registerNodeId_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId, const UA_HistorizingNodeIdSettings setting) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)context; if (getNodeIdStoreContextItem_gathering_default(ctx, nodeId)) { return UA_STATUSCODE_BADNODEIDEXISTS; } if (ctx->storeEnd >= ctx->storeSize) { size_t newStoreSize = ctx->storeSize * 2; ctx->dataStore = (UA_NodeIdStoreContextItem_gathering_default*)UA_realloc(ctx->dataStore, (newStoreSize * sizeof(UA_NodeIdStoreContextItem_gathering_default))); if (!ctx->dataStore) { ctx->storeSize = 0; return UA_STATUSCODE_BADOUTOFMEMORY; } ctx->storeSize = newStoreSize; } UA_NodeId_copy(nodeId, &ctx->dataStore[ctx->storeEnd].nodeId); size_t current = ctx->storeEnd; ctx->dataStore[current].setting = setting; ++ctx->storeEnd; return UA_STATUSCODE_GOOD; } static const UA_HistorizingNodeIdSettings* getHistorizingSetting_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (item) { return &item->setting; } return NULL; } static void deleteMembers_gathering_default(UA_HistoryDataGathering *gathering) { if (gathering == NULL || gathering->context == NULL) return; UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)gathering->context; for (size_t i = 0; i < ctx->storeEnd; ++i) { UA_NodeId_deleteMembers(&ctx->dataStore[i].nodeId); // There is still a monitored item present for this gathering // You need to remove it with UA_Server_deleteMonitoredItem UA_assert(ctx->dataStore[i].monitoredResult.monitoredItemId == 0); } UA_free(ctx->dataStore); UA_free(gathering->context); } static UA_Boolean updateNodeIdSetting_gathering_default(UA_Server *server, void *context, const UA_NodeId *nodeId, const UA_HistorizingNodeIdSettings setting) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (!item) { return false; } stopPoll_gathering_default(server, context, nodeId); item->setting = setting; return true; } static void setValue_gathering_default(UA_Server *server, void *context, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, UA_Boolean historizing, const UA_DataValue *value) { UA_NodeIdStoreContext *ctx = (UA_NodeIdStoreContext*)context; UA_NodeIdStoreContextItem_gathering_default *item = getNodeIdStoreContextItem_gathering_default(ctx, nodeId); if (!item) { return; } if (item->setting.historizingUpdateStrategy == UA_HISTORIZINGUPDATESTRATEGY_VALUESET) { item->setting.historizingBackend.serverSetHistoryData(server, item->setting.historizingBackend.context, sessionId, sessionContext, nodeId, historizing, value); } } UA_HistoryDataGathering UA_HistoryDataGathering_Default(size_t initialNodeIdStoreSize) { UA_HistoryDataGathering gathering; memset(&gathering, 0, sizeof(UA_HistoryDataGathering)); gathering.setValue = &setValue_gathering_default; gathering.getHistorizingSetting = &getHistorizingSetting_gathering_default; gathering.registerNodeId = ®isterNodeId_gathering_default; gathering.startPoll = &startPoll_gathering_default; gathering.stopPoll = &stopPoll_gathering_default; gathering.deleteMembers = &deleteMembers_gathering_default; gathering.updateNodeIdSetting = &updateNodeIdSetting_gathering_default; UA_NodeIdStoreContext *context = (UA_NodeIdStoreContext*)UA_calloc(1, sizeof(UA_NodeIdStoreContext)); context->storeEnd = 0; context->storeSize = initialNodeIdStoreSize; context->dataStore = (UA_NodeIdStoreContextItem_gathering_default*)UA_calloc(initialNodeIdStoreSize, sizeof(UA_NodeIdStoreContextItem_gathering_default)); gathering.context = context; return gathering; }