UA ANSI C Server Professional  1.4.1.289
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
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 UaServer_Settings can be used to parse a configuration file and automatically fill the configuration structure. The UaServer_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 <uaserver_settings.h>
...
UaServer_Settings settings;
...
pServerConfiguration = UaServer_GetConfiguration( &g_UaServer );
uStatus = UaServer_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 */
UaServer_Settings_ReplaceUaString(&pServerConfiguration->ApplicationDescription.ApplicationName.Text, "[gethostname]", szHostname);
UaServer_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 );