ANSI C Based OPC UA Client/Server SDK  1.8.3.398
How to Integrate the OPC UA Server SDK into Your Application

There are two main development tasks to implement an OPC UA Server using the SDK. One is to integrate the SDK and the UA Server into a new or an existing application. The second task is to integrate the data sources into the SDK. This tutorial gives an introduction on how to integrate the ANSI C UA SDK into a new application or an existing application.

The provided sample code implements the startup and the shutdown code in the main function of the console application. For integration in existing applications the startup code for the SDK can be integrated into the startup sequence of the application and the shutdown code of the SDK into the shutdown sequence of the application.

Please read the Introduction section before working through this tutorial.

Global Variables Needed for the Integration

There are two variables needed during the whole runtime of the OPC UA Server. One variable is the handle of the UA Stack platform layer. The other one is the UaServer object containing configuration and management data for the OPC UA Server instance. The variables can be global variables or part of an application management structure available during runtime of the Server.

/* The UA Stack platform layer instance */
static OpcUa_Handle g_hProxyStubPlatformLayer = OpcUa_Null;
/* The UA Server instance */
static UaServer g_UaServer;

Startup Code for UA Stack and UA Server

The first step in the startup code is the initialization of the UA Stack. This requires filling the ProxyStubConfiguration structure with the configuration setting for the UA Stack. The helper function OpcUa_ProxyStubConfiguration_InitializeDefault is setting default values configured for the stack. The trace and serializer settings can be changed. In a second step the stack is initialized and the platform layer is assigned to the stack.

See also
OpcUa_ProxyStubConfiguration_InitializeDefault for a description of the structure
OpcUa_StatusCode InitializeOpcUaStack(OpcUa_Handle *a_phProxyStubPlatformLayer,
OpcUa_ProxyStubConfiguration *a_pProxyStubConfiguration)
{
OpcUa_InitializeStatus(OpcUa_Module_Server, "InitializeOpcUaStack");
/* Initialize Stack */
OpcUa_Trace(OPCUA_TRACE_LEVEL_WARNING, "UA Server: Initializing Stack...\n");
/* Default values can be changed here */
a_pProxyStubConfiguration->bProxyStub_Trace_Enabled = OpcUa_True;
a_pProxyStubConfiguration->uProxyStub_Trace_Level = OPCUA_TRACE_OUTPUT_LEVEL_ERROR;
uStatus = UaBase_Module_InitializeUaStack(a_phProxyStubPlatformLayer, a_pProxyStubConfiguration);
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}

The second step is to initialize the UaServer object. With the first call to UaServer_Initialize the structure and contained structures will be initialized.

After initializing the server object, UaServer_GetConfiguration can be used to get the configuration structure of the server to set server instance specific settings like EndpointUrl, ApplicationUri, ApplicationName and product specific settings like the ProductUri.

With the following calls the providers are initialized:

