High Performance OPC UA Server SDK  1.4.1.263
Lesson 1: Setting up an OPC UA Server Console Application

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

Files used in this lesson:

Prerequisites

The recommended way of building applications with the SDK is using CMake. This and the further lessons rely on CMake being installed on the build machine.

Step 1: CMakeLists.txt

Each lesson contains a CMakeLists.txt with the following structure:

  • project name and programming language:
    project(server_lesson01 C)
  • minimum required version of cmake:
    cmake_minimum_required(VERSION 3.0)
  • include of the sdk.cmake delivered with the SDK (you might need to adjust this path to your specific install location)
    include(../../../sdk.cmake)
  • lists with source and header files (this lesson only has one source file and no header files)
    set(SOURCES server_main.c)
  • executable to build; the project name is used as name for the executable
    add_executable(${PROJECT_NAME} ${SOURCES})
  • libraries to link to the project; these variables are set by the included sdk.cmake
    target_link_libraries(${PROJECT_NAME}
    ${SDK_SERVER_LIBRARIES}
    ${SDK_BASE_LIBRARIES}
    ${OS_LIBS}
    ${SDK_SYSTEM_LIBS}
    )
  • folder where the executable should be installed (bin)
    install(TARGETS ${PROJECT_NAME} DESTINATION bin)

Step 2: server_main.c

The file server_main.c contains the functions server_main_init to start the server and server_main_clear to shut down the server. These are called by the main entry function of the compiled binary.

Create the main Function

The main function starts the server and keeps the server running by calling uaapplication_timed_docom in the main loop until a shutdown is requested. Then the server is shut down:

int main(int argc, char *argv[])
{
int ret;
unsigned int i;
struct commandline_args args;
parse_commandline(argc, argv, &args);
g_shutdown_time = args.shutdown_time;
ret = server_main_init(args.trace_level, args.facility_mask, args.config_file);
if (ret != 0) {
printf("server_main_init() failed with error 0x%08x\n", ret);
exit(EXIT_FAILURE);
}
printf("Server is up and running.\n");
for (i = 0; i < g_appconfig.endpoint.num_endpoints; ++i) {
printf("Listening on %s\n", g_appconfig.endpoint.endpoints[i].endpoint_url);
}
fflush(stdout);
while (ua_shutdown_should_shutdown() == 0) {
}
TRACE_NOTICE(TRACE_FAC_APPLICATION, "Shutting down server...\n");
server_main_clear();
return 0;
}

Initialize the UA Stack

First a crash handler is installed, the platform layer is initialized and the trace (if available) is opened with the given level and facilities:

To continue, the configuration of the server must be set. It is being stored in a global variable that must be named g_appconfig:

The appconfig struct controls memory consumption by specifing the maximum amount of dynamically allocated memory and the number of stack and SDK resources, like the maximum number of secure channels, sessions, or subscriptions. It also specifies the number and properties of endpoints to open and some further settings. The easiest way to create the appconfig is loading it from a settings file:

There are some fields in the appconfig that are not loaded from the settings file but have to be set manually. These are the information about the manufacturer, the product, and the application.

g_appconfig.manufacturer_name = COMPANY_LONG_NAME;
g_appconfig.product_name = PRODUCT_NAME;
g_appconfig.product_uri = PRODUCT_URI;
g_appconfig.application_name = SERVER_LONG_NAME;
g_appconfig.application_uri = uri;
g_appconfig.dns = fqdn;

Now the shared memory and interprocess communication mechanism can be initialized. This allows using IPC functionality like ipc_malloc.

Initialize the UA SDK

Next the SDK is initialized to set up libaries like encoder and allocate SDK resources. Also the address space is created by initializing the providers.

The providers to be loaded are stored in a global array with a handle and an init function for each array. The handle used to uniquely identify the provider will be assigned by the SDK, so it should be set to a negative number before start. The init function must be implemented by the provider. How to create a provider and implement such a function will be covered in the next lesson, in this application only the server provider is included:

static struct uaserver_provider g_provider[] = {{-1, uaprovider_server_init}};
TRACE_INFO(TRACE_FAC_APPLICATION, "Initializing providers...\n");
ret = uaserver_init(g_provider, countof(g_provider));
if (ret < 0) goto out_server_init;

Start the Server

Now the server can be started to begin listening for network connections:

TRACE_INFO(TRACE_FAC_APPLICATION, "Starting server...\n");
ret = uaserver_start();
if (ret < 0) goto out_server_start;

Shut down the Server

The server stops handling network connections by calling uaserver_stop. The resources are cleared in the opposite direction than they were allocated:

int server_main_clear(void)
{
unsigned int sec;
for (sec = g_shutdown_time; sec > 2; --sec) {
TRACE_NOTICE(TRACE_FAC_APPLICATION, "Sending shutdown notice to client (%u)...\n", sec);
/* inform clients about shutdown */
uaapplication_shutdown(&g_app, 1000); /* process events f*/
}
/* stop the server: no more info is sent to client */
TRACE_NOTICE(TRACE_FAC_APPLICATION, "Stopping server...\n");
for (; sec > 0; --sec) {
TRACE_NOTICE(TRACE_FAC_APPLICATION, "Waiting for server to cleanup resources (%u)...\n", sec);
uaapplication_shutdown(&g_app, 1000); /* process events */
}
TRACE_NOTICE(TRACE_FAC_APPLICATION, "Final cleanup.\n");
/* final cleanup */
uaserver_clear(g_provider, countof(g_provider));
return 0;
}

Step 3: Run CMake and Build Application

CMake needs to know the source folder and the build folder for the project. The source folder is the lesson01 folder where the above mentioned CMakeLists.txt and server_main.c are stored (you can also use the higher level server_gettingstarted folder to build all lessons at once). The build folder is the folder where the executable will be built, e.g. a newly created folder inside or next to the source folder.

Depending on your platform you can choose one of the options described below.

Console on Linux

  • Open a console and change the working directory to the source folder, then create the build folder:
    $ mkdir bld
  • Change the working directory to the build folder:
    $ cd bld
  • Run cmake with options like build type and the source folder as argument
    $ cmake -DCMAKE_BUILD_TYPE=Debug ..
  • Build the application using make
    $ make

Visual Studio on Windows

  • Open CMake (cmake-gui) from the start menu
  • Enter the source folder and build folder (the build folder does not need to exist yet)
  • Click the Configure button and choose your Visual Studio version
  • Click the Generate button
  • Open the generated solution in the build folder with Visual Studio
  • Use Visual Studio to build the project

Step 4: Run Application

To start the server, the follwing files must be in the same folder as the server:

settings.conf
Configuration settings for the server
passwd
User names and passwords for authentication
users
User names for authorization
groups
Group names for authorization
ns0.bin
File for loading the server namespace

Furthermore, the openssl dynamic libraries are needed. These must be either in the default search path of the system or also next to the server executable.

The endpoint address and port of the server default to 127.0.0.1 and

  1. These can be changed in the settings.conf file by editing the values of endpoint/0/address and endpoint/0/port.

Now the server application can be started from its folder and a UA client like UaExpert can be used to connect and browse through the server addressspace.