C++ Based OPC UA Client/Server/PubSub SDK  1.7.6.537
Lesson 1: Setting up a Basic OPC UA Server Console Application

This lesson will guide you through the process of setting up a basic OPC UA Server console application.

Utilities Used in this Lesson

The lesson uses servermain.cpp from examples.

In addition the following utility files are used from examples/utilities:

  • opcserver.h/opcserver.cpp
  • shutdown.h/shutdown.cpp

The utility files opcserver.h/opcserver.cpp contain the class OpcServer used to manage all OPC UA SDK modules.

The utility files shutdown.h/shutdown.cpp provide helper functionality for handling operating system specific shut down signals.

Linux specific: Signal handlers

On Linux you SHOULD install a signal handler to handle SIGPIPE. SIGPIPE is sent when writing on a closed socket which can happen when a connection is broken. The default handler terminates the application which is the desired behaviour when using pipes for inter-process communication, but not for networking applications like OPC UA. You can ignore this signal by calling signal(SIGPIPE, SIG_IGN) or by installing a signal handler.

You should also handle the signals SIGINT and SIGTERM like shown in the following example. SIGINT is sent when a user wants to interrupt a process by pressing CTRL-C on controlling terminal. SIGTERM is sent when a process is requested to terminate. SIGTERM is the default signal sent to a process by the kill or killall command. Unlike the SIGKILL signal, it can be caught by an application so that it can properly terminate. SIGTERM is also sent by the init process during system shutdown, it then waits a few seconds and then sends SIGKILL to all remaining processes to forcibly terminate them.

For more information on signals please see man signal(2), man sigaction(2), or the book UNIX Network Programming from Richard Stevens, Addison Wesley.

In our examples we setup signal handlers in the utility function RegisterSignalHandler() in shutdown.cpp. You can either use this utility function or set up your own signal handlers like shown in the following example.

#include <signal.h>
/* Shutdown flag: Note that this must be defined volatile, because it gets set by a signal handler. */
static volatile unsigned int g_ShutDown = 0;
void signal_handler(int signo)
{
SHUTDOWN_TRACE("Received signal %i\n", signo);
g_ShutDown = 1;
}
void RegisterSignalHandler()
{
/* register signal handlers. */
struct sigaction new_action, old_action;
/* Set up the structure to specify the new action. */
new_action.sa_handler = signal_handler;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
/* install new signal handler for SIGINT */
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN)
{
sigaction(SIGINT, &new_action, NULL);
}
/* install new signal handler for SIGTERM */
sigaction(SIGTERM, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN)
{
sigaction(SIGTERM, &new_action, NULL);
}
/* Set up the structure to prevent program termination on interrupted connections. */
new_action.sa_handler = SIG_IGN;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
/* install new signal handler for SIGPIPE*/
sigaction(SIGPIPE, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN)
{
sigaction(SIGPIPE, &new_action, NULL);
}
}

Step 1: Create a New Project

Set up a console application.

Windows:

Create a new project. Use the following settings:

  • Win32 Console Application
  • No precompiled headers
  • Empty project.

Step 2: Add Files to the Project

Add the files listed below to your application:

  • [SDK Installation Directory]/examples/utilities/opcserver.h
  • [SDK Installation Directory]/examples/utilities/opcserver.cpp
  • [SDK Installation Directory]/examples/utilities/shutdown.h
  • [SDK Installation Directory]/examples/utilities/shutdown.cpp

Create the main function in servermain.cpp

The example is using a console application.

The following code provides a generic main function without OPC UA code that extracts the application path from the input arguments and calls the function OpcServerMain where we will add the OPC UA specific code. The function in the following code contains only the waiting loop for a user shutdown command.

Add a new source file named “servermain.cpp” to your project and add the following code.

