ANSI C Based OPC UA Client/Server/PubSub SDK  1.9.3.467
How to Create a New Data Provider

There are two main development tasks to implement an OPC UA Server using the SDK. One is to integrate the SDK and the UA Server into a new or an existing application. The second task is to integrate the data sources into the SDK. This tutorial gives an introduction on how to integrate data sources into the ANSI C UA SDK by implementing a data provider. It doesn’t cover everything. The emphasis is on teaching how to quickly create a new Data Provider with new address space and new IO data.

Please read the Introduction section before working through this tutorial.

Note
We highly recommend using UaModeler for generating a provider instead of creating it manually, as it will contain all files with method stubs needed and matching your version of the SDK. It also contains a CMakeLists.txt file, which allows generating project or make files depending on your needs.

Creating a New Provider

If you want to create your first data provider it’s the easiest way to just copy the demo provider and rename the files or to generate a provider using the UaModeler. A basic provider should contain these files:

FileDescription
uaprovider_xyz.hContains provider function definitions. Normally only UaProvider_XYZ_Initialize for initializing your provider.
uaprovider_xyz.cImplements the provider initialization and cleanup.
xyz_read.cImplements the read service of the provider interface.
xyz_write.cImplements the write service of the provider interface.
xyz_subscription.cImplements the subscription functionality described in Provider Subscription Mechanism
uaserver_helper.hContains global variable declarations.

Implementing UaProvider_XYZ_Initialize

Every provider has to implement an Initialize function with the following signature. This is the main entry point to set up all other functions a provider may provide.

IFMETHODIMP(UaProvider_XYZ_Initialize)(
UaServer_Provider *a_pProvider,
UaServer_pProviderInterface *a_pProviderInterface)
{
OpcUa_StatusCode uStatus = OpcUa_Good;
// TODO
return uStatus;
}

This function is called when loading the provider and has the following purposes:

  • The server passes the provider context to provider.
  • The provider must fill the provider interface with its own functions pointers so that the server is able to call provider functions.
  • The provider can do some initialization work like adding new nodes to the address space or setting up the underlying communication.

Implementing UaProvider_XYZ_Cleanup

This is called from the Server SDK before the provider is unloaded to give the provider the chance to clean up its resources.

IFMETHODIMP(UaProvider_XYZ_Cleanup)()
{
OpcUa_StatusCode uStatus = OpcUa_Good;
// TODO
return uStatus;
}

Dynamically Linking a Provider

Providers can also be compiled as Dynamic Link Libraries (aka shared objects). In this case you have to provide an exported DLL function InitializeProvider which calls the UaProvider_XYZ_Initialize function. The defines UAPROVIDER_P_EXPORT and UAPROVIDER_P_IMPORT provided by the SDK can be used to declare a XYZPROVIDER_API macro, depending on how the header is included.

All other functions will be accessed through the provider interface and do not need to be exported. You can use the define BUILD_SHARED_LIBS to check if providers are linked statically or dynamically. See Startup Code for UA Stack and UA Server to see how to load dynamic and static providers.

#if defined(_UA_XYZPROVIDER_BUILD_DLL)
# define XYZPROVIDER_API(rtype) UAPROVIDER_P_EXPORT(rtype)
#else
# define XYZPROVIDER_API(rtype) UAPROVIDER_P_IMPORT(rtype)
#endif
OPCUA_BEGIN_EXTERN_C
#ifdef BUILD_SHARED_LIBS
XYZPROVIDER_API(OpcUa_StatusCode) InitializeProvider(
UaServer_Provider* pProvider,
UaServer_pProviderInterface* pProviderInterface);
#endif
OPCUA_END_EXTERN_C