UA Server SDK C++ Bundle  1.3.2.200
 All Data Structures Namespaces Functions Variables Typedefs Enumerations Enumerator Groups Pages
Tutorial Server Hello World

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 the Hello World example that comes with the SDK.

Content:

Utilities used for the Example

The following utility files are used from examples/utilities:

  • shutdown.h / shutdown.cpp provides platform neutral functions to wait for user shutdown command in a console application
  • opcserver.h / opcserver.cpp provides the OpcServer utility class used for loading and unloading all SDK modules
  • serverconfigxml.h / serverconfigxml.cpp provides the ServerConfigXml class used to load the server configuration from a XML file. This class is part of the utilities since it has a dependency to the XML parser. The server SDK libraries have no dependency to the XML parser. The SDK libraries are providing the class ServerConfigSettings to load the server configuration from an INI file.

Main function for the console application

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.

#include "uaplatformlayer.h"
#include "shutdown.h"
#include <stdio.h>
#include <string.h>
int OpcServerMain(const char* szAppPath)
{
printf(" Press %s to shutdown server\n", SHUTDOWN_SEQUENCE);
while(ShutDownFlag() == 0)
{
}
return 0;
}
int main(int argc, char* argv[])
{
int ret = 0;
char *pszFind;
RegisterSignalHandler();
// Extract application path
char szAppPath[MAX_PATH];
strncpy(szAppPath, argv[0], MAX_PATH);
szAppPath[MAX_PATH-1] = 0;
pszFind = strrchr(szAppPath, '\\');
if (pszFind)
{
*pszFind = 0; // cut off appname
}
//-------------------------------------------
// Call the OPC server main method
ret = OpcServerMain(szAppPath);
//-------------------------------------------
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.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
None of the SDK functionality can be used before the UA stack initialization is done.
This requirement includes static members or global variables using UA SDK or UA Stack classes and functions.
If UA SDK or UA Stack classes and functions are used before UaPlatformLayer::init is called the server will crash since the platform layer of the UA stack is not loaded.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

#include "xmldocument.h"
int OpcServerMain(const char* szAppPath)
{
int ret = 0;
// Initialize the XML Parser
// Initialize the UA Stack platform layer
if ( ret == 0 )
{
// SDK can be used now
}
// Cleanup the UA Stack platform layer
// Cleanup the XML Parser
}

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

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 "opcserver.h"
if ( ret == 0 )
{
// Create configuration file name
UaString sXmlFileName(szAppPath);
sXmlFileName += "/ServerConfig.xml";
//- Start up OPC server ---------------------
// Create and initialize server object
OpcServer* pServer = new OpcServer;
pServer->setServerConfig(sXmlFileName, szAppPath);
// Start server object
ret = pServer->start();
if ( ret != 0 )
{
return ret;
}
//-------------------------------------------

Create configuration file name
This code uses the class UaString to create the configuraiton file name including the path. The configuration file in the example can be found in the bin directory that contains also the server executable file.

Start up OPC server
This code can be integrated into a start up sequence of the application where the OPC server should be integrated.

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 configuraiton 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 user shut down command and to shut down the OPC UA server.

#include "uathread.h"
OpcServer* pServer = new OpcServer;
pServer->setServerConfig(sXmlFileName, szAppPath);
ret = pServer->start();
printf(" Press %s to shutdown server\n", SHUTDOWN_SEQUENCE);
// Wait for user command to terminate the server thread.
while(ShutDownFlag() == 0)
{
}
//- 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 shut down"));
delete pServer;
//-------------------------------------------

Wait for user command
The loop checks for a user shut down command and sleeps betwen the checks. The platform independent UaThread::msleep function is used.

Stop OPC server
This code can be integrated into a a shut down sequence of the application where the OPC server should be integrated. 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

The following code is used to create a UA Variable in the server address space and to simulate value changes.

#include "uathread.h"
#include "opcua_basedatavariabletype.h"
// Code added after starting the server with pServer->start();
//- Add variable to address space -----------
// Get the default node manager for server specific nodes from the SDK
NodeManagerConfig* pNodeConfig = pServer->getDefaultNodeManager();
// Create a variable node with a string data type
UaVariant defaultValue;
defaultValue.setString("Hello World");
UaNodeId("HelloWorld", pNodeConfig->getNameSpaceIndex()), // NodeId of the node with string identifier "HelloWorld" and the namespace index of the default node manager which is 1
"HelloWorld", // Name of the node used for display name and browse name
pNodeConfig->getNameSpaceIndex(), // The same namespace index is also used for the brows name
defaultValue, // Setting the default value and the data type of the variable
OpcUa_AccessLevels_CurrentRead, // Setting the access level to read only
pNodeConfig); // The node manager config interface used for this node
// Add the node to the node manager using the objects folder as source node and the reference type HasComponent
pNodeConfig->addNodeAndReference(UaNodeId(OpcUaId_ObjectsFolder, 0), pVariable, OpcUaId_HasComponent);

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 nodem 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

// Simulate data
OpcUa_Boolean useOtherText = OpcUa_True;
UaVariant newValue;
UaDataValue dataValue;
while(ShutDownFlag() == 0)
{
// Toggle string value
if ( useOtherText == OpcUa_False )
{
newValue.setString("Hello World");
useOtherText = OpcUa_True;
}
else
{
newValue.setString("My Text");
useOtherText = OpcUa_False;
}
// Set new value
dataValue.setValue(newValue, OpcUa_False, OpcUa_True);
pVariable->setValue(NULL, dataValue, OpcUa_False);
}

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 UaVariable::setValue method.
The value is changed every second based on the sleep for 1000ms.