ANSI C Based OPC UA Client/Server/PubSub SDK  1.9.2.463
Server Address Space

The Unified Automation ANSI C Server SDK provides several options for creating the server address space.

Introduction

The OPC UA server address space contains nodes that provide information from the underlying system that is represented by the OPC UA server. A very simple address space structure can be created with folder objects. In such a simple structure, the folders contain data variables representing the data of the system.

Most OPC UA servers use specific ObjectTypes for the objects they provide in their address space. These ObjectTypes are typically defined by standard information models or by the server vendor. ObjectTypes define member variables and optionally methods. More details can be found in the Address Space Concepts section.

The address space consists of nodes such as object, variable or method nodes. These nodes are connected by references. A node belongs to a Namespace.

In the ANSI C SDK, a namespace is implemented by a Provider. A product specific provider can be integrated by implementing the Provider Interface.

The namespaces for index 0 (OPC UA) and 1 (local server) are implemented and handled by OPC UA server SDKs in the Server Provider (see Provider Architecture and OPC UA Namespaces). They are automatically created during server start-up. Additional Providers are added by the application specific code in the server main function as shown in the following simplified sample code.

static UaServer g_UaServer;
/* Initialize Providers and create Server Provider */
uStatus = UaServer_ProviderList_Create( &g_UaServer );
OpcUa_GotoErrorIfBad( uStatus );
/* Add demo provider */
OpcUa_MemSet( &Provider, 0, sizeof( Provider ) );
Provider.pfInit = UaProvider_Demo_Initialize;
UaServer_ProviderList_AddProvider( &g_UaServer, &Provider );

The functions main() and ServerMain() in the ANSI C SDK Demo Server provides a more complete example for the code necessary to initialize the SDK and the providers.

The Server Getting Started tutorial provides sample code for creating a simple provider in Lesson 2, Step 1: Creating a New Provider.

The SDK has three main options to create the application specific nodes for a namespace in the server address space:

Create nodes with custom code
Create every single node with UaServer_CreateNode() in application specific code. This option is typically used if the address space if created from a vendor specific configuration or from information provided by the underlying system e.g. the capabilities and configuration of a device.
Use generated code from UaModeler
Create code with UaModeler where all nodes in the namespace are created with the generated code. This option can be used for types known at compile time of the server and is simplifying the server implementation, especially if the namespace defines structure DataTypes and ObjectTypes with methods where the business logic for the methods is implemented in the OPC UA server code.
UANodeSet binary import
Load namespace from UANodeSet binary file to create the nodes for this namespace. This option can be used to load nodes that are not known at compile time of the server. The limitations and the solution to work around this limitations is described in the detailed UANodeSet binary import section.

When generated code is used, typically the type namespaces are created with generated code and the instance namespace is created with custom code. The instances are created and initialized from the current configuration of the underlying system.

Create nodes with custom code

The Server Getting Started tutorial provides sample code for creating ObjectTypes with Variables and Object instances for the ObjectTypes in Lesson 2, Step 2: Creating the TemperatureSensorType and the following steps.

Use generated code from UaModeler

The tool UaModeler is used to generate code for the ANSI C SDK. UaModeler can either load existing information models by importing the corresponding UANodeSet XML file or own information models can be created using UaModeler. The code generation does not require a license. Nevertheless, the SDK license also contains a UaModeler license necessary for exporting information models to a UANodeSet XML file.

The documentation of UaModeler provides a step by step example on how to create a project and an OPC UA type and how to generate code and integrate it into a server. See UaModeler documentation > HowTo and Short Tutorials > HowTo for ANSI C.

If code is generated for a namespace, all dependent namespace must also be available as generated code.

UANodeSet binary import

Nodes can be loaded from UANodeSet binary files. This option can be used to load nodes that are not known at compile time of the server.

The ANSI C SDK has no dependency to an XML parser. Since it is designed for embedded use cases, there are typically not enough resources to handle the large and complex XML UANodeSet files. Therefore the ANSI C SDK uses an optimized binary UANodeSet format defined by Unified Automation for efficient loading of UANodeSet files on embedded systems. See OPC UA Binary File Format for a documentation of the file format.

The UANodeSet binary format is compatible to the XML format. The SDK contains the tool xml2bin for generating a binary file from XML UANodeSet file(s). See Binary UANodeSet Address Space File Generation for the options of the tool.

A XML UANodeSet file can be created with UaModeler. The XML export requires a license but the ANSI C SDK license contains a UaModeler license.

Nodes from depending namespace must be created before loading the binary UANodeSet file.

The following sample code can be used to load the nodes from a UANodeSet binary file into an existing provider using UaServer_FileImporterSimple.

extern UaServer_Provider* g_pDemoProvider;
UaBase_Settings settings;
char szBinaryAddressSpacePath[512] = "myBinaryNodeset.bin";
OpcUa_MemSet(&cb, 0, sizeof(cb));
cb.pfAddNamespaceCB = FileImport_Demo_AddNamespaceCB;
cb.pfAddExtensionNamespaceCB = FileImport_Demo_AddExtensionNamespaceCB;
cb.pfAddNodeCB = FileImport_Demo_AddNodeCB;
cb.pfReportStatCB = FileImport_Demo_ReportStatCB;
uStatus = UaServer_FileImporterSimple_Initialize(&fileImporter,
cb,
g_pDemoProvider,
UaServer_FileImporterSimple_LOADINGMODE_CREATE,
szBinaryAddressSpacePath);
OpcUa_GotoErrorIfBad(uStatus);
uStatus = UaServer_FileImporterSimple_LoadFile(&fileImporter, szBinaryAddressSpacePath);
OpcUa_GotoErrorIfBad(uStatus);
UaServer_FileImporterSimple_Clear(&fileImporter);

The following callback functions must be provided. They are used to add application specific processing during the import. Especially the AddNodeCB is typically of interest to process user extensions on nodes e.g. for variable nodes.

int FileImport_Demo_AddNamespaceCB(OpcUa_UInt16 nsidx, const char *szUri, UaServer_FileImporter_NodeStat *pStat, void *pUserData)
{
return 0;
}
int FileImport_Demo_AddExtensionNamespaceCB(OpcUa_UInt16 nsidx, const char *szUri, void *pUserData)
{
/* Handle extension namespaces */
return 0;
}
int FileImport_Demo_AddNodeCB(OpcUa_BaseNode *pNode, UaServer_FileImporter_BaseNode *pNodeInfo, void *pUserData)
{
/* Check if we have an extension for a variable node */
if (pNodeInfo->NodeClass == OpcUa_NodeClass_Variable &&
pNodeInfo->Extensions.NoOfExtensions > 0)
{
/* Handle extenision */
pNodeInfo->Extensions.Extensions[0].NamespaceIndex; /* Extension namespace */
pNodeInfo->Extensions.Extensions[0].Type; /* Extension type */
pNodeInfo->Extensions.Extensions[0].Body; /* Extension data */
}
return 0;
}
void FileImport_Demo_ReportStatCB(UaServer_FileImporter_StatisticType type, size_t size, void *pUserData)
{
}

Sample code for importing UANodeSet Binary can be found in the ANSI C SDK Demo Server in the function UaProvider_Demo_LoadBinaryAddressSpace. "BinaryAddressSpacePath" in the server config file can be set to import a binary file in the ANSI C SDK Demo Server.