#include "uaplatformlayer.h"
#include "shutdown.h"
int OpcServerMain(const char* szAppPath)
{
int ret = 0;
printf(" Press %s to shut down server\n", SHUTDOWN_SEQUENCE);
while( ShutDownFlag() == 0 )
{
}
return ret;
}
int main(int, char*[])
{
int ret = 0;
RegisterSignalHandler();
// Extract application path
char* pszAppPath = getAppPath();
//-------------------------------------------
// Call the OPC server main method
ret = OpcServerMain(pszAppPath);
//-------------------------------------------
if ( pszAppPath ) delete [] pszAppPath;
return ret;
}

Initializing UA Stack and XML Parser

The UA Stack and the XML Parser modules are ANSI C based modules and do require a global initialization before any of their functionality is used.

The initialization of the XML Parser is only needed if the XML configuration file is used through the class ServerConfigXml.

If the UaPlatformLayer::init call succeeds, the UA SDK and UA Stack classes and functions can be used.

Note
It is important that UaXmlDocument::initParser is only called once on start-up (or only from the main thread) and UaXmlDocument::cleanupParser only once on exit. Calling initParser multiple times from different threads will cause a memory leak (see also class documentation for UaXmlDocument).

Add the marked line and replace the code in OpcServerMain() as shown below.

#include "uaplatformlayer.h"
#include "shutdown.h"
#include "xmldocument.h" // Add this line
int OpcServerMain(const char* szAppPath)
{
int ret = 0;
//- Initialize the environment --------------
// Initialize the XML Parser
// Initialize the UA Stack platform layer
//-------------------------------------------
if ( ret == 0 )
{
// SDK can be used now
}
//- Clean up the environment --------------
// Clean up the UA Stack platform layer
// Clean up the XML Parser
//-------------------------------------------
return ret;
}

Create OPC UA Server

The following code is used to initialize and start the server object. OPC UA clients can connect to the server after this code is executed.

#include "uaplatformlayer.h"
#include "shutdown.h"
#include "xmldocument.h"
#include "opcserver.h" // Add this line
...
//-------------------------------------------
if ( ret == 0 )
{
// New code begins
// Create configuration file name
UaString sConfigFileName(szAppPath);
sConfigFileName += "/ServerConfig.xml";
//- Start up OPC server ---------------------
//-------------------------------------------
// Create and initialize server object
OpcServer* pServer = new OpcServer;
pServer->setServerConfig(sConfigFileName, szAppPath);
// Start server object
ret = pServer->start();
if ( ret != 0 )
{
delete pServer;
pServer = 0;
}
//-------------------------------------------
// New code ends
}
Create configuration file name
This code uses the class UaString to create the configuration file name including the path. The configuration file in the example can be found in [SDK Installation Directory]/bin (which contains the server executable file as well). It is also possible to use an ini configuration file (see the file [SDK Installation Directory]/examples/server_getting_started/lesson01/servermain.cpp). An example is included in the same directory as the xml configuration file.
Start up OPC server
This code can be used in the startup sequence of the application where the OPC server should be integrated into.
Create and initialize server object
This code creates an instance of the class OpcServer used to manage all OPC UA SDK modules. In a second step the configuration file and application path is set to allow the server object to find its configuration and PKI folder.
Start server object
After the start function of the OpcServer object is called successfully, OPC UA clients can connect to the server.

Shut Down OPC UA Server

The following code is used to wait for a user shutdown command and to shut down the OPC UA server.

#include "uaplatformlayer.h"
#include "shutdown.h"
#include "xmldocument.h"
#include "opcserver.h"
#include "uathread.h" // Add this line
...
// Start server object
ret = pServer->start();
if ( ret != 0 )
{
delete pServer;
pServer = 0;
}
// New code begins
if ( ret == 0 )
{
printf("***************************************************\n");
printf(" Press %s to shut down server\n", SHUTDOWN_SEQUENCE);
printf("***************************************************\n");
// Wait for user command to terminate the server thread.
while ( ShutDownFlag() == 0 )
{
}
printf("***************************************************\n");
printf(" Shutting down server\n");
printf("***************************************************\n");
//- Stop OPC server -------------------------
// Stop the server and wait three seconds if clients are connected
// to allow them to disconnect after they received the shutdown signal
pServer->stop(3, UaLocalizedText("", "User shutdown"));
delete pServer;
pServer = NULL;
//-------------------------------------------
}
// New code ends
Wait for user command
The loop checks for a user shutdown command and sleeps between the checks. The platform independent UaThread::msleep function is used.
Stop OPC server
This code can be used in the shutdown sequence of the application where the OPC server should be integrated into. It stops the server and provides information that is sent to the clients connected to the server to allow them to disconnect from the server.

