tutorial_server_variables.rst 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. Adding nodes to a server and connecting nodes to user-defined values
  2. ====================================================================
  3. This tutorial shows how to add variable nodes to a server and how these can be connected to user-defined values and callbacks.
  4. Firstly, we need to introduce a concept of Variants. This is a data structure able to hold any datatype.
  5. Variants
  6. --------
  7. The datatype UA_Variant a belongs to the built-in datatypes of OPC UA and is used as a container type. A Variant can hold any other built-in scalar datatype (except Variants) or array built-in datatype (array of variants too). The variant is structured like this in open62541:
  8. .. code-block:: c
  9. typedef struct {
  10. const UA_DataType *type; ///< The nodeid of the datatype
  11. enum {
  12. UA_VARIANT_DATA, ///< The data is "owned" by this variant (copied and deleted together)
  13. UA_VARIANT_DATA_NODELETE, /**< The data is "borrowed" by the variant and shall not be
  14. deleted at the end of this variant's lifecycle. It is not
  15. possible to overwrite borrowed data due to concurrent access.
  16. Use a custom datasource with a mutex. */
  17. } storageType; ///< Shall the data be deleted together with the variant
  18. UA_Int32 arrayLength; ///< the number of elements in the data-pointer
  19. void *data; ///< points to the scalar or array data
  20. UA_Int32 arrayDimensionsSize; ///< the number of dimensions the data-array has
  21. UA_Int32 *arrayDimensions; ///< the length of each dimension of the data-array
  22. } UA_Variant;
  23. The members of the struct are
  24. * type: a pointer to the vtable entry. It points onto the functions which handles the stored data i.e. encode/decode etc.
  25. * storageType: used to declare who the owner of data is and to which lifecycle it belongs. Three different cases are possible:
  26. * UA_VARIANT_DATA: this is the simplest case. The data belongs to the variant, which means if the variant is deleted so does the data which is kept inside
  27. * UA_VARIANT_DATASOURCE: in this case user-defined functions are called to access the data. The signature of the functions is defined by UA_VariantDataSource structure. A use-case could be to access a sensor only when the data is asked by some client
  28. * arrayLength: length of the array (-1 if a scalar is saved)
  29. * data: raw pointer to the saved vale or callback
  30. * arrayDimensionsSize: size of arrayDimensions array
  31. * arrayDimensions: dimensinos array in case the array is interpreted as a multi-dimensional construction, e.g., [5,5] for a 5x5 matrix
  32. Adding a variable node to the server that contains a user-defined variable
  33. --------------------------------------------------------------------------
  34. This simple case allows to 'inject' a pre-defined variable into a variable node. The variable is wrapped by a "UA_Variant" before being insterted into the node.
  35. Consider 'examples/server_variable.c' in the repository. The examples are compiled if the Cmake option UA_BUILD_EXAMPLE is turned on.
  36. Adding a variable node to the server that contains a user-defined callback
  37. --------------------------------------------------------------------------
  38. The latter case allows to define callback functions that are executed on read or write of the node. In this case an "UA_DataSource" containing the respective callback pointer is intserted into the node.
  39. Consider 'examples/server_datasource.c' in the repository. The examples are compiled if the Cmake option UA_BUILD_EXAMPLE is turned on.