This simple Hello World tutorial explains how to create an OPC UA server with a few lines of code.
The complete source code can be found in Examples → Hello World Server project.
Utilities Used for the Example
The example is using a console application. A description on how to set up an empty project to develop an OPC UA Server using the Unified Automation C++ based SDK can be found in Lesson 1: Setting up a Basic OPC UA Server Console Application.
The following utility files are used from examples/utilities. Add these file to your project:
- shutdown.h/shutdown.cpp provide platform neutral functions to wait for the user shutdown command in a console application
- opcserver.h/opcserver.cpp provide the OpcServer utility class used for loading and unloading all SDK modules
Main Function for the Console Application
The following code provides a generic main method without OPC UA code that extracts the application path from the input arguments and calls the method OpcServerMain where we will add the OPC UA specific code. As a first step, OpcServerMain contains only the waiting loop for a user shutdown command.
#include <stdio.h>
#include <string.h>
#include "shutdown.h"
#include "uaplatformlayer.h"
int OpcServerMain(const char* szAppPath)
{
int ret = 0;
printf("***************************************************\n");
printf(" Press %s to shut down server\n", SHUTDOWN_SEQUENCE);
printf("***************************************************\n");
while (ShutDownFlag() == 0)
{
}
return ret;
}
int main(int, char*[])
{
int ret = 0;
RegisterSignalHandler();
char* pszAppPath = getAppPath();
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).
Replace OpcServerMain:
int OpcServerMain(const char* szAppPath)
{
int ret = 0;
if ( ret == 0 )
{
printf("***************************************************\n");
printf(" Press %s to shut down server\n", SHUTDOWN_SEQUENCE);
printf("***************************************************\n");
while (ShutDownFlag() == 0)
{
}
}
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.
...
if ( ret == 0 )
{
sConfigFileName += "/ServerConfig.xml";
if ( ret != 0 )
{
delete pServer;
return ret;
}
...
- 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_hello_world/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 the user sending the shutdown command and to shut down the OPC UA server.
...
printf("***************************************************\n");
printf(" Press %s to shut down server\n", SHUTDOWN_SEQUENCE);
printf("***************************************************\n");
while(ShutDownFlag() == 0)
{
}
printf("***************************************************\n");
printf(" Shutting down server\n");
printf("***************************************************\n");
delete pServer;
pServer = NULL;
...
- 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.
Create a variable in the server’s address space
The following code is used to create a UA Variable in the server’s address space and to simulate value changes.
#include "opcua_basedatavariabletype.h"
...
if ( ret != 0 )
{
delete pServer;
return ret;
}
"HelloWorld",
defaultValue,
OpcUa_AccessLevels_CurrentRead,
pNodeConfig);
...
- Get default NodeManager
- The server object provides a method to get the default node manager of the server with namespace index 1 that can be used to add nodes that have no specific namespace.
- Create a variable node
- This code can be integrated after starting the server. It uses one of the SDK provided classes to create a UA Variable with a string data type. After creating the node it is added to the node manager using the method NodeManagerConfig::addNodeAndReference. This method adds the node to the node manager and creates a reference from the source node to the newly created target node. The source node can be passed in as node pointer or as NodeId. In this case the NodeId of the OPC UA defined Objects Folder is passed in as source node. The Objects Folder is the main entry point for instance nodes.
OpcUa_Boolean useOtherText = OpcUa_True;
while(ShutDownFlag() == 0)
{
if ( useOtherText == OpcUa_False )
{
useOtherText = OpcUa_True;
}
else
{
useOtherText = OpcUa_False;
}
dataValue.
setValue(newValue, OpcUa_False, OpcUa_True);
pVariable->
setValue(NULL, dataValue, OpcUa_False);
}
printf("***************************************************\n");
printf(" Shutting down server\n");
printf("***************************************************\n");
- Simulate data
- To simulate data the shut down waiting loop is used to change the data. The sample code toggles the string value between “Hello World” and “My Text”. The new value is then passed to the variable by using the method UaVariable::setValue. The value is changed every second based on the sleep for 1000ms.