Step 3: Add Include Directories

Add the following include paths to your application:

[SDK Installation Directory]\examples\utilities
[SDK Installation Directory]\include\uastack
[SDK Installation Directory]\include\uabasecpp
[SDK Installation Directory]\include\uapkicpp
[SDK Installation Directory]\include\xmlparsercpp
[SDK Installation Directory]\include\uaservercpp
[SDK Installation Directory]\third-party\win32\[VisualStudioVersion]\openssl\inc32
[SDK Installation Directory]\third-party\win32\[VisualStudioVersion]\libxml2\include

The following include path is only needed for this example:
[SDK Installation Directory]/examples/simulation_buildingautomation

For more information see LibraryOverview.

Step 4: Add Linker Settings

Windows:
For Additional Library Directories (Debug) enter the following values:
[SDK Installation Directory]\lib
[SDK Installation Directory]\third-party\win32\[VisualStudioVersion]\openssl\out32dll.dbg
[SDK Installation Directory]\third-party\win32\[VisualStudioVersion]\libxml2\out32dll.dbg

For Additional Library Directories (Release) enter the following values:
[SDK Installation Directory]\lib
[SDK Installation Directory]\third-party\win32\[VisualStudioVersion]\openssl\out32dll
[SDK Installation Directory]\third-party\win32\[VisualStudioVersion]\libxml2\out32dll

For Additional Dependencies (Debug) enter:
uastackd.lib
uabasecppd.lib
uapkicppd.lib
uamoduled.lib
coremoduled.lib
xmlparsercppd.lib
libcryptod.lib
libxml2d.lib
ws2_32.lib
rpcrt4.lib
crypt32.lib

For Additional Dependencies (Release) enter:
uastack.lib
uabasecpp.lib
uapkicpp.lib
uamodule.lib
coremodule.lib
xmlparsercpp.lib
libcrypto.lib
libxml2.lib
ws2_32.lib
rpcrt4.lib
crypt32.lib

Linux:
For Additional Library Directories enter the following values:
-L[SDK Installation Directory]/lib

For Additional Dependencies (Debug) enter:
-luamoduled -lcoremoduled -luapkicppd -luabasecppd -luastackd -lxmlparsercppd -lssl -lxml2

For Additional Dependencies (Release) enter:
-luamodule -lcoremodule -luapkicpp -luabasecpp -luastack -lxmlparsercpp -lssl -lxml2

Note
GCC requires that the libraries are linked in the correct order.

For more information see LibraryOverview.

Step 5: Add Preprocessor Defines

Add:

_UA_STACK_USE_DLL

Additonal defines for Windows:

UNICODE
_UNICODE
_CRT_SECURE_NO_WARNINGS
_CRT_SECURE_NO_DEPRECATE

Step 6: Set Output Path

Set the output path to “bin” where the config file “ServerConfig.xml” can be found.

Windows:

Enter these values:

Output Directory
[SDK Installation Directory]\bin
Intermediate Directory
obj\$(Configuration)

Step 7: Run Application

Compile and run the server application.

Try to connect to the server with an OPC UA Client, e.g. UaExpert. Figure 1-1 shows how to add a server in UaExpert.

Figure 1-1 Add server to UaExpert

l4gettingstartedlesson01_uaexpert_addserver.png

Double click on “<Double click to Add Server...>” beneath “Custom Discovery” and edit the server properties as shown in Figure 1-2.

Figure 1-2 Edit server properties.

l4gettingstartedlesson01_uaexpert_editserver.png