ANSI C UA Server SDK  1.6.0.341
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Modules Pages
custom_provider_subscription.c
/*****************************************************************************
* *
* Copyright (c) 2006-2016 Unified Automation GmbH. All rights reserved. *
* *
* Software License Agreement ("SLA") Version 2.5 *
* *
* Unless explicitly acquired and licensed from Licensor under another *
* license, the contents of this file are subject to the Software License *
* Agreement ("SLA") Version 2.5, or subsequent versions as allowed by the *
* SLA, and You may not copy or use this file in either source code or *
* executable form, except in compliance with the terms and conditions of *
* the SLA. *
* *
* All software distributed under the SLA is provided strictly on an "AS *
* IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, *
* AND LICENSOR HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT *
* LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR *
* PURPOSE, QUIET ENJOYMENT, OR NON-INFRINGEMENT. See the SLA for specific *
* language governing rights and limitations under the SLA. *
* *
* The complete license agreement can be found here: *
* http://unifiedautomation.com/License/SLA/2.5/ *
* *
* Project: Unified Automation ANSI C based OPC UA Server SDK *
* *
*****************************************************************************/
#include <uaserver_config.h>
#include <opcua_statuscodes.h>
#include <uabase_p_atomic.h>
#include <uaserver_monitoring.h>
#include <uaserver_monitoreditemmanager.h>
#include <uaserver_basenode.h>
#include <uaserver_utilities.h>
#include "custom_provider_helper.h"
OPCUA_BEGIN_EXTERN_C
#if OPCUA_USE_SYNCHRONISATION
static OpcUa_Mutex g_mtxList;
# define LOCK_LIST() OpcUa_Mutex_Lock(g_mtxList)
# define UNLOCK_LIST() OpcUa_Mutex_Unlock(g_mtxList)
#else
# define LOCK_LIST()
# define UNLOCK_LIST()
#endif
#define NUM_SAMPLINGRATES 7
static OpcUa_Int g_aiSamplingRates[NUM_SAMPLINGRATES] = {50, 100, 250, 500, 1000, 2500, 5000};
static OpcUa_Timer g_hSamplingTimers[NUM_SAMPLINGRATES];
static UaBase_Vector g_vecSamplingLists[NUM_SAMPLINGRATES];
/* Forward declarations */
IFMETHODIMP(CustomProvider_TimerCallback)(OpcUa_Void *a_pvCallbackData,
OpcUa_Timer a_hTimer,
OpcUa_UInt32 a_msecElapsed);
OpcUa_StatusCode CustomProvider_Subscription_Initialize()
{
OpcUa_UInt32 i;
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_Subscription_Initialize");
#if OPCUA_USE_SYNCHRONISATION
uStatus = OpcUa_Mutex_Create(&g_mtxList);
OpcUa_GotoErrorIfBad(uStatus);
#endif
/* init vectors and start sampling timers */
for (i = 0; i < NUM_SAMPLINGRATES; i++)
{
UaBase_Vector_Initialize(&g_vecSamplingLists[i], 10, 10);
uStatus = OpcUa_Timer_Create(&g_hSamplingTimers[i],
g_aiSamplingRates[i],
CustomProvider_TimerCallback,
OpcUa_Null,
&g_vecSamplingLists[i]);
OpcUa_GotoErrorIfBad(uStatus);
}
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
OpcUa_StatusCode CustomProvider_Subscription_Cleanup()
{
OpcUa_UInt32 i;
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_Subscription_Cleanup");
#if OPCUA_USE_SYNCHRONISATION
OpcUa_Mutex_Delete(&g_mtxList);
#endif
for (i = 0; i < NUM_SAMPLINGRATES; i++)
{
OpcUa_Timer_Delete(&g_hSamplingTimers[i]);
UaBase_Vector_Clear(&g_vecSamplingLists[i]);
}
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
IFMETHODIMP(CustomProvider_AddItem)(UaServer_MonitoredItem *a_pItem)
{
OpcUa_UInt32 i = 0;
OpcUa_UInt32 iDist = 0;
OpcUa_UInt32 iTmp = 0;
OpcUa_UInt32 iRevised = 0;
OpcUa_BaseNode *pNode = OpcUa_Null;
UaServer_AddressSpace *pAddressSpace = &(g_pCustomProvider->AddressSpace);
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_AddItem");
OpcUa_ReturnErrorIfArgumentNull(a_pItem);
iDist = UaBase_P_Abs(a_pItem->iSamplingInterval - g_aiSamplingRates[0]);
/* Find closest sampling rate */
for (i = 1; i < NUM_SAMPLINGRATES; i++)
{
iTmp = UaBase_P_Abs(a_pItem->iSamplingInterval - g_aiSamplingRates[i]);
if (iTmp < iDist)
{
iDist = iTmp;
iRevised = i;
}
}
/* Revise values */
a_pItem->iSamplingInterval = g_aiSamplingRates[iRevised];
/* Get the requested node */
UaServer_GetNode(pAddressSpace, &a_pItem->NodeId, &pNode);
if (pNode)
{
if (a_pItem->ItemType == DATA)
{
UaServer_MonitoredItemData* pMonitoredItemData = (UaServer_MonitoredItemData*)a_pItem;
if (OpcUa_BaseNode_GetClass(pNode) == OpcUa_NodeClass_Variable &&
pMonitoredItemData->AttributeId == OpcUa_Attributes_Value)
{
OpcUa_DataVariable* pVariable = (OpcUa_DataVariable*)pNode;
/* check if user is allowed to read the value */
{
uStatus = OpcUa_BadNotReadable;
}
/* Check index range */
if (OpcUa_IsGood(uStatus) && pMonitoredItemData->NoOfIndexRanges > 0)
{
pMonitoredItemData->IndexRanges,
pMonitoredItemData->NoOfIndexRanges);
}
/* Check data change filter */
if (OpcUa_IsGood(uStatus) &&
pMonitoredItemData->DataChangeFilter.DeadbandType != OpcUa_DeadbandType_None)
{
if (pMonitoredItemData->DataChangeFilter.DeadbandType == OpcUa_DeadbandType_Percent)
{
uStatus = OpcUa_BadMonitoredItemFilterUnsupported;
}
else
{
switch (OpcUa_Variable_GetValue(pVariable)->Datatype)
{
case OpcUaType_SByte:
case OpcUaType_Byte:
case OpcUaType_Int16:
case OpcUaType_UInt16:
case OpcUaType_Int32:
case OpcUaType_UInt32:
case OpcUaType_Int64:
case OpcUaType_UInt64:
case OpcUaType_Float:
case OpcUaType_Double:
break;
default:
uStatus = OpcUa_BadFilterNotAllowed;
break;
}
}
}
if (OpcUa_IsGood(uStatus))
{
/* Add to list */
LOCK_LIST();
a_pItem->uProviderHandle = UaBase_Vector_Add(&g_vecSamplingLists[iRevised], pMonitoredItemData);
UNLOCK_LIST();
}
}
else
{
OpcUa_Variant currentValue;
OpcUa_Variant_Initialize(&currentValue);
/* check if user is allowed to read the attribute */
{
uStatus = OpcUa_BadNotReadable;
}
/* Check data change filter */
if (OpcUa_IsGood(uStatus) &&
pMonitoredItemData->DataChangeFilter.DeadbandType != OpcUa_DeadbandType_None)
{
uStatus = OpcUa_BadFilterNotAllowed;
}
/* Index range not available for non-value attribute */
if (OpcUa_IsGood(uStatus) && pMonitoredItemData->NoOfIndexRanges > 0)
{
uStatus = OpcUa_BadIndexRangeNoData;
}
/* Get value of the attribute */
if (OpcUa_IsGood(uStatus))
{
uStatus = UaServer_GetNonValueAttribute(pMonitoredItemData->AttributeId,
pNode,
&currentValue,
}
if (OpcUa_IsGood(uStatus))
{
/* Add to list */
LOCK_LIST();
a_pItem->uProviderHandle = UaBase_Vector_Add(&g_vecSamplingLists[iRevised], pMonitoredItemData);
UNLOCK_LIST();
}
OpcUa_Variant_Clear(&currentValue);
}
}
}
else
{
uStatus = OpcUa_BadNodeIdUnknown;
}
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
IFMETHODIMP(CustomProvider_RemoveItem)(UaServer_MonitoredItem *a_pItem)
{
UaBase_Vector *pVec = 0;
OpcUa_UInt32 size;
OpcUa_UInt32 i;
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_RemoveItem");
OpcUa_ReturnErrorIfArgumentNull(a_pItem);
for (i = 0; i < NUM_SAMPLINGRATES; i++)
{
if (g_aiSamplingRates[i] == a_pItem->iSamplingInterval)
{
pVec = &g_vecSamplingLists[i];
break;
}
}
if (pVec == OpcUa_Null)
{
return OpcUa_BadNotFound;
}
/* Only data monitored items are added to the lists, nothing to do for event monitored items */
if (a_pItem->ItemType == DATA)
{
LOCK_LIST();
size = UaBase_Vector_GetSize(pVec);
if (a_pItem->uProviderHandle < size)
{
/* a_pItem->uProviderHandle is position of item in vector.
If item to remove is not the last item, move last item
in list to location of item to remove and set uProviderHandle
of moved item accordingly */
if (a_pItem->uProviderHandle < size - 1)
{
UaServer_MonitoredItem *pTmpItem = UaBase_Vector_Get(pVec, size - 1);
pTmpItem->uProviderHandle = a_pItem->uProviderHandle;
UaBase_Vector_Set(pVec, a_pItem->uProviderHandle, pTmpItem);
}
UaBase_Vector_Resize(pVec, size - 1);
}
else
{
uStatus = OpcUa_BadInternalError;
}
UNLOCK_LIST();
}
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
IFMETHODIMP(CustomProvider_Subscribe)(UaServer_ProviderSubscribeContext *a_pCtx)
{
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_Subscribe");
/* Send the callback */
UaBase_Atomic_Increment(&a_pCtx->nOutstandingCbs);
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
OpcUa_Void CustomProvider_SampleData(OpcUa_Void* a_pvCallbackData)
{
unsigned int i = 0;
UaBase_Vector *pVecList = OpcUa_Null;
UaServer_MonitoredItemData *pMonitoredItemData = OpcUa_Null;
OpcUa_Variable *pVariable = OpcUa_Null;
unsigned int size;
pVecList = (UaBase_Vector*)a_pvCallbackData;
LOCK_LIST();
size = UaBase_Vector_GetSize(pVecList);
for (i = 0; i < size; i++)
{
pMonitoredItemData = (UaServer_MonitoredItemData*)UaBase_Vector_Get(pVecList, i);
/* The status of the items is always good, so status change will never trigger a data change */
if (pMonitoredItemData->MonitoringMode == OpcUa_MonitoringMode_Disabled)
{
continue;
}
/* Get node */
UaServer_GetNode(&g_pCustomProvider->AddressSpace,
&pMonitoredItemData->NodeId,
(OpcUa_BaseNode**)&pVariable);
if (pVariable)
{
/* Create new data value */
OpcUa_DataValue *pValue = (OpcUa_DataValue*)OpcUa_Alloc(sizeof(OpcUa_DataValue));
OpcUa_DataValue_Initialize(pValue);
if (pMonitoredItemData->AttributeId == OpcUa_Attributes_Value)
{
OpcUa_Variant_CopyTo(OpcUa_Variable_GetValue(pVariable), &pValue->Value);
}
else
{
(OpcUa_BaseNode*)pVariable,
&pValue->Value,
}
/* set timestamps */
pValue->ServerTimestamp = OpcUa_DateTime_UtcNow();
if (pMonitoredItemData->AttributeId == OpcUa_Attributes_Value)
{
pValue->SourceTimestamp = pValue->ServerTimestamp;
}
UaServer_NewItemValue(pMonitoredItemData, pValue);
}
}
UNLOCK_LIST();
}
IFMETHODIMP(CustomProvider_TimerCallback)(OpcUa_Void *a_pvCallbackData,
OpcUa_Timer a_hTimer,
OpcUa_UInt32 a_msecElapsed)
{
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_TimerCallback");
OpcUa_ReferenceParameter(a_hTimer);
OpcUa_ReferenceParameter(a_msecElapsed);
CustomProvider_SampleData(a_pvCallbackData);
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
OPCUA_END_EXTERN_C