Building a Simple Server¶
This series of tutorial guide you through your first steps with open62541. For compiling the examples, you need a compiler (MS Visual Studio 2015 or newer, GCC, Clang and MinGW32 are all known to be working). The compilation instructions are given for GCC but should be straightforward to adapt.
It will also be very helpful to install an OPC UA Client with a graphical frontend, such as UAExpert by Unified Automation. That will enable you to examine the information model of any OPC UA server.
To get started, downdload the open62541 single-file release from
http://open62541.org or generate it according to the build instructions with the “amalgamation” option enabled. From now on, we assume
you have the open62541.c/.h
files in the current folder. Now create a new
C source-file called myServer.c
with the following content:
#include <signal.h>
#include "open62541.h"
UA_Boolean running = true;
static void stopHandler(int sig) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
running = false;
}
int main(void) {
signal(SIGINT, stopHandler);
signal(SIGTERM, stopHandler);
UA_ServerConfig config = UA_ServerConfig_standard;
UA_ServerNetworkLayer nl =
UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 4840);
config.networkLayers = &nl;
config.networkLayersSize = 1;
UA_Server *server = UA_Server_new(config);
UA_Server_run(server, &running);
UA_Server_delete(server);
nl.deleteMembers(&nl);
return 0;
}
This is all that is needed for a simple OPC UA server. With the GCC compiler, the following command produces an executable:
$ gcc -std=c99 open62541.c myServer.c -o myServer
Now start the server (stop with ctrl-c):
$ ./myServer
You have now compiled and run your first OPC UA server. You can go ahead and
browse the information model with client. The server is listening on
opc.tcp://localhost:4840
. In the next two sections, we will continue to
explain the different parts of the code in detail.
Server Configuration and Plugins¶
open62541 provides a flexible framework for building OPC UA servers and clients. The goals is to have a core library that accomodates for all use cases and runs on all platforms. Users can then adjust the library to fit their use case via configuration and by developing (platform-specific) plugins. The core library is based on C99 only and does not even require basic POSIX support. For example, the lowlevel networking code is implemented as an exchangeable plugin. But don’t worry. open62541 provides plugin implementations for most platforms and sensible default configurations out-of-the-box.
In the above server code, we simply take the default server configuration and add a single TCP network layer that is listerning on port 4840.
Server Lifecycle¶
The code in this example shows the three parts for server lifecycle
management: Creating a server, running the server, and deleting the server.
Creating and deleting a server is trivial once the configuration is set up.
The server is started with UA_Server_run
. Internally, the server then
uses timeouts to schedule regular tasks. Between the timeouts, the server
listens on the network layer for incoming messages.
You might ask how the server knows when to stop running. For this, we have
created a global variable running
. Furthermore, we have registered the
method stopHandler
that catches the signal (interrupt) the program
receives when the operating systems tries to close it. This happens for
example when you press ctrl-c in a terminal program. The signal handler then
sets the variable running
to false and the server shuts down once it
takes back control. [1]
In order to integrated OPC UA in a single-threaded application with its own mainloop (for example provided by a GUI toolkit), one can alternatively drive the server manually. See the section of the server documentation on Server Lifecycle for details.
The server configuration and lifecycle management is needed for all servers. We will use it in the following tutorials without further comment.
[1] | Be careful with global variables in multi-threaded applications. You
might want to allocate the running variable on the heap. |