tutorial_server_variables.rst 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. .. role:: ccode(code)
  2. :language: c
  3. 2. Adding variables to a server
  4. ===============================
  5. This tutorial shows how to add variable nodes to a server and how these can be
  6. connected to a physical process in the background. Make sure to read the
  7. :ref:`introduction <introduction>` first.
  8. This is the code for a server with a single variable node holding an integer. We
  9. will take this example to explain some of the fundamental concepts of open62541.
  10. .. code-block:: c
  11. #include <signal.h>
  12. #include "open62541.h"
  13. UA_Boolean running = true;
  14. static void stopHandler(int sign) {
  15. running = false;
  16. }
  17. int main(int argc, char** argv) {
  18. signal(SIGINT, stopHandler); /* catch ctrl-c */
  19. UA_ServerConfig config = UA_ServerConfig_standard;
  20. UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
  21. config.networkLayers = &nl;
  22. config.networkLayersSize = 1;
  23. UA_Server *server = UA_Server_new(config);
  24. /* 1) Define the attribute of the myInteger variable node */
  25. UA_VariableAttributes attr;
  26. UA_VariableAttributes_init(&attr);
  27. UA_Int32 myInteger = 42;
  28. UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
  29. attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
  30. attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
  31. /* 2) Add the variable node to the information model */
  32. UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
  33. UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
  34. UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  35. UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
  36. UA_StatusCode retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
  37. parentReferenceNodeId, myIntegerName,
  38. UA_NODEID_NULL, attr, NULL, NULL);
  39. if(retval == UA_STATUSCODE_GOOD)
  40. UA_Server_run(server, &running);
  41. UA_Server_delete(server);
  42. nl.deleteMembers(&nl);
  43. return retval;
  44. }
  45. Variants and Datatypes
  46. ----------------------
  47. The datatype *variant* belongs to the built-in datatypes of OPC UA and is used
  48. as a container type. A variant can hold any other datatype as a scalar (except
  49. Variant) or as an array. Array variants can additionally denote the
  50. dimensionality of the data (e.g. a 2x3 matrix) in an additional integer array.
  51. You can find the code that defines the variant datatype :ref:`here <variant>`.
  52. The `UA_VariableAttributes` type contains a variant member `value`. The command
  53. :ccode:`UA_Variant_setScalar(&attr.value, &myInteger,
  54. &UA_TYPES[UA_TYPES_INT32])` sets the variant to point to the integer. Note that
  55. this does not make a copy of the integer (for which `UA_Variant_setScalarCopy`
  56. can be used). The variant (and its content) is then copied into the newly
  57. created node.
  58. Since it is a bit involved to set variants by hand, there are four basic
  59. functions you should be aware of:
  60. * **UA_Variant_setScalar** will set the contents of the variant to a pointer
  61. to the object that you pass with the call. Make sure to never deallocate
  62. that object while the variant exists!
  63. * **UA_Variant_setScalarCopy** will copy the object pointed to into a new
  64. object of the same type and attach that to the variant.
  65. * **UA_Variant_setArray** will set the contents of the variant to be an array
  66. and point to the exact pointer/object that you passed the call.
  67. * **UA_Variant_setArrayCopy** will create a copy of the array passed with the
  68. call.
  69. The equivalent code using allocations is as follows:
  70. .. code-block:: c
  71. UA_VariableAttributes attr;
  72. UA_VariableAttributes_init(&attr);
  73. UA_Int32 myInteger = 42;
  74. UA_Variant_setScalarCopy(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
  75. attr.description = UA_LOCALIZEDTEXT_ALLOC("en_US","the answer");
  76. attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en_US","the answer");
  77. /* add the variable node here */
  78. UA_VariableAttributes_deleteMembers(&attr); /* free the allocated memory */
  79. Finally, one needs to tell the server where to add the new variable in the
  80. information model. For that, we state the NodeId of the parent node and the
  81. (hierarchical) reference to the parent node.
  82. NodeIds
  83. -------
  84. A node ID is a unique identifier in server's context. It is composed of two members:
  85. +-------------+-----------------+---------------------------+
  86. | Type | Name | Notes |
  87. +=============+=================+===========================+
  88. | UInt16 | namespaceIndex | Number of the namespace |
  89. +-------------+-----------------+---------------------------+
  90. | Union | identifier | One idenifier of the |
  91. | | * String | listed types |
  92. | | * Integer | |
  93. | | * GUID | |
  94. | | * ByteString | |
  95. +-------------+-----------------+---------------------------+
  96. The first parameter is the number of node's namespace, the second one may be a
  97. numeric, a string or a GUID (Globally Unique ID) identifier. The following are
  98. some examples for their usage.
  99. .. code-block:: c
  100. UA_NodeId id1 = UA_NODEID_NUMERIC(1, 1234);
  101. UA_NodeId id2 = UA_NODEID_STRING(1, "testid"); /* points to the static string */
  102. UA_NodeId id3 = UA_NODEID_STRING_ALLOC(1, "testid");
  103. UA_NodeId_deleteMembers(&id3); /* free the allocated string */
  104. Adding a variable node to the server that contains a user-defined callback
  105. --------------------------------------------------------------------------
  106. The latter case allows to define callback functions that are executed on read or
  107. write of the node. In this case an "UA_DataSource" containing the respective
  108. callback pointer is intserted into the node.
  109. Consider ``examples/server_datasource.c`` in the repository. The examples are
  110. compiled if the Cmake option UA_BUILD_EXAMPLE is turned on.
  111. Asserting success/failure
  112. -------------------------
  113. Almost all functions of the open62541 API will return a `StatusCode` 32-bit
  114. integer. The actual statuscodes are defined :ref:`here <statuscodes>`. Normally,
  115. the functions should return `UA_STATUSCODE_GOOD`, which maps to the zero
  116. integer.