UA Server SDK C++ Bundle
1.4.3.291
|
The C++ SDK Demo Server is a collection of examples for developing different features of an OPC UA server. It is mainly composed of the final results of the Server Getting Started lessons with instance nodes in the BuildingAutomation folder and the Unified Automation Demo address space with nodes in the Demo folder.
The complete project can be found in Examples → Server Cpp Demo.
There are several steps necessary to implement user authentication and user authorization. The provided sample code for user authentication in the first three steps provides a lot of reusable code. User authorization shown in the rest of the example is very application specific and the example shows one simplified option.
The example for user authentication is applied to the Unified Automation Demo address space. Depending on the user, access to nodes in Objects → Demo → 005_AccessRights is limited. The server has the three users John, Operator and Admin. The password equals user name. John and Operator are members of the Operators group. Admin is member of the Administrators group.
The example is composed of the following steps:
On the toolkit layer used in the example there are different methods you can overwrite or callbacks you can implement to influence the behaviour based on the user context. These overwrites are implemented in the class NodeManagerImpl (see files demo_nodemanagerimpl.h and demo_nodemanagerimpl.cpp).
The following methods overwrite default functionality in the class NodeManagerImpl
An example on how to update variable values is already shown in the building automation server introduced in the Server Getting Started Tutorials. This section describes a different approach.
In this tutorial, the variable value handling is set to UaVariable_Value_Cache to indicate that the value needs to be polled through readValues() if a client is interested in the latest value. The method readValues() is called by the SDK for a Read service call and value change checks for monitored items.
If a server should decide whether a value is checked for changes, the value handling of these variables can be set to UaVariable_Value_CacheIsSource | UaVariable_Value_CacheIsUpdatedOnRequest. In this case, the implementer is informed about changes in monitoring and can implement his or her own logic for checking currently monitored variables for changes. The methods readValues() and writeValues() are still called for Read and Write service calls from clients.
The following code snippets show the differences to the tutorial code and can be found in the following files
Alterations are marked by comments in the following form:
Replace the following line in the file controllerobject.cpp
by this code snippet:
The most important change in nmbuildingautomation.h is to overwrite IOManagerUaNode::variableCacheMonitoringChanged(). This method informs the derived class that the monitoring for a variable has been changed.
In addition, the class is derived from UaThread to implement the sampling in a background worker thread. The other additions, like the method run() or the member variables, are necessary to sample the variables which are active in monitoring.
The new members are initialized in the constructor of the class NmBuildingautomation():
Start the sampling worker thread in NmBuildingautomation::afterStartUp():
Stop the sampling worker thread in NmBuildingAutomation::beforeShutDown():
To configure the sampling in the worker thread we have to implement variableCacheMonitoringChanged(). Based on the UaVariableCache::signalCount() the variable is added to sampling when the first monitored item is created, and removed from sampling when the last monitored item is removed. UaVariableCache::getMinSamplingInterval() returns the shortest sampling interval currently used for the variable. This information is not used in this example but it may be used if variables can be sampled with different rates.
The internal sampling is implemented in the main method of the worker thread:
The demo server contains an example for loading all or part of the address space from a UANodeSet XML file.
The sample code can be found in
The example XML file is located in the directory “bin” of the demo server executable. The file buildingautomationxml.xml contains the same controller objects as the building automation server introduced in the Server Getting Started Tutorials. Some of the sample code to connect the loaded variables to the controller simulation is similar to the code used in the tutorials.
The parser and the necessary classes are initialized and added with the following code in the file servermain.cpp.
The class MyBaseNodeFactory is derived from UaBase::BaseNodeFactory and overwrites the method UaBase::BaseNodeFactory::createVariable(). The factory creates data classes for the OPC UA nodes in the XML file. These data classes are used in a second step to create the nodes in the NodeManager. Overwriting the factory allows the creation of specialized data classes. In the example, the data class MyVariable derived from UaBase::Variable is used to parse the extension in the XML file that contains the user data for the variable.
The class MyNodeManagerNodeSetXmlCreator is used to create specialized NodeManagers for a known namespace in the XML file. In this example the special NodeManager is implemented in the class MyNodeManagerNodeSetXml. This class implements all logic necessary to initialize the controller objects and to implement data access to variable values and methods as well as event and alarm handling.
The demo address space code contains sample code for the handling of alarm objects of different types. The example includes alarm objects which show up as OPC UA nodes in the address space as well as alarm objects not visible in the address space. The latter are working completely without nodes in the address space.
The sample code is contained in the class NodeManagerDemo which can be found in the files
The alarm objects are created and initialized in the method createAlarmNodes(). The objects visible in the address space are also added to the NodeManager. These objects are deleted by the NodeManager at shutdown. In addition this method creates variables used to trigger the alarm states.
The alarm objects without nodes have to be deleted in the destructor of the NodeManagerDemo.
The state of the alarm objects can be activated by writing “true” to the OffNormalAlarm variables or by assigning analog values for the level alarms ranging from 0 to 100. Values below 30 trigger a low alarm. Values above 70 trigger a high alarm. To modify the alarm states on write, the method IOManagerUaNode::afterSetAttributeValue() is overwritten in NodeManagerDemo. This method contains the sample code for changing the alarm states.
To handle alarm acknowledgement the methods EventManagerUaNode::OnAcknowledge and EventManagerUaNode::OnConfirm are overwritten in the class NodeManagerDemo. This works only for alarm objects visible in the address space. To handle the Acknowledge and AddComment methods for alarm objects without nodes, the methods getMethodHandle() and beginCall() are implemented to provide the MethodManager functionality for these alarm objects.
The demo address space code contains sample code for the handling of structured data types. A variable with a nested structure can be found in Objects → BuildingAutomation → ControllerConfigurations. This variable contains a structure with two arrays of structures with the configuration parameters for all controllers.
The sample code can be found in the file nmbuildingautomation.cpp
The enumeration, structured data type nodes, and dictionary are created in the function NmBuildingAutomation::createTypeNodes().
The structure value is filled up in the function NmBuildingAutomation::readValues().
The demo address space code contains sample code for the handling of historical access for events. The area object providing event history can be found in Objects → Server → AreaAirConditioner. This object event notifier attribute indicates availablity of event history.
The sample code can be found in the files
The internal event monitored item is created in the function NmBuildingAutomation::afterStartUp().
The class HistoryManagerCache implements all data and event history functions and data and event historizing. The events and data changes are monitored with internal monitored items and are stored in memory.
The sample code expects that the nodes providing event history are managed by the NodeManager that also manages the HistoryManager. This is necessary to forward HistoryRead requests for a node to the right HistoryManager.
If a node is managed by another NodeManager, the responsible HistoryManager must be registered for the a UaNode or for the whole NodeManager. The class NodeManagerBase provides two options. The first is to use NodeManagerBase::setHistoryManager() to set the default HistoryManager for all UaNodes in the NodeManager. The second option is to set a HistoryManager for single UaNodes using the methods NodeManagerBase::setHistoryManagerForUaNode() and NodeManagerBase::removeHistoryManagerForUaNode().
The following sample code shows how to handle event history for the Server object managed by NodeManagerRoot.
The demo address space code contains sample code for dynamic creation of nodes including model change event. The folder with the functionality can be found in Objects → Demo → 008_DynamicNodes. This folder contains two methods for node creation and deletion, a NodeVersion property and the dynamic node. Only nodes with a NodeVersion property are allowed to fire specific model change events of type GeneralModelChangeEventType. This event indicates the changed nodes with a NodeVersion property and the changes (node added/deleted, reference added/deleted). Since the new node does not have a NodeVersion property, only the added reference from the node (008_DynamicNodes) with NodeVersion property has a new reference contained in the model change event.
Changes of nodes without NodeVersion property can be indicated by using the BaseModelChangeEvent. But this event does not indicate which part of the address space has changed.
The sample code can be found in the files
During start-up of the server the GeneralModelChangeEventType must be created in the event type hierarchy. This can be done with the following code contained in NodeManagerDemo::afterStartUp().
The full sample code for the generation of the model change event can be found in the methods NodeManagerDemo::Demo_DynamicNodes_CreateDynamicNode() and NodeManagerDemo::Demo_DynamicNodes_DeleteDynamicNode(). The following code is the essential part of the event generation.
The sample code creates only one change. If more than one node with a NodeVersion property is affected, all changes should be combined in one event.