ANSI C Based OPC UA Client/Server SDK  1.8.0.369
Migration Guides

Migration from V1.6.0 to V1.7

This guide contains instructions for users of the Unified Automation ANSI C SDK V1.6.0 on migrating existing code to the new SDK version 1.7.

  • UaServer_GetUserIdentity has to be replaced with UaServer_GetUserId and the password out parameter has to be removed.
  • UaBase_P_InitShutdownFlag and UaBase_P_ClearShutdownFlag have to be replaced with UaBase_P_RegisterShutdownHandler and UaBase_P_UnregisterShutdownHandler.
  • There is no need anymore to check for shutdown keystrokes/signals in server applications. If UaBase_P_RegisterShutdownHandler was called, UaBase_DoCom will return OpcUa_BadShutdown or OpcUa_BadContinue, depending on the shutdown keystroke/signal. The delay can be configured with UaServer_SetShutdownDelay. See the file examples/server_c_demo/demoserver.c for an example.

Migration from V1.5.3 to V1.6

This guide contains instructions for users of the Unified Automation ANSI C SDK V1.5.3 on migrating existing code to the new SDK version 1.6.

  • OpcUa_Method_SetUserExecutable is only available if UASERVER_SUPPORT_AUTHORIZATION is disabled and has to be enclosed as follows:
    #if UASERVER_SUPPORT_AUTHORIZATION == OPCUA_CONFIG_NO
    OpcUa_Method_SetUserExecutable(...);
    #endif
  • UaServer_(Timed)DoCom and UaClient_(Timed)DoCom have to be replaced with UaBase_(Timed)DoCom
  • UaBase_PkiCertificate_Create has a new parameter a_signatureAlgorithm which has to be complemented

Migration from V1.4.2 to V1.5

This guide contains instructions for users of the Unified Automation ANSI C SDK V1.4.2 on migrating existing code to the new SDK version 1.5.

CMake

The CMake files of the SDK have been reworked completely and new CMake modules have been added to the folder /sdk/cmake. To migrate an old CMake file to our new structure, we recommend using /sdk/examples/server_c_demo/CMakeLists.txt as an example on how to rewrite existing CMake files.

The most important changes are:

  • CMAKE_MODULE_PATH should be set to the folder cmake instead of uasdk_cmake
  • The line
    FIND_PACKAGE(UASDK REQUIRED)
    should be replaced with
    include(FindUaOpenSSL)
    include(ConfigureCompiler)
    include(ConfigureUaStack)
    include(ConfigureAnsiCSdk)
  • All checks for BUILD_UASTACK_ENC_OBJ should be removed; the option has been renamed and will always be enabled by default.
  • There are unified include dir variables defined in /sdk/cmake/ConfigureUaStack.cmake and /sdk/cmake/ConfigureAnsiCSdk.cmake that should be used with include_directories() instead of including the SDK include folders separately (e.g. UASERVERC_INCLUDE).
  • There are unified link library variables defined in /sdk/cmake/ConfigureUaStack.cmake and /sdk/cmake/ConfigureAnsiCSdk.cmake that should be used with target_link_libraries() instead of linking the SDK libraries otherwise (e.g. UASERVERC_LIBRARY).
  • All common utility structures and functions have been moved to the new SDK library uabasec. All applications using the SDK have to link against it (UABASEC_LIBRARY) and include the corresponding include folder (UABASEC_INCLUDE).

DataLogger

The function UaServer_DataLogger_ReadValues was modified to take an additional parameter TimeoutHint.

Provider

All common utility structures and functions were renamed to start with UaBase_ instead of UaServer_. Existing code should be adapted accordingly. For a faster migration, the CMake option UABASE_USE_COMPAT_HEADERS can be enabled, which adds headers containing compatibility defines for all renamed structures and functions, so old code will work out of the box. It is recommended to use this option only temporarily and modify the code instead to match the new names.

Migration from V1.3.3 to V1.4

This guide contains instructions for users of the Unified Automation ANSI C SDK V1.3.3 on migrating existing code to the new SDK version 1.4.

Provider

Initialization Function UaProvider_XYZ_Initialize

UaServer_pProviderCBInterface has been removed for simplicity, simply remove it from your init function’s parameter list. Also, remove the g_pXYZProviderCBInterface forward declaration from your uaprovider_XYZ_helper.h file.

In some places in old providers, the provider interface was used directly without using shortcut macros. These have to be replaced with the according SDK interface functions, e.g.:

OLD CODE: a_pProviderCBInterface->RegisterAddressSpace()

The replacements can be done with a simple search and replace, replacing “g_pXYZProviderCBInterface->” with “UaServer_”.

No Shortcut Macros Needed Any More

As the UaServer_pProviderCBInterface has been removed, all shortcut macros in your uaprovider_XYZ_helper.h file can be removed (all “UaServer_...” macros). In order to avoid replacing function names in your existing provider code, you may want to add the following macros to your uaprovider_XYZ_helper.h file:

