ANSI C Based OPC UA Client/Server/PubSub SDK  1.9.2.463
ANSI C SDK Demo Server

The ANSI C SDK Demo Server is a collection of examples for developing different features of an OPC UA server and contains the Unified Automation Demo address space with nodes in the Demo folder.

The complete project can be found in Examples → UA Demo Server Project.

Command Line Arguments

The Demo Server application is able to recognize a set of command line arguments. On Linux systems, the native getopt implementation is used, on Windows, the pthread-win32 compatibility library is used which provides a getopt implementation for Windows.

The following command line arguments are recognized:

Usage: uaserverc.exe [-h] [-v] [-n <hostname>] [-c <config file>] [-s <shutdown delay>]
  -h: Shows this help
  -v: Shows detailed version information
  -n: Sets the hostname to bind on
  -c: Sets the configuration file to use (default=settings.ini)
  -s: Sets the shutdown delay in seconds (default=3)

The sample code can be found in the files

Structured Data Type Example

The demo address space code contains generated sample code for the handling of structured data types. A method with a structured type as parameter can be found in Objects → Demo → 003_Method → VectorAdd. This methods takes two parameters of type Demo_Vector and adds them.

The sample code can be found in the files

The structured data type nodes and the dictionary are created in the functions UaProvider_Demo_AddDictionary() and UaProvider_Demo_AddVector().

Custom encodeable types are not registered at the UaStack anymore, as doing do leads to problems when reinitializing the SDK during runtime. Instead, following functions can be used to (de-)serialize values of custom encodeable types:

  • UaBase_Structure_To_ExtensionObject
  • UaBase_Structures_To_ExtensionObjects
  • UaBase_Structures_To_Variant
  • UaBase_Structure_From_ExtensionObject
  • UaBase_Structures_From_ExtensionObjects

The method arguments are checked for validity and extracted in the function UaProvider_Demo_Method_CallVectorAdd(). From there the function UaProvider_Demo_Method_VectorAdd() is called, this is where the actual addition is executed.

Historical Data Access Example

The demo address space code contains sample code for the handling of historical data access. The variables providing history can be found in Objects → Demo → 002_History. The data changes are monitored with internal monitored items and are stored in a file store using the “datalogger” module.

The sample code can be found in the files

The data logger is instantiated and initialized using the settings in the function UaProvider_Demo_SetupDataLogger():

g_hDataLogger = UaServer_FileLogger_Create( szDataLoggerPath );

After that, the internal history monitored items are created using the function UaProvider_Demo_HistorizeItem():

/* create datalog item if it does not already exist. */
iSamplingInterval,
monitoringMode,
100,
OpcUa_Null,
g_hDataLogger,
&hLogItem);
OpcUa_GotoErrorIfBad(uStatus);

The data log item information is stored in the user data of the nodes in order to retrieve the history for these items later in the function UaProvider_Demo_HistoryReadRawModifiedAsync:

pUserData->Data.HistoryDataLogger.DataLogger,
pUserData->Data.HistoryDataLogger.DataLogItem,
a_pHistoryReadRawModifiedCtx->pHistoryReadRawModifiedDetails,
a_pHistoryReadRawModifiedCtx->TimestampsToReturn,
a_pHistoryReadRawModifiedCtx->ReleaseContinuationPoints,
&a_pHistoryReadRawModifiedCtx->pNodesToRead[i],
pHistoryResult,
a_pHistoryReadRawModifiedCtx->RequestHeader.TimeoutHint);
a_pHistoryReadRawModifiedCtx->pResponse->Results[i].StatusCode = ret;

Model Change Event Example

The demo address space code contains sample code for dynamic creation of nodes including model change event. The folder with the functionality can be found in Objects → Demo → 008_DynamicNodes. This folder contains two methods for node creation and deletion, a NodeVersion property and the dynamic node. Only nodes with a NodeVersion property are allowed to fire specific model change events of type GeneralModelChangeEventType. This event indicates the changed nodes with a NodeVersion property and the changes (node added/deleted, reference added/deleted).

Changes of nodes without NodeVersion property can be indicated by using the BaseModelChangeEvent. But this event does not indicate which part of the address space has changed.

The sample code can be found in the files

The full sample code for the generation of the model change event can be found in the method UaProvider_Demo_SendModelChangeEvent(). The following code is the essential part of the event generation.

modelChanges.Datatype = OpcUaType_ExtensionObject;
modelChanges.ArrayType = OpcUa_VariantArrayType_Array;
modelChanges.Value.Array.Value.ExtensionObjectArray = (OpcUa_ExtensionObject*)OpcUa_Alloc(1 * sizeof(OpcUa_ExtensionObject));
OpcUa_ReturnErrorIfAllocFailed(modelChanges.Value.Array.Value.ExtensionObjectArray);
modelChanges.Value.Array.Length = 1;
OpcUa_ExtensionObject_Initialize(&modelChanges.Value.Array.Value.ExtensionObjectArray[0]);
ret = OpcUa_EncodeableObject_CreateExtension(&OpcUa_ModelChangeStructureDataType_EncodeableType,
&modelChanges.Value.Array.Value.ExtensionObjectArray[0],
(OpcUa_Void**)&pModelChanges);
OpcUa_ReturnErrorIfBad(ret);
pModelChanges->Verb = verb;
OpcUa_NodeId_CopyTo(pParentId, &pModelChanges->Affected);
pModelChanges->AffectedType.Identifier.Numeric = OpcUaId_FolderType;
UaServer_Events_SetEventField(pEvent, GeneralModelChangeEventTypeField_Changes, &modelChanges);