UaServer_Provider customProvider;
UaServer_Configuration *pServerConfig = OpcUa_Null;
...
/* Initialize Server */
uStatus = UaServer_Initialize(&uaServer);
OpcUa_GotoErrorIfBad(uStatus);
/* Get configuration structure of the server */
pServerConfig = UaServer_GetConfiguration(&uaServer);
/* Configure ApplicationDescription of the server */
OpcUa_String_AttachCopy(&pServerConfig->ApplicationDescription.ApplicationUri, szApplicationUri);
OpcUa_String_AttachReadOnly(&pServerConfig->ApplicationDescription.ProductUri, UASERVER_PRODUCTURI);
OpcUa_String_AttachReadOnly(&pServerConfig->ApplicationDescription.ApplicationName.Text, UASERVER_APPLICATIONNAME);
pServerConfig->ApplicationDescription.NoOfDiscoveryUrls = 1;
pServerConfig->ApplicationDescription.DiscoveryUrls = OpcUa_Alloc(sizeof(OpcUa_String));
OpcUa_String_Initialize(&pServerConfig->ApplicationDescription.DiscoveryUrls[0]);
OpcUa_String_AttachCopy(&pServerConfig->ApplicationDescription.DiscoveryUrls[0], szEndpointURL);
/* Configure BuildInfo of the server */
OpcUa_String_AttachReadOnly(&pServerConfig->BuildInfo.ManufacturerName, UASERVER_MANUFACTURERNAME);
OpcUa_String_AttachReadOnly(&pServerConfig->BuildInfo.ProductName, UASERVER_PRODUCTNAME);
OpcUa_String_AttachReadOnly(&pServerConfig->BuildInfo.SoftwareVersion, UASDK_VERSION);
OpcUa_String_AttachReadOnly(&pServerConfig->BuildInfo.BuildNumber, UASDK_BUILD_VERSION_STR);
OpcUa_String_AttachReadOnly(&pServerConfig->BuildInfo.ProductUri, UASERVER_APPLICATIONURI);
pServerConfig->BuildInfo.BuildDate = UaServer_GetBuildDate();
/* Create one endpoint */
pServerConfig->uNoOfEndpoints = 1;
pServerConfig->pEndpoints = OpcUa_Alloc(sizeof(UaServer_Endpoint));
UaServer_Endpoint_Initialize(&pServerConfig->pEndpoints[0]);
pEndpoint = &pServerConfig->pEndpoints[0];
/* Set the endpoint URL */
OpcUa_String_AttachCopy(&pEndpoint->sEndpointUrl, szEndpointURL);
/* This example does not use security, disable PKI */
OpcUa_String_AttachReadOnly(&pEndpoint->PkiConfigName, "PkiStoreNone");
pEndpoint->PkiConfig.strPkiType = (char*)OPCUA_PKI_TYPE_NONE;
/* Set the endpoint configuration to use no security */
pEndpoint->pSecurityPolicyConfigurations = OpcUa_Alloc(sizeof(OpcUa_Endpoint_SecurityPolicyConfiguration));
OpcUa_MemSet(pEndpoint->pSecurityPolicyConfigurations, 0, sizeof(OpcUa_Endpoint_SecurityPolicyConfiguration));
OpcUa_String_AttachReadOnly(&pEndpoint->pSecurityPolicyConfigurations[0].sSecurityPolicy, OpcUa_SecurityPolicy_None);
pEndpoint->pSecurityPolicyConfigurations[0].uMessageSecurityModes = OPCUA_ENDPOINT_MESSAGESECURITYMODE_NONE;
/* Set the endpoint configuration to use anonymous logon */
pEndpoint->uNoOfUserTokenPolicy = 1;
pEndpoint->pUserTokenPolicy = OpcUa_Alloc(sizeof(OpcUa_UserTokenPolicy));
OpcUa_UserTokenPolicy_Initialize(&pEndpoint->pUserTokenPolicy[0]);
OpcUa_String_AttachReadOnly(&pEndpoint->pUserTokenPolicy[0].PolicyId, "Anonymous");
/* Initialize the provider list, the server provider is added automatically */
OpcUa_Trace(OPCUA_TRACE_LEVEL_WARNING, "UA Server: Building Provider List...\n");
uStatus = UaServer_ProviderList_Create(&uaServer);
OpcUa_GotoErrorIfBad(uStatus);
/* Add custom provider */
OpcUa_MemSet(&customProvider, 0, sizeof(customProvider));
customProvider.pfInit = CustomProvider_Initialize;
uStatus = UaServer_ProviderList_AddProvider(&uaServer, &customProvider);
OpcUa_GotoErrorIfBad(uStatus);
/* Load providers */
OpcUa_Trace(OPCUA_TRACE_LEVEL_WARNING, "UA Server: Loading Provider Modules...\n");
uStatus = UaServer_Providers_Initialize(&uaServer);
OpcUa_GotoErrorIfBad(uStatus);

The third step is to start the Ua Server. After this call, the main loop has to be executed by calling UaBase_DoCom cyclically until it returns an error or the server should be shut down. From this point on, clients are able to connect to the server.

/* Start up server */
uStatus = UaServer_StartUp(&uaServer);
OpcUa_GotoErrorIfBad(uStatus);
...
/* Initialize the check for shutdown keystrokes. On Linux, default signal handlers
are added in UaBase_P_RegisterShutdownHandler. */
/******************************************************************************/
/* Serve! */
while (OpcUa_IsGood(uStatus))
{
uStatus = UaBase_DoCom();
}
/******************************************************************************/
/* Clean up the check for shutdown keystrokes */