#define UaServer_Alloc(a) OpcUa_Alloc(a)
#define UaServer_Realloc(a, b) OpcUa_Realloc(a, b)
#define UaServer_Free(a) OpcUa_Free(a)
#define UaServer_VariantCompare(a_vA, a_vB) OpcUa_Variant_Compare(a_vA, a_vB)
#define UaServer_QualifiedNameCompare(a_qnA, a_qnB) OpcUa_QualifiedName_Compare(a_qnA, a_qnB)
#define UaServer_NodeIdClone(a_pValue, a_pCopy) OpcUa_NodeId_Clone(a_pValue, a_pCopy)
#define UaServer_QualifiedNameClone(a_pValue, a_pCopy) OpcUa_QualifiedName_Clone(a_pValue, a_pCopy)
#define UaServer_LocalizedTextClone(a_pValue, a_pCopy) OpcUa_LocalizedText_Clone(a_pValue, a_pCopy)
#define UaServer_VariantClone(a_pValue, a_pCopy) OpcUa_Variant_Clone(a_pValue, a_pCopy)
#define UaServer_BaseNodeDefaultValuesGet() OpcUa_BaseNode_DefaultValues_Get()

It is recommended to replace these functions in the provider code instead, e.g. replace UaServer_Alloc with OpcUa_Alloc. The parameter order has not been changed, so a simple search & replace will work.

New Include Headers Needed

The SDK’s interface functions that are used by providers have been moved to the according files. Hence, it is necessary to add new includes to your provider code. You can simply add the following includes to your uaprovider_XYZ_helper.h file to have all needed includes:

#include <uaserver_addressspace.h>
#include <uaserver_read.h>
#include <uaserver_write.h>
#include <uaserver_browse.h>
#include <uaserver_translate.h>
#include <uaserver_register.h>
#include <uaserver_monitoring.h>
#include <uaserver_monitoreditemmanager.h>
#if UASERVER_SERVICES_HISTORYREAD
#include <uaserver_historyread.h>
#endif /* UASERVER_SERVICES_HISTORYREAD */
#if UASERVER_SERVICES_CALL
#include <uaserver_call.h>
#endif /* UASERVER_SERVICES_CALL */

Another possibility is to add the includes to the according provider files, e.g. to add “::include <uaserver_write.h>” to the uaprovider_XYZ_write.c file.

Deleted Define NUM_SAMPLINGRATES

The define NUM_SAMPLINGRATES has been removed from the uaserver_config.h header, as each provider is responsible for its sampling rates. For existing providers you only need to add the following line to the file uaprovider_XYZ_subscription.c before the first use of the define:

#define NUM_SAMPLINGRATES 7

New TranslateBrowsePathsToNodeIds Interface

It was necessary to radically change the TranslateBrowsePathsToNodeIds interface of the SDK to allow real asynchronous processing of translate calls. This makes it necessary to rewrite your provider’s TranslateAsync function. If you didn’t change the generic implementation of TranslateAsync in your provider, you can simply replace it with the following code, you will only need to replace the two occurrences of “XYZ” with your provider name:

IFMETHODIMP(UaProvider_XYZ_TranslateAsync)(UaServer_ProviderTranslateContext* a_pTranslateCtx)
{
OpcUa_UInt32 iNoOfStartingNodes, i;
OpcUa_NodeId *pStartingNode = OpcUa_Null;
UaServer_AddressSpace *pNodeAddressSpace = OpcUa_Null;
UaServer_Atomic_Increment(&a_pTranslateCtx->nOutstandingCbs);
iNoOfStartingNodes = UaServer_Vector_GetSize(&a_pTranslateCtx->StartingNodes);
for (i = 0; i < iNoOfStartingNodes; i++)
{
pStartingNode = UaServer_Vector_Get(&a_pTranslateCtx->StartingNodes, i);
UaServer_AddressSpace_Get(pStartingNode->NamespaceIndex, &pNodeAddressSpace);
if (pNodeAddressSpace == &g_pXYZProvider->AddressSpace)
{
UaServer_TranslateInternal(a_pTranslateCtx, i);
}
}
/* send callback */
UaServer_TranslateComplete(a_pTranslateCtx);
return OpcUa_Good;
}

New Argument in UaServer_GetNonValueAttribute

In order to return user specific access rights correctly, an additional argument of type UaServer_PublicSession had to be added to the function UaServer_GetNonValueAttribute.

The new parameter has to be added to all invocations of this function which is used by providers in their 'uaprovider_xyz_subscription.c' file. Getting the session of a monitored item can be achieved by the new function UaServer_Subscription_GetSession :

/* get value of the attribute */
if (OpcUa_IsGood(uStatus))
{
uStatus = UaServer_GetNonValueAttribute(pMonitoredItemData->AttributeId,
pNode,
&currentValue,
UaServer_Subscription_GetSession(pMonitoredItemData->pSubscription));
}

Optional: Building the Provider as Shared Library

