High Performance OPC UA Server SDK
1.4.2.279
|
This example shows how to create an address space from a binary address space file containing some types and how to create instances from these types at runtime.
Therefor we created a simple information model using UaModeler and exported this model as XML file. This XML file is then converted into a binary file using the provided xml2bin
tool.
The model contains an instance of FolderType
called Sensors
below Objects
which will be the root entry point of all our sensor instances. Below BaseObjectType
we created a type hierarchy for our sensors consisting of an abtract BaseSensorType
and a TemperatureSensorType
. The TemperatureSensorType
contains a process value named CurrentTemperature
which is an instance of AnalogItemType
. The meta information EngineeringUnits
and EURange
is defined already in the type and all instances will automatically get the same values. For this reason the AccessLevel
is defined ReadOnly
. This is a hint for the ua_object_create_instance function which will reuse the same value for all instances if it is ReadOnly
. The value of CurrentTemperature
will be connected to a custom value store in our code.
The following image shows how this model looks like in UaModeler:
When the example is compiled and running we can connect with UaExpert. As you can see in the following screenshot the address space contains 10 instances of the TemperatureSensorType
. On the right side in the attributes window you can see the unit of the select EngineeringUnits
property of one sensor instance. In the data access view in the center of the screen you can see subscribed temperature values with values provided by the custom value store.
The following table describes the files of this example.
File | Description |
---|---|
sensormodel.tt2pro | UaModeler project. |
sensormodel.ua | UaModeler binary information model. |
sensormodel.xml | The sensormodel.ua exported as XML. |
sensormodel.bin | Binary address space file generated with xml2bin . |
gen.sh | Helper script to generate sensormodel.bin from sensormodel.xml |
server_main.c | Main application |
provider_sensor.* | The sensor provider implementation which loads the sensormodel.bin file. |
provider_sensor_store.* | The custom store for sensor values. |
sensordemo_indentifiers.h | Contains some defines for numeric nodeids for the demo model. |
Loading the binary address space file is simply done by calling ua_addressspace_load_file. Normally the allocated memory pools are exactly as big as required for the model that gets loaded. Because we know that we want to add additional nodes to this namespace dynamically we pass this additional memory as the last parameter to ua_addressspace_load_file. Therefor we create the variable config
which contains the additional memory requirements.
The following code fragment is part of provider_sensor_init()
in provider_sensor.c
:
Configuring Memory of additional dynamic nodes (for create_instances):
Loading the binary file:
We can also statically link the address space instead of loading the binary file into RAM. But then we cannot create instances anymore in this address space. Instead we create another dynamic address space for the instances.
Loading the statically linked address space.
Creating a 3rd dynamic address space for the instances.
Note that we concatenated the generated string constants SENSOR_MODEL_NAMESPACE_URI
with "Instances", which results in the URI http://www.unifiedautomation.com/SensorModel/Instances
.
Creating the instances is done in function provider_sensor_create_instance
which is also called from provider_sensor_init
after the address space was successfully loaded.
Therefor we retrieve the node handles of the required nodes by there well-known numeric nodeid. Then we create a string nodeid dynamically for our new instance and call ua_object_create_instance. The whole process of creating a node is done in a for loop. The number of instances can be configured using the define NUM_SENSOR_INSTANCES
.
Connecting the variables with a value store is simply done by setting the store id and the value index by using ua_variable_set_store_index and ua_variable_set_value_index.
The complicated part in this example is that we don't know the nodeids if our CurrentTemperature
variables. These nodes will be created dynamically by ua_object_create_instance. But we know the nodeids of the sensor instances, that we created ourself in provider_sensor_create_instance
, and we know the BrowseName
of the CurrentTemperature
variables. So we iterate over all references of the sensor instance and compare the browsename of the referenced nodes. When the CurrentTemperature
is found we set the store information and call break
to exit the foreach
loop.
The store getter function simply returns a double value that was created as a global double
array.