blob: e276d44dccfca5d1e94b198ea2fc3d03c68e2f29 [file] [log] [blame]
/* 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 <open62541/client_subscriptions.h>
#include <open62541/plugin/historydata/history_data_gathering_default.h>
#include <open62541/plugin/historydata/history_database_default.h>
#include <string.h>
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 = &registerNodeId_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;
}