If you want to build and use the provider as shared library, it needs to implement a generic provider initialization function for the SDK to be able to load the provider dynamically. For this purpose, add the following code to your provider, replacing “XYZ” with your provider’s name:

Add the following code to your uaprovider_XYZ.h:

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

Add the following code into your uaprovider_XYZ.c:

#if BUILD_SHARED_LIBS
XYZPROVIDER_API(OpcUa_StatusCode) InitializeProvider(UaServer_Provider* pProvider,
UaServer_pProviderInterface* pProviderInterface)
{
return UaProvider_XYZ_Initialize(pProvider, pProviderInterface);
}
#endif

Now, when building the provider, the preprocessor defines BUILD_SHARED_LIBS _UA_XYZPROVIDER_BUILD_DLL have to be set, this leads to the InitializeProvider function to be exported by the linker and allows the SDK to load the provider dynamically.

Application

Simplified Function UaServer_Initialize

The server initialization function UaServer_Initialize has been simplified and now only takes the global server object as parameter. All settings previously passed to UaServer_Initialize are now configured using the server’s configuration structure retrieved by UaServer_GetConfiguration.

For convenience, the new utility structure UaBase_Settings can be used to parse a configuration file and automatically fill the configuration structure. The UaBase_Settings module can be modified to use a custom implementation if no configuration file shall be used (e.g. hard-coded values, database etc.).

Use following code to open the settings file “settings.ini” and to fill the server configuration automatically using the function UaServer_Settings_GetConfigurationFromSettings :

#include <uabase_settings.h>
...
UaBase_Settings settings;
...
pServerConfiguration = UaServer_GetConfiguration( &g_UaServer );
uStatus = UaBase_Settings_Initialize(&settings, "settings.ini");
OpcUa_GotoErrorIfBad( uStatus );
uStatus = UaServer_Settings_GetConfigurationFromSettings(&settings, pServerConfiguration, szHostname);
OpcUa_GotoErrorIfBad( uStatus );

The following settings contain build information and so should always be hardcoded and not read from a settings file.

/* ApplicationDescription */
OpcUa_String_AttachReadOnly(&pServerConfiguration->ApplicationDescription.ApplicationUri, UASERVER_APPLICATION_URI);
OpcUa_String_AttachReadOnly(&pServerConfiguration->ApplicationDescription.ProductUri, UASERVER_PRODUCT_URI);
OpcUa_String_AttachReadOnly(&pServerConfiguration->ApplicationDescription.ApplicationName.Text, UASERVER_APPLICATION_NAME);
pServerConfiguration->ApplicationDescription.ApplicationType = OpcUa_ApplicationType_Server;
/* DiscoveryUrls are filled in UaServer_Settings_GetConfigurationFromSettings using all endpoint URLs */
/* Replace [gethostname] in ApplicationName and ApplicationUri */
UaBase_Settings_ReplaceUaString(&pServerConfiguration->ApplicationDescription.ApplicationName.Text, "[gethostname]", szHostname);
UaBase_Settings_ReplaceUaString(&pServerConfiguration->ApplicationDescription.ApplicationUri, "[gethostname]", szHostname);
/* BuildInfo */
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.ProductUri, UASERVER_PRODUCT_URI);
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.ManufacturerName, UASERVER_MANUFACTURER_NAME);
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.ProductName, UASERVER_PRODUCT_NAME);
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.SoftwareVersion, UASDK_VERSION);
OpcUa_String_AttachReadOnly(&pServerConfiguration->BuildInfo.BuildNumber, UASERVER_BUILD_NUMBER);
OpcUa_DateTime_GetDateTimeFromString(UASERVER_BUILD_DATE, &pServerConfiguration->BuildInfo.BuildDate);

Further information is available in the How to Integrate the OPC UA Server SDK into Your Application, the Getting Started Lessons and in the ANSI C SDK Demo Server delivered with the SDK.

Removed Define UAPROVIDER_STATIC

The hard-coded define UAPROVIDER_STATIC was removed from the uaserver_config.h file. Instead the macro BUILD_SHARED_LIBS is defined when the CMake switch with the same name is enabled. Enabling BUILD_SHARED_LIBS in CMake configures the build system to build all libraries as shared libraries. When BUILD_SHARED_LIBS is disabled all libraries are built as static libraries and the macro BUILD_SHARED_LIBS is not defined. Note, that the UaStack still has its own switch BUILD_SHARED_STACK, so that you can build the UaStack as a shared library while the rest of the SDK is built as static libraries.

Hence, when loading a provider the new define BUILD_SHARED_LIBS has to be used to determine if the provider should be loaded dynamically or static:

/* Add XYZ provider */
memset( &Provider, 0, sizeof( Provider ) );
#ifndef BUILD_SHARED_LIBS
Provider.pfInit = UaProvider_XYZ_Initialize;
#else
Provider.sProviderLibrary = "XYZprovider";
#endif
UaServer_ProviderList_AddProvider( &g_UaServer, &Provider );