The sample code creates only one change. If more than one node with a NodeVersion property is affected, all changes should be combined in one event.

Alarm Example

The demo address space code contains sample code for alarm condition type events (BaseEventType → ConditionType → AcknowledgeableConditionType → AlarmConditionType).

The sample code creates two alarm conditions for a low and high temperature alarm on a temperature sensor. The corresponding object can be found in Objects → Demo → 004_Events → Machine → TemperatureSensor. The variable HeaterSwitch is used to influence the temperature. A temperature value below 0 activates the low alarm. A temperature above 100 activates the high alarm.

The sample code can be found in the files

Sample alarm condition type events are created and initialized in the function UaProvider_Demo_InitializeSimulation():

/* create acknowledgeable condition type events */
UaBase_CreateNumericNodeIdEx(&nodeid, OpcUaId_AlarmConditionType, 0);
/* create acknowledgeable conditions used by this sensor */
pEvent = UaServer_Events_CreateEvent(&nodeid);
UaProvider_Demo_InitializeAlarmConditionTypeEvent(
pEvent,
&g_machine.TemperatureSensor.NodeId,
"Temperature Sensor",
&g_machine.TemperatureSensor.HighAlarmId,
"HighAlarm");
pEvent = UaServer_Events_CreateEvent(&nodeid);
UaProvider_Demo_InitializeAlarmConditionTypeEvent(
pEvent,
&g_machine.TemperatureSensor.NodeId,
"Temperature Sensor",
&g_machine.TemperatureSensor.LowAlarmId,
"LowAlarm");

Firing/disabling of alarms is simulated in UaProvider_Demo_SimulateMachine():

/* check if the temperature has over-/underrun certain values and fire events if necessary */
if (oldTemperature < 100 && *pTemp >= 100)
{
UaServer_Event *pHighAlarm = UaServer_Events_GetConditionByNodeId(g_UaProviderDemo_uNamespaceIndex1,
&a_pMachine->TemperatureSensor.HighAlarmId);
if (pHighAlarm)
UaProvider_Demo_FireAlarmConditionTypeEvent(pHighAlarm);
}
if (oldTemperature > 0 && *pTemp <= 0)
{
UaServer_Event *pLowAlarm = UaServer_Events_GetConditionByNodeId(g_UaProviderDemo_uNamespaceIndex1,
&a_pMachine->TemperatureSensor.LowAlarmId);
if (pLowAlarm)
UaProvider_Demo_FireAlarmConditionTypeEvent(pLowAlarm);
}
if (oldTemperature >= 100 && *pTemp < 100)
{
UaServer_Event *pHighAlarm = UaServer_Events_GetConditionByNodeId(g_UaProviderDemo_uNamespaceIndex1,
&a_pMachine->TemperatureSensor.HighAlarmId);
if (pHighAlarm)
UaProvider_Demo_DisableAlarmConditionTypeEvent(pHighAlarm);
}
if (oldTemperature <= 0 && *pTemp > 0)
{
UaServer_Event *pLowAlarm = UaServer_Events_GetConditionByNodeId(g_UaProviderDemo_uNamespaceIndex1,
&a_pMachine->TemperatureSensor.LowAlarmId);
if (pLowAlarm)
UaProvider_Demo_DisableAlarmConditionTypeEvent(pLowAlarm);
}

Sample code for initialization of an alarm is in UaProvider_Demo_InitializeAlarmConditionTypeEvent().

The sample code for firing alarm state changes can be found in UaProvider_Demo_FireAlarmConditionTypeEvent() and UaProvider_Demo_DisableAlarmConditionTypeEvent().

Also, sample code for acknowledge and confirm method calls for the alarm events can be found in UaProvider_Demo_CallAcknowledge() and UaProvider_Demo_CallConfirm() respectively in uaprovider_demo_call.c

The alarm sample code contains also sample code for handling Condition Branches. The corresponding sample code is contained in the define UASERVER_SUPPORT_CONDITION_BRANCHES and can be found in the files

Adding custom settings

The SDK brings the option to load its configuration from a settings backend, by default using a settings file. Using the function UaServer_Settings_GetConfigurationFromSettings the SDK is configured using the passed UaBase_Settings structure.

Applications and providers can extend the configuration with their own custom settings as needed; the demo provider adds settings for the data logger as an example.

The sample code can be found in the file

The settings file delivered with the SDK contains one additional section [DemoProvider] which contains the custom settings. The $ENV{} placeholder is dynamically replaced with the content of the accordant environment variable by the settings module:

[DemoProvider]
# Start logging on startup
StartLogging = false
# Folder to contain the logged data
DataLoggerPath = $ENV{ALLUSERSPROFILE}/UnifiedAutomation/UaSdkAnsiCServerPro/historian

In the function UaProvider_Demo_SetupDataLogger a UaBase_Settings structure is initialized with the name of the settings file. On Windows “settings.ini” is used, on all other operating systems “settings.conf”. Using this settings object the custom settings are retrieved and used for initializing the data logger:

#if defined(__WIN32) || defined(WIN32)
UaBase_Settings_Initialize(&settings, "settings.ini");
#else
UaBase_Settings_Initialize(&settings, "settings.conf");
#endif
UaBase_Settings_BeginGroup(&settings, "DemoProvider");
UaBase_Settings_ReadBool(&settings, "StartLogging", &bStartLogging, OpcUa_False);
UaBase_Settings_ReadString(&settings, "DataLoggerPath", szDataLoggerPath, sizeof(szDataLoggerPath), "");