UA ANSI C Server Professional  1.4.2.297
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Modules Pages
oversampling_provider_subscription.c
/******************************************************************************
**
** **************************** auto-generated ****************************
** This code was generated by a tool: UaModeler
** Runtime Version: 1.2.0, using ANSI C SDK 1.4.0 provider template (version 1)
**
** This is a template file that was generated for your convenience.
** This file will not be overwritten when generating code again.
** ADD YOUR IMPLEMTATION HERE!
** **************************** auto-generated ****************************
** Copyright (c) 2006-2014 Unified Automation GmbH All rights reserved.
** Web: http://www.unifiedautomation.com
**
** Software License Agreement ("SLA") Version 2.3
**
** 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.3, 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.
**
** Project: OPC Ansi C OverSamplingProvider for namespace http://www.unifiedautomation.com/OverSampling
**
** Description: OPC Unified Architecture Software Development Kit.
**
** The complete license agreement can be found here:
** http://unifiedautomation/License/SLA/2.3/
**
** Created: 15.07.2014
**
******************************************************************************/
#include <uaserver_config.h>
#include <opcua_statuscodes.h>
#include <opcua_attributes.h>
#include <uaserver_basenode.h>
#include <uaserver_utilities.h>
#include <uaserver_p_atomic.h>
#include <uaserver_monitoring.h>
#include <uaserver_monitoreditemmanager.h>
#include "uaprovider_oversampling_helper.h"
#include "uaprovider_oversampling_identifiers_1.h"
/* include headers for POSIX timer */
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include <time.h>
/* define signal number to use for timer */
#define SIGTIMER SIGALRM
OPCUA_BEGIN_EXTERN_C
#if OPCUA_MULTITHREADED || 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] = {1, 50, 100, 250, 500, 1000, 2500};
static timer_t g_hSamplingTimers[NUM_SAMPLINGRATES];
static UaServer_Vector g_vecSamplingLists[NUM_SAMPLINGRATES];
/* timer thread id */
static pthread_t g_timerthreadid;
static pthread_attr_t g_timerthreadattr;
static volatile int g_bExitTimerThread = 0;
/* simulate variables */
double g_Voltage = 0.0;
double g_Current = 0.0;
/* enable debug trace to see if the timers cannot be handled fast enough */
#define TIMER_OVERRUN_TRACE 0
static void UaProvider_OverSampling_SampleData(UaServer_Vector *pvecList)
{
unsigned int i = 0;
UaServer_MonitoredItemData *pMonitoredItemData = OpcUa_Null;
OpcUa_Variable *pVariable = OpcUa_Null;
OpcUa_DataValue *pValue = OpcUa_Null;
unsigned int size;
if (pvecList == &g_vecSamplingLists[0])
{
/* simulate value at 1Khz interval */
g_Voltage += 0.1;
g_Current += 0.17;
}
LOCK_LIST();
size = UaServer_Vector_GetSize(pvecList);
/* iterate over all monitored items of this sampling rate */
for (i = 0; i < size; i++)
{
pMonitoredItemData = (UaServer_MonitoredItemData*)UaServer_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;
}
/* create new data value */
pValue = (OpcUa_DataValue*)OpcUa_Alloc(sizeof(OpcUa_DataValue));
if (pValue == 0) break;
OpcUa_DataValue_Initialize(pValue);
/* set timestamps */
pValue->ServerTimestamp = OpcUa_DateTime_UtcNow();
/* check attribute type */
if (pMonitoredItemData->AttributeId == OpcUa_Attributes_Value)
{
pValue->SourceTimestamp = pValue->ServerTimestamp;
switch (pMonitoredItemData->NodeId.Identifier.Numeric)
{
case OverSampling_Objects_S1KHz_Voltage:
pValue->Value.Datatype = OpcUaType_Double;
pValue->Value.Value.Double = g_Voltage;
break;
case OverSampling_Objects_S1KHz_Current:
pValue->Value.Datatype = OpcUaType_Double;
pValue->Value.Value.Double = g_Current;
break;
default:
/* default fallback to node based values */
UaServer_GetNode(&g_pOverSamplingProvider->AddressSpace, &pMonitoredItemData->NodeId, (OpcUa_BaseNode**)&pVariable);
if (pVariable)
{
OpcUa_Variant_CopyTo(OpcUa_Variable_GetValue(pVariable), &pValue->Value);
}
break;
}
}
else
{
/* all other attributes are read from nodes */
UaServer_GetNode(&g_pOverSamplingProvider->AddressSpace, &pMonitoredItemData->NodeId, (OpcUa_BaseNode**)&pVariable);
if (pVariable)
{
(OpcUa_BaseNode*)pVariable,
&pValue->Value,
}
}
/* push new value into SDK */
UaServer_NewItemValue(pMonitoredItemData, pValue);
}
UNLOCK_LIST();
}
/* Timer thread which handles SIGTIMER using sigwaitinfo.
* The configured signal SIGTIMER is blocked by this thread using sigprocmask,
* so no signal handler is needed.
* sigwaitinfo suspends the thread until the signal is pending and removes it from pending list.
*
* We are using this timer for 1kHz sampling of UA data.
* [posix thread] */
void* timer_thread (void *arg)
{
UaServer_Vector *pvecList = OpcUa_Null;
sigset_t waitset;
int sig;
siginfo_t info;
int index;
OpcUa_ReferenceParameter(arg);
/* block signals processed by the timer thread */
sigemptyset( &waitset );
sigaddset( &waitset, SIGTIMER );
sigprocmask( SIG_BLOCK, &waitset, NULL );
while (g_bExitTimerThread == 0)
{
sig = sigwaitinfo( &waitset, &info );
switch(sig)
{
case SIGTIMER:
index = info.si_value.sival_int;
if (index < 0 || index >= NUM_SAMPLINGRATES) break;
pvecList = &g_vecSamplingLists[index];
if (info.si_code == SI_TIMER)
{
#if TIMER_OVERRUN_TRACE
timer_t timerid;
timerid = &g_hSamplingTimers[index];
/* debug printf
printf("timer expired: %p\n", pvecList);
*/
if (timer_getoverrun(timerid) > 0)
{
/* if you see this output your system is not able to process the timers fast enough.
* It make your sampling routine faster or use a slower sampling rate.
* You should remove this printf from productive code.
*/
fprintf(stderr, "timer overrun\n");
}
#endif /* TIMER_OVERRUN_TRACE */
/* timer expired, sample data */
UaProvider_OverSampling_SampleData(pvecList);
}
break;
default:
/* debug info, this should not happen */
perror("sigwaitinfo");
break;
}
}
return 0;
}
OpcUa_StatusCode UaProvider_OverSampling_Subscription_Initialize()
{
OpcUa_UInt32 i;
struct sigevent se;
struct itimerspec interval;
int ret;
sigset_t waitset;
OpcUa_InitializeStatus(OpcUa_Module_Server, "UaProvider_OverSampling_Subscription_Initialize");
#if OPCUA_MULTITHREADED || OPCUA_USE_SYNCHRONISATION
uStatus = OpcUa_Mutex_Create(&g_mtxList);
OpcUa_GotoErrorIfBad(uStatus);
#endif
LOCK_LIST();
/* block signals processed by the timer thread */
sigemptyset( &waitset );
sigaddset( &waitset, SIGTIMER );
sigprocmask( SIG_BLOCK, &waitset, NULL );
pthread_attr_init(&g_timerthreadattr);
pthread_attr_setstacksize(&g_timerthreadattr, 1024*1024);
/* start timer thread */
pthread_create(&g_timerthreadid, &g_timerthreadattr, timer_thread, 0);
/* init vectors and start sampling timers */
for (i = 0; i < NUM_SAMPLINGRATES; i++)
{
UaServer_Vector_Initialize(&g_vecSamplingLists[i], 10, 10);
/* create sampling timer */
OpcUa_MemSet(&se, 0, sizeof(struct sigevent));
se.sigev_notify = SIGEV_SIGNAL; /* notifiy by own thread function */
se.sigev_signo = SIGTIMER;
se.sigev_value.sival_int = i; /* store sampling index */
ret = timer_create(CLOCK_MONOTONIC, &se, &g_hSamplingTimers[i]);
if (ret != 0)
{
perror("timer_create");
}
/* arm timer */
interval.it_interval.tv_sec = g_aiSamplingRates[i] / 1000;
interval.it_interval.tv_nsec = (g_aiSamplingRates[i] % 1000) * 1000000;
interval.it_value.tv_sec = interval.it_interval.tv_sec;
interval.it_value.tv_nsec = interval.it_interval.tv_nsec;
ret = timer_settime(g_hSamplingTimers[i], 0, &interval, 0);
if (ret != 0)
{
perror("timer_settime");
}
}
UNLOCK_LIST();
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
OpcUa_StatusCode UaProvider_OverSampling_Subscription_Cleanup()
{
OpcUa_UInt32 i;
void *retval = 0;
OpcUa_InitializeStatus(OpcUa_Module_Server, "UaProvider_OverSampling_Subscription_Cleanup");
/* wait for timer thread to terminate */
g_bExitTimerThread = 1;
printf("wait for timer thread to terminate\n");
pthread_join(g_timerthreadid, &retval);
LOCK_LIST();
for (i = 0; i < NUM_SAMPLINGRATES; i++)
{
/* delete the timer associated with this thread */
timer_delete(g_hSamplingTimers[i]);
/* cleanup our sampling list */
UaServer_Vector_Clear(&g_vecSamplingLists[i]);
}
UNLOCK_LIST();
#if OPCUA_MULTITHREADED || OPCUA_USE_SYNCHRONISATION
OpcUa_Mutex_Delete(&g_mtxList);
#endif
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
OpcUa_Range* UaProvider_OverSampling_GetEURange(const OpcUa_NodeId *pNodeId)
{
OpcUa_Range *pRange = OpcUa_Null;
OpcUa_BaseNode *pNode = OpcUa_Null, *pProperty = OpcUa_Null;
OpcUa_QualifiedName qnEURange;
OpcUa_QualifiedName_Initialize(&qnEURange);
qnEURange.NamespaceIndex = 0;
OpcUa_String_AttachReadOnly(&qnEURange.Name, OpcUa_BrowseName_EURange);
UaServer_GetNode(&g_pOverSamplingProvider->AddressSpace, (OpcUa_NodeId*)pNodeId, &pNode);
if (pNode)
{
unsigned int iReference;
UaServer_Vector *pReferencesVector = OpcUa_BaseNode_GetReferences(pNode);
for (iReference = 0; iReference < UaServer_Vector_GetSize(pReferencesVector); iReference++)
{
OpcUa_Reference *pReference = UaServer_Vector_Get(pReferencesVector, iReference);
if (pReference->refType.NamespaceIndex == 0
&& pReference->refType.IdentifierType == OpcUa_IdentifierType_Numeric
&& pReference->refType.Identifier.Numeric == OpcUaId_HasProperty
&& pReference->targetType == TargetTypeNode
&& OpcUa_BaseNode_GetClass(pReference->target.pNode) == OpcUa_NodeClass_Variable)
{
if (OpcUa_QualifiedName_Compare(OpcUa_BaseNode_GetBrowseName(pReference->target.pNode), &qnEURange) == 0)
{
pProperty = pReference->target.pNode;
break;
}
}
}
if (pProperty)
{
OpcUa_Variant *pValue = OpcUa_Variable_GetValue(pProperty);
if (pValue->Datatype == OpcUaType_ExtensionObject
&& pValue->ArrayType == OpcUa_VariantArrayType_Scalar
&& pValue->Value.ExtensionObject != OpcUa_Null
&& pValue->Value.ExtensionObject->Encoding == OpcUa_ExtensionObjectEncoding_EncodeableObject
&& pValue->Value.ExtensionObject->Body.EncodeableObject.Type == &OpcUa_Range_EncodeableType)
{
pRange = (OpcUa_Range*)pValue->Value.ExtensionObject->Body.EncodeableObject.Object;
}
}
}
return pRange;
}
IFMETHODIMP(UaProvider_OverSampling_AddItem)(UaServer_MonitoredItem* a_pItem)
{
OpcUa_Int i = 0;
OpcUa_Int iDist = 0;
OpcUa_Int iTmp = 0;
OpcUa_Int iRevised = 0; /* revised index */
OpcUa_BaseNode *pNode = OpcUa_Null;
UaServer_AddressSpace *pAddressSpace = &(g_pOverSamplingProvider->AddressSpace);
#if UASERVER_SUPPORT_AUTHORIZATION
#endif
OpcUa_InitializeStatus(OpcUa_Module_Server, "UaProvider_OverSampling_AddItem");
OpcUa_ReturnErrorIfArgumentNull(a_pItem);
iDist = UaServer_P_Abs(a_pItem->iSamplingInterval - g_aiSamplingRates[0]);
/* find closest sampling rate */
for (i = 1; i < NUM_SAMPLINGRATES; i++)
{
iTmp = UaServer_P_Abs(a_pItem->iSamplingInterval - g_aiSamplingRates[i]);
if (iTmp < iDist)
{
iDist = iTmp;
iRevised = i;
}
}
/* revise values */
a_pItem->iSamplingInterval = g_aiSamplingRates[iRevised];
/* set data type */
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;
#if UASERVER_SUPPORT_AUTHORIZATION
/* check if user is allowed to read the value */
{
uStatus = OpcUa_BadNotReadable;
}
#endif
/* 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)
{
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) && pMonitoredItemData->DataChangeFilter.DeadbandType == OpcUa_DeadbandType_Percent)
{
if (pMonitoredItemData->DataChangeFilter.DeadbandValue < 0.0
|| pMonitoredItemData->DataChangeFilter.DeadbandValue > 100.0)
{
uStatus = OpcUa_BadDeadbandFilterInvalid;
}
}
}
/* get EURange property if DeadbandPercent */
if (OpcUa_IsGood(uStatus) && pMonitoredItemData->DataChangeFilter.DeadbandType == OpcUa_DeadbandType_Percent)
{
OpcUa_Range *pRange = UaProvider_OverSampling_GetEURange(&pMonitoredItemData->NodeId);
if (pRange)
{
pMonitoredItemData->pEURange = pRange;
}
else
{
uStatus = OpcUa_BadMonitoredItemFilterUnsupported;
}
}
if (OpcUa_IsGood(uStatus))
{
/* add to list */
LOCK_LIST();
a_pItem->uProviderHandle = UaServer_Vector_Add(&g_vecSamplingLists[iRevised], pMonitoredItemData);
UNLOCK_LIST();
}
}
else
{
OpcUa_Variant currentValue;
OpcUa_Variant_Initialize(&currentValue);
#if UASERVER_SUPPORT_AUTHORIZATION
/* check if user is allowed to read the attribute */
{
uStatus = OpcUa_BadNotReadable;
}
#endif
/* 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 = UaServer_Vector_Add(&g_vecSamplingLists[iRevised], pMonitoredItemData);
UNLOCK_LIST();
}
OpcUa_Variant_Clear(&currentValue);
}
}
}
else
{
uStatus = OpcUa_BadNodeIdUnknown;
}
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
IFMETHODIMP(UaProvider_OverSampling_RemoveItem)(UaServer_MonitoredItem* a_pItem)
{
UaServer_Vector *pVec = 0;
unsigned int size;
unsigned int i;
OpcUa_InitializeStatus(OpcUa_Module_Server, "UaProvider_OverSampling_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;
}
if (a_pItem->ItemType == DATA)
{
LOCK_LIST();
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 = UaServer_Vector_Get(pVec, size - 1);
pTmpItem->uProviderHandle = a_pItem->uProviderHandle;
UaServer_Vector_Set(pVec, a_pItem->uProviderHandle, pTmpItem);
}
UaServer_Vector_Resize(pVec, size - 1);
}
else
{
uStatus = OpcUa_BadInternalError;
}
UNLOCK_LIST();
}
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
IFMETHODIMP(UaProvider_OverSampling_Subscribe)(UaServer_ProviderSubscribeContext *a_pCtx)
{
OpcUa_InitializeStatus(OpcUa_Module_Server, "UaProvider_OverSampling_Subscribe");
/* Send the callback */
UaServer_Atomic_Increment(&a_pCtx->nOutstandingCbs);
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
OPCUA_END_EXTERN_C