ANSI C Based OPC UA Client/Server SDK  1.8.3.398
custom_provider.c
/*****************************************************************************
* *
* Copyright (c) 2006-2018 Unified Automation GmbH. All rights reserved. *
* *
* Software License Agreement ("SLA") Version 2.7 *
* *
* 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.7, 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.7/ *
* *
* Project: Unified Automation ANSI C based OPC UA Server SDK *
* *
*****************************************************************************/
#include "custom_provider.h"
#include <uaserver_utilities.h>
#if HAVE_DATA_LOGGER
# include <uaserver_monitoring.h>
# include <uaserver_datalogger.h>
# if HAVE_DATA_LOGGER_FILE_BACKEND
# include <uaserver_filelogger.h>
# endif /* HAVE_DATA_LOGGER_FILE_BACKEND */
#endif /* HAVE_DATA_LOGGER */
#include "custom_provider_helper.h"
OPCUA_BEGIN_EXTERN_C
/* Global provider data */
UaServer_Provider *g_pCustomProvider;
UaServer_pProviderInterface *g_pCustomProviderInterface;
OpcUa_UInt16 g_uCustomProvider_NamespaceIndex = OpcUa_UInt16_Max;
Machine *g_pMyCustomMachine = OpcUa_Null;
OpcUa_Timer g_hSimulationTimer;
OpcUa_Boolean g_bMyCustomMachineSwitch;
OpcUa_Double g_bMyCustomMachineTemperature;
#if HAVE_DATA_LOGGER
OpcUa_Int g_hDataLogger = -1;
#endif /* HAVE_DATA_LOGGER */
/* Forward declarations */
IFMETHODIMP(CustomProvider_Cleanup)(OpcUa_Void);
IFMETHODIMP(CustomProvider_ReadAsync)(UaServer_ProviderReadContext *a_pReadCtx);
IFMETHODIMP(CustomProvider_WriteAsync)(UaServer_ProviderWriteContext *a_pWriteCtx);
IFMETHODIMP(CustomProvider_BrowseAsync)(UaServer_ProviderBrowseContext *a_pBrowseCtx);
IFMETHODIMP(CustomProvider_TranslateAsync)(UaServer_ProviderTranslateContext *a_pTranslateCtx);
IFMETHODIMP(CustomProvider_AddItem)(UaServer_MonitoredItem *a_pItem);
IFMETHODIMP(CustomProvider_RemoveItem)(UaServer_MonitoredItem *a_pItem);
IFMETHODIMP(CustomProvider_Subscribe)(UaServer_ProviderSubscribeContext *a_pCtx);
IFMETHODIMP(CustomProvider_CallAsync)(UaServer_ProviderCallContext *a_pCallContext);
IFMETHODIMP(CustomProvider_HistoryReadRawModifiedAsync)(UaServer_ProviderHistoryReadRawModifiedContext* a_pHistoryReadRawModifiedCtx);
OpcUa_StatusCode CustomProvider_Subscription_Initialize();
OpcUa_StatusCode CustomProvider_Subscription_Cleanup();
OpcUa_StatusCode CustomProvider_RegisterEventTypes(OpcUa_NodeId *a_pStartingNodeId);
OpcUa_StatusCode CustomProvider_UnregisterEventTypes();
OpcUa_StatusCode CustomProvider_InitializeTemperatureAlarm(Machine *a_pMachine,
UaServer_Event *a_pTemperatureAlarm,
OpcUa_NodeId *a_pStartingNodeId);
OpcUa_StatusCode CustomProvider_FireHeaterSwitchedEvent(Machine *a_pMachine);
OpcUa_StatusCode CustomProvider_FireTemperatureAlarm(Machine *a_pMachine,
UaServer_Event *a_pTemperatureAlarm);
/* Creates the TemperatureSensorType nodes in the address space.
* @param [in,out] a_pStartingNodeId A numeric NodeId to use for the new NodeIds.
The identifier is incremented for each created NodeId.
* @param [out] a_ppTemperatureSensorType The newly created TemperatureSensorType.
* @return OpcUa_Good on success.
*/
OpcUa_StatusCode CustomProvider_CreateTemperatureSensorType(OpcUa_NodeId *a_pStartingNodeId,
OpcUa_ObjectType **a_ppTemperatureSensorType)
{
OpcUa_ObjectType *pNewSensorType = OpcUa_Null;
OpcUa_DataVariable *pVariable = OpcUa_Null;
OpcUa_BaseNode *pBaseNode = OpcUa_Null;
UaServer_AddressSpace *pAddressSpace = &g_pCustomProvider->AddressSpace;
UaServer_AddressSpace *pServerAddressSpace = OpcUa_Null;
OpcUa_Variant *pValue = OpcUa_Null;
OpcUa_NodeId nodeId, referenceNodeId;
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_CreateTemperatureSensorType");
OpcUa_ReturnErrorIfArgumentNull(a_pStartingNodeId);
OpcUa_ReturnErrorIfArgumentNull(a_ppTemperatureSensorType);
*a_ppTemperatureSensorType = OpcUa_Null;
UaServer_AddressSpace_Get(0, &pServerAddressSpace);
OpcUa_NodeId_Initialize(&nodeId);
OpcUa_NodeId_Initialize(&referenceNodeId);
/* Create object type */
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_BaseObjectType;
UaServer_GetNode(pServerAddressSpace, &nodeId, &pBaseNode);
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateObjectType(pAddressSpace,
&pNewSensorType,
pBaseNode,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"TemperatureSensorType");
OpcUa_GotoErrorIfBad(uStatus);
/* Create Temperature property */
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateDataVariable(pAddressSpace,
&pVariable,
(OpcUa_BaseNode*)pNewSensorType,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"Temperature");
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_Variable_SetDataType_Numeric(pVariable, OpcUaId_Double, 0);
pValue = OpcUa_Variable_GetValue(pVariable);
pValue->Datatype = OpcUaType_Double;
pValue->Value.Double = 25.0;
/* Set ModellingRule Mandatory for Temperature property */
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_ModellingRule_Mandatory;
referenceNodeId.Identifier.Numeric = OpcUaId_HasModellingRule;
uStatus = OpcUa_BaseNode_AddReferenceToNodeId(pVariable, &nodeId, &referenceNodeId);
OpcUa_GotoErrorIfBad(uStatus);
*a_ppTemperatureSensorType = pNewSensorType;
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/* Creates the MachineType nodes in the address space.
* @param [in,out] a_pStartingNodeId A numeric NodeId to use for the new NodeIds.
The identifier is incremented for each created NodeId.
* @param [out] a_ppMachineType The newly created MachineType.
* @param [out] a_ppTemperatureSensorType The newly created TemperatureSensorType.
* @return OpcUa_Good on success.
*/
OpcUa_StatusCode CustomProvider_CreateMachineType(OpcUa_NodeId *a_pStartingNodeId,
OpcUa_ObjectType **a_ppMachineType,
OpcUa_ObjectType **a_ppTemperatureSensorType)
{
OpcUa_ObjectType *pNewMachineType = OpcUa_Null;
OpcUa_Object *pTemperatureSensor = OpcUa_Null;
OpcUa_DataVariable *pVariable = OpcUa_Null;
OpcUa_BaseNode *pBaseNode = OpcUa_Null;
OpcUa_Method *pMethod = OpcUa_Null;
OpcUa_Property *pProperty = OpcUa_Null;
OpcUa_Argument *pArgument = OpcUa_Null;
OpcUa_UInt32 *pArrayDimensions = OpcUa_Null;
UaServer_AddressSpace *pAddressSpace = &g_pCustomProvider->AddressSpace;
UaServer_AddressSpace *pServerAddressSpace = OpcUa_Null;
OpcUa_Variant *pValue = OpcUa_Null;
OpcUa_NodeId nodeId, typeNodeId, referenceNodeId;
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_CreateMachineType");
OpcUa_ReturnErrorIfArgumentNull(a_pStartingNodeId);
OpcUa_ReturnErrorIfArgumentNull(a_ppMachineType);
OpcUa_ReturnErrorIfArgumentNull(a_ppTemperatureSensorType);
*a_ppMachineType = OpcUa_Null;
*a_ppTemperatureSensorType = OpcUa_Null;
UaServer_AddressSpace_Get(0, &pServerAddressSpace);
OpcUa_NodeId_Initialize(&nodeId);
OpcUa_NodeId_Initialize(&typeNodeId);
OpcUa_NodeId_Initialize(&referenceNodeId);
uStatus = CustomProvider_CreateTemperatureSensorType(a_pStartingNodeId, a_ppTemperatureSensorType);
OpcUa_GotoErrorIfBad(uStatus);
/* Create MachineType object type */
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_BaseObjectType;
UaServer_GetNode(pServerAddressSpace, &nodeId, &pBaseNode);
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateObjectType(pAddressSpace,
&pNewMachineType,
pBaseNode,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"MachineType");
OpcUa_GotoErrorIfBad(uStatus);
/* Create GeneratesEvent reference from machine type to HeaterSwitchedEventType */
nodeId.NamespaceIndex = g_uCustomProvider_NamespaceIndex;
nodeId.Identifier.Numeric = g_HeaterSwitchedEventTypeId;
UaServer_GetNode(pAddressSpace, &nodeId, &pBaseNode);
referenceNodeId.Identifier.Numeric = OpcUaId_GeneratesEvent;
uStatus = OpcUa_BaseNode_AddReferenceToNode(pNewMachineType, pBaseNode, &referenceNodeId, OpcUa_True);
OpcUa_GotoErrorIfBad(uStatus);
/* Create HeaterSwitch property */
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateDataVariable(pAddressSpace,
&pVariable,
(OpcUa_BaseNode*)pNewMachineType,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"HeaterSwitch");
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_Variable_SetDataType_Numeric(pVariable, OpcUaId_Boolean, 0);
pValue = OpcUa_Variable_GetValue(pVariable);
pValue->Datatype = OpcUaType_Boolean;
pValue->Value.Boolean = OpcUa_False;
/* Set ModellingRule Mandatory for Temperature property */
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_ModellingRule_Mandatory;
referenceNodeId.Identifier.Numeric = OpcUaId_HasModellingRule;
uStatus = OpcUa_BaseNode_AddReferenceToNodeId(pVariable, &nodeId, &referenceNodeId);
OpcUa_GotoErrorIfBad(uStatus);
/* Create TemperatureSensor child object */
referenceNodeId.Identifier.Numeric = OpcUaId_HasComponent;
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateNode(pAddressSpace,
(OpcUa_BaseNode **)&pTemperatureSensor,
(OpcUa_BaseNode*)pNewMachineType,
a_pStartingNodeId,
OpcUa_NodeClass_Object,
&referenceNodeId,
OpcUa_BaseNode_GetId(*a_ppTemperatureSensorType),
"TemperatureSensor",
"TemperatureSensor",
"TemperatureSensor");
OpcUa_GotoErrorIfBad(uStatus);
/* Set ModellingRule Mandatory for TemperatureSensor child object */
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_ModellingRule_Mandatory;
referenceNodeId.Identifier.Numeric = OpcUaId_HasModellingRule;
uStatus = OpcUa_BaseNode_AddReferenceToNodeId(pTemperatureSensor, &nodeId, &referenceNodeId);
OpcUa_GotoErrorIfBad(uStatus);
/* Create TemperatureSensor's Temperature property */
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateDataVariable(pAddressSpace,
&pVariable,
(OpcUa_BaseNode*)pTemperatureSensor,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"Temperature");
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_Variable_SetDataType_Numeric(pVariable, OpcUaId_Double, 0);
pValue = OpcUa_Variable_GetValue(pVariable);
pValue->Datatype = OpcUaType_Double;
pValue->Value.Double = 25.0;
/* Set ModellingRule Mandatory for TemperatureSensor's Temperature property */
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_ModellingRule_Mandatory;
referenceNodeId.Identifier.Numeric = OpcUaId_HasModellingRule;
uStatus = OpcUa_BaseNode_AddReferenceToNodeId(pVariable, &nodeId, &referenceNodeId);
OpcUa_GotoErrorIfBad(uStatus);
/* Create switch method */
a_pStartingNodeId->Identifier.Numeric++;
typeNodeId.Identifier.Numeric = OpcUaId_MethodNode;
referenceNodeId.Identifier.Numeric = OpcUaId_HasComponent;
uStatus = UaServer_CreateNode(pAddressSpace,
(OpcUa_BaseNode**)&pMethod,
pNewMachineType,
a_pStartingNodeId,
OpcUa_NodeClass_Method,
&referenceNodeId,
OpcUa_Null,
"SetSwitchState",
"SetSwitchState",
"Switches the machine on or off and returns the current temperature");
OpcUa_GotoErrorIfBad(uStatus);
/* Set ModellingRule Mandatory for switch method child object */
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_ModellingRule_Mandatory;
referenceNodeId.Identifier.Numeric = OpcUaId_HasModellingRule;
uStatus = OpcUa_BaseNode_AddReferenceToNodeId(pMethod, &nodeId, &referenceNodeId);
OpcUa_GotoErrorIfBad(uStatus);
/* Input arguments */
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateProperty(pAddressSpace,
&pProperty,
(OpcUa_BaseNode*)pMethod,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"InputArguments");
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_Variable_SetDataType_Numeric(pProperty, OpcUaId_Argument, 0);
OpcUa_Variable_SetValueRank(pProperty, OpcUa_ValueRanks_OneDimension);
pArrayDimensions = (OpcUa_UInt32*)OpcUa_Alloc(sizeof(OpcUa_UInt32));
pArrayDimensions[0] = 1;
OpcUa_Variable_AttachArrayDimensions(pProperty, pArrayDimensions, 1);
pValue = OpcUa_Variable_GetValue(pProperty);
pValue->Datatype = OpcUaType_ExtensionObject;
pValue->ArrayType = OpcUa_VariantArrayType_Array;
pValue->Value.Array.Length = 1;
pValue->Value.Array.Value.ExtensionObjectArray = (OpcUa_ExtensionObject*)OpcUa_Alloc(sizeof(OpcUa_ExtensionObject));
uStatus = OpcUa_EncodeableObject_CreateExtension(&OpcUa_Argument_EncodeableType,
&pValue->Value.Array.Value.ExtensionObjectArray[0],
(OpcUa_Void**)&pArgument);
OpcUa_GotoErrorIfBad(uStatus);
pArgument->ValueRank = OpcUa_ValueRanks_Scalar;
pArgument->DataType.Identifier.Numeric = OpcUaId_Boolean;
OpcUa_String_StrnCpy(&pArgument->Name,
OpcUa_String_FromCString("SwitchState"),
OPCUA_STRING_LENDONTCARE);
OpcUa_String_StrnCpy(&pArgument->Description.Locale,
OpcUa_String_FromCString("en"),
OPCUA_STRING_LENDONTCARE);
OpcUa_String_StrnCpy(&pArgument->Description.Text,
OpcUa_String_FromCString("The state to set the switch to"),
OPCUA_STRING_LENDONTCARE);
/* Output arguments */
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateProperty(pAddressSpace,
&pProperty,
(OpcUa_BaseNode*)pMethod,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"OutputArguments");
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_Variable_SetDataType_Numeric(pProperty, OpcUaId_Argument, 0);
OpcUa_Variable_SetValueRank(pProperty, OpcUa_ValueRanks_OneDimension);
pArrayDimensions = (OpcUa_UInt32*)OpcUa_Alloc(sizeof(OpcUa_UInt32));
pArrayDimensions[0] = 1;
OpcUa_Variable_AttachArrayDimensions(pProperty, pArrayDimensions, 1);
pValue = OpcUa_Variable_GetValue(pProperty);
pValue->Datatype = OpcUaType_ExtensionObject;
pValue->ArrayType = OpcUa_VariantArrayType_Array;
pValue->Value.Array.Length = 1;
pValue->Value.Array.Value.ExtensionObjectArray = (OpcUa_ExtensionObject*)OpcUa_Alloc(sizeof(OpcUa_ExtensionObject));
uStatus = OpcUa_EncodeableObject_CreateExtension(&OpcUa_Argument_EncodeableType,
&pValue->Value.Array.Value.ExtensionObjectArray[0],
(OpcUa_Void**)&pArgument);
OpcUa_GotoErrorIfBad(uStatus);
pArgument->ValueRank = OpcUa_ValueRanks_Scalar;
pArgument->DataType.Identifier.Numeric = OpcUaId_Double;
OpcUa_String_StrnCpy(&pArgument->Name,
OpcUa_String_FromCString("CurrentTemperature"),
OPCUA_STRING_LENDONTCARE);
OpcUa_String_StrnCpy(&pArgument->Description.Locale,
OpcUa_String_FromCString("en"),
OPCUA_STRING_LENDONTCARE);
OpcUa_String_StrnCpy(&pArgument->Description.Text,
OpcUa_String_FromCString("The current temperature of the machine"),
OPCUA_STRING_LENDONTCARE);
*a_ppMachineType = pNewMachineType;
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/* Creates a new TemperatureSensor instance in the address space.
* @param [in] a_sSensorName The name of the new sensor.
* @param [in] a_pOwner The parent node of the new TemperatureSensor.
* @param [in] a_pTemperatureSensorTypeId The type NodeId to use for the new TemperatureSensor instance.
* @param [in,out] a_pStartingNodeId A numeric NodeId to use for the new NodeIds.
The identifier is incremented for each created NodeId.
* @param [out] a_ppNewTemperatureSensor The UserData struct set to the created TemperatureSensor node.
* @return OpcUa_Good on success.
*/
OpcUa_StatusCode CustomProvider_CreateTemperatureSensor(const OpcUa_CharA *a_sSensorName,
OpcUa_BaseNode *a_pOwner,
OpcUa_NodeId *a_pTemperatureSensorTypeId,
OpcUa_NodeId *a_pStartingNodeId,
TemperatureSensor **a_ppNewTemperatureSensor)
{
TemperatureSensor *pNewSensor = OpcUa_Null;
OpcUa_Object *pObject = OpcUa_Null;
OpcUa_DataVariable *pVariable = OpcUa_Null;
OpcUa_Variant *pValue = OpcUa_Null;
UaServer_AddressSpace *pAddressSpace = &g_pCustomProvider->AddressSpace;
UaServer_AddressSpace *pServerAddressSpace = OpcUa_Null;
OpcUa_NodeId nodeId, referenceNodeId;
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_CreateTemperatureSensor");
OpcUa_ReturnErrorIfArgumentNull(a_sSensorName);
OpcUa_ReturnErrorIfArgumentNull(a_pOwner);
OpcUa_ReturnErrorIfArgumentNull(a_pStartingNodeId);
OpcUa_ReturnErrorIfArgumentNull(a_ppNewTemperatureSensor);
UaServer_AddressSpace_Get(0, &pServerAddressSpace);
OpcUa_NodeId_Initialize(&nodeId);
OpcUa_NodeId_Initialize(&referenceNodeId);
/* Create and initialize new temperature sensor struct */
pNewSensor = (TemperatureSensor*)OpcUa_Alloc(sizeof(TemperatureSensor));
OpcUa_ReturnErrorIfAllocFailed(pNewSensor);
OpcUa_MemSet(pNewSensor, 0, sizeof(TemperatureSensor));
pNewSensor->Type = UserDataTemperature;
/* Create new TemperatureSensor instance in the address space */
referenceNodeId.Identifier.Numeric = OpcUaId_HasComponent;
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateNode(pAddressSpace,
(OpcUa_BaseNode **)&pObject,
a_pOwner,
a_pStartingNodeId,
OpcUa_NodeClass_Object,
&referenceNodeId,
a_pTemperatureSensorTypeId,
a_sSensorName,
a_sSensorName,
a_sSensorName);
OpcUa_GotoErrorIfBad(uStatus);
/* Create Temperature property */
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateDataVariable(pAddressSpace,
&pVariable,
(OpcUa_BaseNode*)pObject,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"Temperature");
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_Variable_SetAccessLevel(pVariable, OpcUa_AccessLevels_CurrentReadOrWrite | OpcUa_AccessLevels_HistoryRead);
OpcUa_Variable_SetDataType_Numeric(pVariable, OpcUaId_Double, 0);
pValue = OpcUa_Variable_GetValue(pVariable);
pValue->Datatype = OpcUaType_Double;
pValue->Value.Double = 25.0;
OpcUa_NodeId_Initialize(&pNewSensor->nodeIdTemperatureValue);
OpcUa_NodeId_CopyTo(a_pStartingNodeId, &pNewSensor->nodeIdTemperatureValue);
/* Set new TemperatureSensor struct as user data of the new node */
OpcUa_BaseNode_SetUserData(pVariable, pNewSensor);
*a_ppNewTemperatureSensor = pNewSensor;
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/* Creates a new Machine instance in the address space.
* @param [in] a_sMachineName The name of the new Machine.
* @param [in] a_sTemperatureSensorName The name of the new Machine's TemperatureSensor.
* @param [in] a_pOwner The parent node of the new Machine.
* @param [in] a_pMachineTypeId The type NodeId to use for the new Machine instance.
* @param [in] a_pTemperatureSensorTypeId The type NodeId to use for the new TemperatureSensor instance.
* @param [in,out] a_pStartingNodeId A numeric NodeId to use for the new NodeIds.
The identifier is incremented for each created NodeId.
* @param [out] a_ppNewMachine The UserData struct set to the created Machine node.
* @return OpcUa_Good on success.
*/
OpcUa_StatusCode CustomProvider_CreateMachine(const OpcUa_CharA *a_sMachineName,
const OpcUa_CharA *a_sTemperatureSensorName,
OpcUa_BaseNode *a_pOwner,
OpcUa_NodeId *a_pMachineTypeId,
OpcUa_NodeId *a_pTemperatureSensorTypeId,
OpcUa_NodeId *a_pStartingNodeId,
Machine **a_ppNewMachine)
{
Machine *pNewMachine = OpcUa_Null;
MachineSwitch *pNewHeaterSwitch = OpcUa_Null;
OpcUa_Object *pObject = OpcUa_Null;
OpcUa_BaseNode *pBaseNode = OpcUa_Null;
OpcUa_DataVariable *pVariable = OpcUa_Null;
OpcUa_Method *pMethod = OpcUa_Null;
OpcUa_Property *pProperty = OpcUa_Null;
OpcUa_Argument *pArgument = OpcUa_Null;
OpcUa_UInt32 *pArrayDimensions = OpcUa_Null;
OpcUa_Variant *pValue = OpcUa_Null;
UaServer_AddressSpace *pAddressSpace = &g_pCustomProvider->AddressSpace;
UaServer_AddressSpace *pServerAddressSpace = OpcUa_Null;
OpcUa_NodeId nodeId, typeNodeId, referenceNodeId;
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_CreateMachine");
OpcUa_ReturnErrorIfArgumentNull(a_sMachineName);
OpcUa_ReturnErrorIfArgumentNull(a_sTemperatureSensorName);
OpcUa_ReturnErrorIfArgumentNull(a_pOwner);
OpcUa_ReturnErrorIfArgumentNull(a_pStartingNodeId);
OpcUa_ReturnErrorIfArgumentNull(a_ppNewMachine);
UaServer_AddressSpace_Get(0, &pServerAddressSpace);
OpcUa_NodeId_Initialize(&nodeId);
OpcUa_NodeId_Initialize(&typeNodeId);
OpcUa_NodeId_Initialize(&referenceNodeId);
/* Create and initialize new machine struct */
pNewMachine = (Machine*)OpcUa_Alloc(sizeof(Machine));
OpcUa_ReturnErrorIfAllocFailed(pNewMachine);
OpcUa_MemSet(pNewMachine, 0, sizeof(Machine));
pNewMachine->Type = UserDataMachine;
/* Create new Machine instance in the address space */
referenceNodeId.Identifier.Numeric = OpcUaId_Organizes;
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateNode(pAddressSpace,
(OpcUa_BaseNode **)&pObject,
a_pOwner,
a_pStartingNodeId,
OpcUa_NodeClass_Object,
&referenceNodeId,
a_pMachineTypeId,
a_sMachineName,
a_sMachineName,
a_sMachineName);
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_NodeId_CopyTo(a_pStartingNodeId, &pNewMachine->nodeId);
/* Set new machine struct as user data of the new node */
OpcUa_BaseNode_SetUserData(pObject, pNewMachine);
/* Set the nodeId of the machine to use it later for events */
OpcUa_NodeId_Initialize(&pNewMachine->nodeId);
OpcUa_NodeId_CopyTo(a_pStartingNodeId, &pNewMachine->nodeId);
OpcUa_String_Initialize(&pNewMachine->sMachineName);
OpcUa_String_StrnCpy(&pNewMachine->sMachineName, OpcUa_String_FromCString(a_sMachineName), OPCUA_STRING_LENDONTCARE);
/* Set the EventNotifier attribute of the node that clients know that it emits events */
OpcUa_Object_SetEventNotifier(pObject, OpcUa_EventNotifiers_SubscribeToEvents);
/* Register machine as event notifier */
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_Server;
uStatus = UaServer_Events_RegisterEventNotifier(a_pStartingNodeId, &nodeId);
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_NodeId_CopyTo(a_pStartingNodeId, &nodeId);
/* Create HasNotifier reference from server object to new machine */
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_Server;
UaServer_GetNode(pServerAddressSpace, &nodeId, (OpcUa_BaseNode**)&pBaseNode);
referenceNodeId.Identifier.Numeric = OpcUaId_HasNotifier;
uStatus = OpcUa_BaseNode_AddReferenceToNode(pBaseNode, (OpcUa_BaseNode*)pObject, &referenceNodeId, OpcUa_True);
OpcUa_GotoErrorIfBad(uStatus);
/* Create HeaterSwitch property */
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateDataVariable(pAddressSpace,
&pVariable,
(OpcUa_BaseNode*)pObject,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"HeaterSwitch");
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_Variable_SetDataType_Numeric(pVariable, OpcUaId_Boolean, 0);
pValue = OpcUa_Variable_GetValue(pVariable);
pValue->Datatype = OpcUaType_Boolean;
pValue->Value.Boolean = OpcUa_False;
/* Create and initialize new heater switch struct */
pNewHeaterSwitch = (MachineSwitch*)OpcUa_Alloc(sizeof(MachineSwitch));
OpcUa_ReturnErrorIfAllocFailed(pNewHeaterSwitch);
OpcUa_MemSet(pNewHeaterSwitch, 0, sizeof(MachineSwitch));
pNewHeaterSwitch->Type = UserDataMachineSwitch;
/* Register HeaterSwitch as event source */
uStatus = UaServer_Events_RegisterEventSource(a_pStartingNodeId, &nodeId);
OpcUa_GotoErrorIfBad(uStatus);
/* Create HasEventSource reference from machine object to new heater switch */
referenceNodeId.Identifier.Numeric = OpcUaId_HasEventSource;
uStatus = OpcUa_BaseNode_AddReferenceToNode(pObject, (OpcUa_BaseNode*)pVariable, &referenceNodeId, OpcUa_True);
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_BaseNode_SetUserData(pVariable, pNewHeaterSwitch);
pNewMachine->pHeaterSwitch = pNewHeaterSwitch;
/* Create TemperatureSensor */
uStatus = CustomProvider_CreateTemperatureSensor(a_sTemperatureSensorName,
(OpcUa_BaseNode*)pObject,
a_pTemperatureSensorTypeId,
a_pStartingNodeId,
&pNewMachine->pTemperatureSensor);
OpcUa_GotoErrorIfBad(uStatus);
/* Create switch method */
a_pStartingNodeId->Identifier.Numeric++;
typeNodeId.Identifier.Numeric = OpcUaId_MethodNode;
referenceNodeId.Identifier.Numeric = OpcUaId_HasComponent;
uStatus = UaServer_CreateNode(pAddressSpace,
(OpcUa_BaseNode**)&pMethod,
pObject,
a_pStartingNodeId,
OpcUa_NodeClass_Method,
&referenceNodeId,
OpcUa_Null,
"SetSwitchState",
"SetSwitchState",
"Switches the machine on or off and returns the current temperature");
OpcUa_GotoErrorIfBad(uStatus);
/* Set new machine struct as user data of the method */
OpcUa_BaseNode_SetUserData(pMethod, pNewMachine);
/* Input arguments */
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateProperty(pAddressSpace,
&pProperty,
(OpcUa_BaseNode*)pMethod,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"InputArguments");
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_Variable_SetDataType_Numeric(pProperty, OpcUaId_Argument, 0);
OpcUa_Variable_SetValueRank(pProperty, OpcUa_ValueRanks_OneDimension);
pArrayDimensions = (OpcUa_UInt32*)OpcUa_Alloc(sizeof(OpcUa_UInt32));
pArrayDimensions[0] = 1;
OpcUa_Variable_AttachArrayDimensions(pProperty, pArrayDimensions, 1);
pValue = OpcUa_Variable_GetValue(pProperty);
pValue->Datatype = OpcUaType_ExtensionObject;
pValue->ArrayType = OpcUa_VariantArrayType_Array;
pValue->Value.Array.Length = 1;
pValue->Value.Array.Value.ExtensionObjectArray = (OpcUa_ExtensionObject*)OpcUa_Alloc(sizeof(OpcUa_ExtensionObject));
uStatus = OpcUa_EncodeableObject_CreateExtension(&OpcUa_Argument_EncodeableType,
&pValue->Value.Array.Value.ExtensionObjectArray[0],
(OpcUa_Void**)&pArgument);
OpcUa_GotoErrorIfBad(uStatus);
pArgument->ValueRank = OpcUa_ValueRanks_Scalar;
pArgument->DataType.Identifier.Numeric = OpcUaId_Boolean;
OpcUa_String_StrnCpy(&pArgument->Name,
OpcUa_String_FromCString("SwitchState"),
OPCUA_STRING_LENDONTCARE);
OpcUa_String_StrnCpy(&pArgument->Description.Locale,
OpcUa_String_FromCString("en"),
OPCUA_STRING_LENDONTCARE);
OpcUa_String_StrnCpy(&pArgument->Description.Text,
OpcUa_String_FromCString("The state to set the switch to"),
OPCUA_STRING_LENDONTCARE);
/* Output arguments */
a_pStartingNodeId->Identifier.Numeric++;
uStatus = UaServer_CreateProperty(pAddressSpace,
&pProperty,
(OpcUa_BaseNode*)pMethod,
a_pStartingNodeId->Identifier.Numeric,
g_uCustomProvider_NamespaceIndex,
"OutputArguments");
OpcUa_GotoErrorIfBad(uStatus);
OpcUa_Variable_SetDataType_Numeric(pProperty, OpcUaId_Argument, 0);
OpcUa_Variable_SetValueRank(pProperty, OpcUa_ValueRanks_OneDimension);
pArrayDimensions = (OpcUa_UInt32*)OpcUa_Alloc(sizeof(OpcUa_UInt32));
pArrayDimensions[0] = 1;
OpcUa_Variable_AttachArrayDimensions(pProperty, pArrayDimensions, 1);
pValue = OpcUa_Variable_GetValue(pProperty);
pValue->Datatype = OpcUaType_ExtensionObject;
pValue->ArrayType = OpcUa_VariantArrayType_Array;
pValue->Value.Array.Length = 1;
pValue->Value.Array.Value.ExtensionObjectArray = (OpcUa_ExtensionObject*)OpcUa_Alloc(sizeof(OpcUa_ExtensionObject));
uStatus = OpcUa_EncodeableObject_CreateExtension(&OpcUa_Argument_EncodeableType,
&pValue->Value.Array.Value.ExtensionObjectArray[0],
(OpcUa_Void**)&pArgument);
OpcUa_GotoErrorIfBad(uStatus);
pArgument->ValueRank = OpcUa_ValueRanks_Scalar;
pArgument->DataType.Identifier.Numeric = OpcUaId_Double;
OpcUa_String_StrnCpy(&pArgument->Name,
OpcUa_String_FromCString("CurrentTemperature"),
OPCUA_STRING_LENDONTCARE);
OpcUa_String_StrnCpy(&pArgument->Description.Locale,
OpcUa_String_FromCString("en"),
OPCUA_STRING_LENDONTCARE);
OpcUa_String_StrnCpy(&pArgument->Description.Text,
OpcUa_String_FromCString("The current temperature of the machine"),
OPCUA_STRING_LENDONTCARE);
/* Create and initialize TemperatureAlarm */
nodeId.NamespaceIndex = g_uCustomProvider_NamespaceIndex;
nodeId.Identifier.Numeric = g_TemperatureAlarmTypeId;
pNewMachine->pTemperatureAlarm = UaServer_Events_CreateEvent(&nodeId);
uStatus = CustomProvider_InitializeTemperatureAlarm(pNewMachine,
pNewMachine->pTemperatureAlarm,
a_pStartingNodeId);
OpcUa_GotoErrorIfBad(uStatus);
*a_ppNewMachine = pNewMachine;
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
OpcUa_Void CustomProvider_DeleteMachine(Machine **a_ppMachine)
{
if (a_ppMachine == OpcUa_Null ||
*a_ppMachine == OpcUa_Null)
{
return;
}
OpcUa_Free((*a_ppMachine)->pHeaterSwitch);
(*a_ppMachine)->pHeaterSwitch = OpcUa_Null;
OpcUa_NodeId_Clear(&(*a_ppMachine)->pTemperatureSensor->nodeIdTemperatureValue);
OpcUa_Free((*a_ppMachine)->pTemperatureSensor);
(*a_ppMachine)->pTemperatureSensor = OpcUa_Null;
OpcUa_NodeId_Clear(&(*a_ppMachine)->nodeId);
OpcUa_String_Clear(&(*a_ppMachine)->sMachineName);
UaServer_Events_DeleteEvent(&(*a_ppMachine)->pTemperatureAlarm);
OpcUa_Free(*a_ppMachine);
*a_ppMachine = OpcUa_Null;
}
/* Internal Helper For Creating The AddressSpace In The Server */
OpcUa_StatusCode CustomProvider_CreateAddressSpace()
{
OpcUa_UInt16 uNsIdx = g_uCustomProvider_NamespaceIndex;
OpcUa_Folder *pCustomProvider = OpcUa_Null;
OpcUa_Folder *pFolder = OpcUa_Null;
OpcUa_ObjectType *pMachineType = OpcUa_Null;
OpcUa_ObjectType *pTemperatureSensorType = OpcUa_Null;
OpcUa_NodeId nodeId;
UaServer_AddressSpace *pAddressSpace = &(g_pCustomProvider->AddressSpace);
UaServer_AddressSpace *pServerAddressSpace = OpcUa_Null;
OpcUa_BaseNode_DefaultValues *pBaseNode_DefaultValues = OpcUa_BaseNode_DefaultValues_Get();
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_CreateAddressSpace");
OpcUa_NodeId_Initialize(&nodeId);
/* Set default values for node creation */
#if UASERVER_ENABLE_MINSAMPLINGINTERVAL
pBaseNode_DefaultValues->MinimumSamplingInterval = 50.0;
#endif
pBaseNode_DefaultValues->AccessLevel = OpcUa_AccessLevels_CurrentReadOrWrite;
#if UASERVER_SUPPORT_AUTHORIZATION == OPCUA_CONFIG_NO
pBaseNode_DefaultValues->UserAccessLevel = OpcUa_AccessLevels_CurrentReadOrWrite;
#endif
/* Get objects folder node */
UaServer_AddressSpace_Get(0, &pServerAddressSpace);
nodeId.NamespaceIndex = 0;
nodeId.Identifier.Numeric = OpcUaId_ObjectsFolder;
UaServer_GetNode(pServerAddressSpace, &nodeId, (OpcUa_BaseNode**)&pFolder);
/* Register new event types */
nodeId.NamespaceIndex = uNsIdx;
nodeId.Identifier.Numeric = 1;
uStatus = CustomProvider_RegisterEventTypes(&nodeId);
OpcUa_GotoErrorIfBad(uStatus);
/* Create custom object types */
uStatus = CustomProvider_CreateMachineType(&nodeId, &pMachineType, &pTemperatureSensorType);
OpcUa_GotoErrorIfBad(uStatus);
/* Create custom provider base node */
nodeId.Identifier.Numeric++;
uStatus = UaServer_CreateFolder(pAddressSpace,
&pCustomProvider,
(OpcUa_BaseNode*)pFolder,
nodeId.Identifier.Numeric,
uNsIdx,
"Custom Provider");
OpcUa_GotoErrorIfBad(uStatus);
/* Create instance of MachineType */
uStatus = CustomProvider_CreateMachine("MyCustomMachine",
"MyCustomTemperatureSensor",
(OpcUa_BaseNode*)pCustomProvider,
OpcUa_BaseNode_GetId(pMachineType),
OpcUa_BaseNode_GetId(pTemperatureSensorType),
&nodeId,
&g_pMyCustomMachine);
OpcUa_GotoErrorIfBad(uStatus);
g_pMyCustomMachine->pHeaterSwitch->pValue = &g_bMyCustomMachineSwitch;
g_pMyCustomMachine->pTemperatureSensor->pValue = &g_bMyCustomMachineTemperature;
g_bMyCustomMachineSwitch = OpcUa_False;
g_bMyCustomMachineTemperature = 25.0;
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
IFMETHODIMP(CustomProvider_SimulationTimerCallback)(OpcUa_Void *a_pvCallbackData,
OpcUa_Timer a_hTimer,
OpcUa_UInt32 a_msecElapsed)
{
static OpcUa_Boolean bLastSwitchPosition = OpcUa_False;
static OpcUa_Boolean bTemperatureAlarmActive = OpcUa_False;
OpcUa_ReferenceParameter(a_pvCallbackData);
OpcUa_ReferenceParameter(a_hTimer);
OpcUa_ReferenceParameter(a_msecElapsed);
if (bLastSwitchPosition != g_bMyCustomMachineSwitch)
{
CustomProvider_FireHeaterSwitchedEvent(g_pMyCustomMachine);
}
bLastSwitchPosition = g_bMyCustomMachineSwitch;
if (g_bMyCustomMachineSwitch == OpcUa_False &&
g_bMyCustomMachineTemperature > -30.0)
{
g_bMyCustomMachineTemperature -= 0.5;
}
else if (g_bMyCustomMachineSwitch == OpcUa_True &&
g_bMyCustomMachineTemperature < 150.0)
{
g_bMyCustomMachineTemperature += 0.5;
}
if ( bTemperatureAlarmActive == OpcUa_False &&
(g_bMyCustomMachineTemperature >= 100.0 || g_bMyCustomMachineTemperature <= 0.0) )
{
CustomProvider_FireTemperatureAlarm(g_pMyCustomMachine, g_pMyCustomMachine->pTemperatureAlarm);
bTemperatureAlarmActive = OpcUa_True;
}
else if ( bTemperatureAlarmActive != OpcUa_False &&
(g_bMyCustomMachineTemperature < 100.0 && g_bMyCustomMachineTemperature > 0.0) )
{
CustomProvider_FireTemperatureAlarm(g_pMyCustomMachine, g_pMyCustomMachine->pTemperatureAlarm);
bTemperatureAlarmActive = OpcUa_False;
}
return OpcUa_Good;
}
#if HAVE_DATA_LOGGER
void CustomProvider_SetupDataLogger()
{
OpcUa_BaseNode *pNode = 0;
UaServer_AddressSpace *pAddressSpace = &g_pCustomProvider->AddressSpace;
/* create file logger */
#if HAVE_DATA_LOGGER_FILE_BACKEND
g_hDataLogger = UaServer_FileLogger_Create( "historian" );
#endif
if (g_hDataLogger < 0) return;
/* add Temperature to data logging */
UaServer_GetNode(pAddressSpace, &g_pMyCustomMachine->pTemperatureSensor->nodeIdTemperatureValue, &pNode);
if ( pNode )
{
TemperatureSensor *pTemperatureSensor = (TemperatureSensor*)OpcUa_BaseNode_GetUserData(pNode);
UaServer_DataLoggerItemData_Create(&g_pMyCustomMachine->pTemperatureSensor->nodeIdTemperatureValue,
100,
100,
OpcUa_Null,
g_hDataLogger,
&pTemperatureSensor->hDataLogItemTemperatureValue);
}
/* start historian data logging */
UaServer_DataLogger_Start( g_hDataLogger );
}
#endif /* HAVE_DATA_LOGGER */
/* Initialization function called by the server */
IFMETHODIMP(CustomProvider_Initialize)(UaServer_Provider *a_pProvider,
UaServer_pProviderInterface *a_pProviderInterface)
{
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_Initialize");
OpcUa_Trace(OPCUA_TRACE_LEVEL_WARNING, "Initialize CustomProvider ...\n");
/* Store values */
g_pCustomProvider = a_pProvider;
g_pCustomProviderInterface = a_pProviderInterface;
OpcUa_MemSet(g_pCustomProviderInterface, 0, sizeof(UaServer_pProviderInterface));
/* Register service handlers */
a_pProviderInterface->Cleanup = CustomProvider_Cleanup;
a_pProviderInterface->ReadAsync = CustomProvider_ReadAsync;
a_pProviderInterface->WriteAsync = CustomProvider_WriteAsync;
a_pProviderInterface->BrowseAsync = CustomProvider_BrowseAsync;
a_pProviderInterface->TranslateAsync = CustomProvider_TranslateAsync;
a_pProviderInterface->AddItem = CustomProvider_AddItem;
a_pProviderInterface->RemoveItem = CustomProvider_RemoveItem;
a_pProviderInterface->Subscribe = CustomProvider_Subscribe;
a_pProviderInterface->CallAsync = CustomProvider_CallAsync;
a_pProviderInterface->HistoryReadRawModifiedAsync = CustomProvider_HistoryReadRawModifiedAsync;
/* Register address space */
uStatus = UaServer_RegisterAddressSpace((OpcUa_Handle *)a_pProvider,
&g_uCustomProvider_NamespaceIndex,
"http://www.unifiedautomation.com/customprovider/",
1009);
OpcUa_ReturnErrorIfBad(uStatus);
/* Create address space */
uStatus = CustomProvider_CreateAddressSpace();
OpcUa_ReturnErrorIfBad(uStatus);
/* Initialize subscription management */
uStatus = CustomProvider_Subscription_Initialize();
OpcUa_ReturnErrorIfBad(uStatus);
#if HAVE_DATA_LOGGER
CustomProvider_SetupDataLogger();
#endif /* HAVE_DATA_LOGGER */
/* Create timer to simulate the machine */
OpcUa_Timer_Create(&g_hSimulationTimer,
250,
CustomProvider_SimulationTimerCallback,
OpcUa_Null,
OpcUa_Null);
OpcUa_Trace(OPCUA_TRACE_LEVEL_WARNING, "Initialize CustomProvider DONE!\n");
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
/* Clean up the custom provider */
IFMETHODIMP(CustomProvider_Cleanup)()
{
OpcUa_InitializeStatus(OpcUa_Module_Server, "CustomProvider_Cleanup");
OpcUa_Trace(OPCUA_TRACE_LEVEL_WARNING, "Cleanup CustomProvider ...\n");
/* Delete timer that simulates the machine */
OpcUa_Timer_Delete(&g_hSimulationTimer);
/* Cleanup subscription management */
CustomProvider_Subscription_Cleanup();
#if HAVE_DATA_LOGGER
UaServer_DataLoggerItemData_Remove(g_hDataLogger, g_pMyCustomMachine->pTemperatureSensor->hDataLogItemTemperatureValue);
UaServer_DataLogger_Stop( g_hDataLogger );
UaServer_DataLogger_Delete( g_hDataLogger );
#endif /* HAVE_DATA_LOGGER */
/* Cleanup machine structure */
CustomProvider_DeleteMachine(&g_pMyCustomMachine);
/* Cleanup custom event types */
CustomProvider_UnregisterEventTypes();
OpcUa_Trace(OPCUA_TRACE_LEVEL_WARNING, "Cleanup CustomProvider DONE!\n");
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}
#ifdef BUILD_SHARED_LIBS
CUSTOMPROVIDER_API(OpcUa_StatusCode) InitializeProvider(UaServer_Provider* pProvider,
UaServer_pProviderInterface* pProviderInterface)
{
return CustomProvider_Initialize(pProvider, pProviderInterface);
}
#endif
OPCUA_END_EXTERN_C