Using Settings to Configure the Server

An alternative to manually setting the server configuration is to use the UaBase_Settings utilities:

UaServer_Configuration *pServerConfiguration = OpcUa_Null;
...
pServerConfiguration = UaServer_GetConfiguration( &g_UaServer );
#if OPCUA_TRACE_ENABLE && UABASE_USE_FILESYSTEM
uStatus = UaBase_FileTrace_SetApplicationName(OpcUa_String_FromCString(UASERVER_APPLICATION_NAME));
OpcUa_GotoErrorIfBad(uStatus);
uStatus = UaBase_FileTrace_SetApplicationVersion(OpcUa_String_FromCString(UASDK_VERSION));
OpcUa_GotoErrorIfBad(uStatus);
#endif /* OPCUA_TRACE_ENABLE && UABASE_USE_FILESYSTEM */
uStatus = UaServer_Settings_GetConfigurationFromSettings(pSettings, pServerConfiguration, szHostname);
OpcUa_GotoErrorIfBad( uStatus );
/* ApplicationDescription and BuildInfo are not set by UaServer_Settings_GetConfigurationFromSettings,
the application has to set those manually */
/* ApplicationDescription */
OpcUa_String_AttachReadOnly(&pServerConfiguration->ApplicationDescription.ApplicationUri, UASERVER_APPLICATION_URI);
OpcUa_String_AttachReadOnly(&pServerConfiguration->ApplicationDescription.ProductUri, UASERVER_PRODUCT_URI);
OpcUa_String_AttachReadOnly(&pServerConfiguration->ApplicationDescription.ApplicationName.Text, UASERVER_APPLICATION_NAME);
/* DiscoveryUrls are filled in UaServer_Settings_GetConfigurationFromSettings using all endpoint URLs */
/* Replace [gethostname] in ApplicationName and ApplicationUri */
UaBase_Settings_ReplaceUaString(&pServerConfiguration->ApplicationDescription.ApplicationName.Text, "[gethostname]", szHostname);
UaBase_Settings_ReplaceUaString(&pServerConfiguration->ApplicationDescription.ApplicationUri, "[gethostname]", szHostname);
/* BuildInfo */
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.ProductUri, UASERVER_PRODUCT_URI);
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.ManufacturerName, UASERVER_MANUFACTURERNAME);
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.ProductName, UASERVER_PRODUCT_NAME);
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.SoftwareVersion, UASDK_VERSION);
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.BuildNumber, UASDK_BUILD_VERSION_STR);
pServerConfiguration->BuildInfo.BuildDate = UaServer_GetBuildDate();
/* ServerCapabilities */
pServerConfiguration->pServerCapabilities = OpcUa_Alloc(3 * sizeof(*pServerConfiguration->pServerCapabilities));
OpcUa_GotoErrorIfAllocFailed(pServerConfiguration->pServerCapabilities);
OpcUa_MemSet(pServerConfiguration->pServerCapabilities, 0, 3 * sizeof(*pServerConfiguration->pServerCapabilities));
pServerConfiguration->uNoOfServerCapabilities = 3;
OpcUa_String_AttachReadOnly(&pServerConfiguration->pServerCapabilities[0], "DA");
OpcUa_String_AttachReadOnly(&pServerConfiguration->pServerCapabilities[1], "HD");
OpcUa_String_AttachReadOnly(&pServerConfiguration->pServerCapabilities[2], "AC");

Shutdown Code for UA Server and UA Stack

The shutdown sequence clears the UaServer object first which also closes the Endpoints. Clients are no longer able to connect to the server after this call. In the second call the UA stack is cleaned up.

/* Clean up server */
UaServer_Clear(&uaServer);
...
OpcUa_StatusCode CleanupOpcUaStack(OpcUa_Handle *a_phProxyStubPlatformLayer)
{
OpcUa_InitializeStatus(OpcUa_Module_Server, "CleanupOpcUaStack");
/* Clean Up UA Stack */
uStatus = UaBase_Module_ClearUaStack(a_phProxyStubPlatformLayer);
OpcUa_ReturnStatusCode;
OpcUa_BeginErrorHandling;
OpcUa_FinishErrorHandling;
}