tutorial_server_datasource.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  2. * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
  3. /**
  4. * Connecting a Variable with a Physical Process
  5. * ---------------------------------------------
  6. *
  7. * In OPC UA-based architectures, servers are typically situated near the source
  8. * of information. In an industrial context, this translates into servers being
  9. * near the physical process and clients consuming the data at runtime. In the
  10. * previous tutorial, we saw how to add variables to an OPC UA information
  11. * model. This tutorial shows how to connect a variable to runtime information,
  12. * for example from measurements of a physical process. For simplicty, we take
  13. * the system clock as the underlying "process".
  14. *
  15. * The following code snippets are each concerned with a different way of
  16. * updating variable values at runtime. Taken together, the code snippets define
  17. * a compilable source file.
  18. *
  19. * Updating variables manually
  20. * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  21. * As a starting point, assume that a variable for a value of type
  22. * :ref:`datetime` has been created in the server with the identifier
  23. * "ns=1,s=current-time". Assuming that our applications gets triggered when a
  24. * new value arrives from the underlying process, we can just write into the
  25. * variable. */
  26. #include <signal.h>
  27. #include "open62541.h"
  28. static void
  29. updateCurrentTime(UA_Server *server) {
  30. UA_DateTime now = UA_DateTime_now();
  31. UA_Variant value;
  32. UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  33. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
  34. UA_Server_writeValue(server, currentNodeId, value);
  35. }
  36. static void
  37. addCurrentTimeVariable(UA_Server *server) {
  38. UA_DateTime now = 0;
  39. UA_VariableAttributes attr;
  40. UA_VariableAttributes_init(&attr);
  41. attr.displayName = UA_LOCALIZEDTEXT("en_US", "Current time");
  42. UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  43. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
  44. UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time");
  45. UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  46. UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
  47. UA_NodeId variableTypeNodeId = UA_NODEID_NULL;
  48. UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
  49. parentReferenceNodeId, currentName,
  50. variableTypeNodeId, attr, NULL, NULL);
  51. updateCurrentTime(server);
  52. }
  53. /**
  54. * Variable Value Callback
  55. * ^^^^^^^^^^^^^^^^^^^^^^^
  56. *
  57. * When a value changes continuously, such as the system time, updating the
  58. * value in a tight loop would take up a lot of resources. Value callbacks allow
  59. * to synchronize a variable value with an external representation. They attach
  60. * callbacks to the variable that are executed before every read and after every
  61. * write operation. */
  62. static void
  63. beforeReadTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
  64. const UA_NumericRange *range) {
  65. UA_Server *server = (UA_Server*)handle;
  66. UA_DateTime now = UA_DateTime_now();
  67. UA_Variant value;
  68. UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  69. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
  70. UA_Server_writeValue(server, currentNodeId, value);
  71. }
  72. static void
  73. afterWriteTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
  74. const UA_NumericRange *range) {
  75. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  76. "The variable was updated");
  77. }
  78. static void
  79. addValueCallbackToCurrentTimeVariable(UA_Server *server) {
  80. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
  81. UA_ValueCallback callback ;
  82. callback.handle = server;
  83. callback.onRead = beforeReadTime;
  84. callback.onWrite = afterWriteTime;
  85. UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
  86. }
  87. /**
  88. * Variable Data Sources
  89. * ^^^^^^^^^^^^^^^^^^^^^
  90. *
  91. * With value callbacks, the value is still stored in the variable node.
  92. * So-called data sources go one step further. The server redirects every read
  93. * and write request to a callback function. Upon reading, the callback provides
  94. * copy of the current value. Internally, the data source needs to implement its
  95. * own memory management. */
  96. static UA_StatusCode
  97. readCurrentTime(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
  98. const UA_NumericRange *range, UA_DataValue *dataValue) {
  99. UA_DateTime now = UA_DateTime_now();
  100. UA_Variant_setScalarCopy(&dataValue->value, &now,
  101. &UA_TYPES[UA_TYPES_DATETIME]);
  102. dataValue->hasValue = true;
  103. return UA_STATUSCODE_GOOD;
  104. }
  105. static UA_StatusCode
  106. writeCurrentTime(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
  107. const UA_NumericRange *range) {
  108. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  109. "Changing the system time is not implemented");
  110. return UA_STATUSCODE_BADINTERNALERROR;
  111. }
  112. static void
  113. addCurrentTimeDataSourceVariable(UA_Server *server) {
  114. UA_VariableAttributes attr;
  115. UA_VariableAttributes_init(&attr);
  116. attr.displayName = UA_LOCALIZEDTEXT("en_US", "Current time - data source");
  117. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-datasource");
  118. UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
  119. UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  120. UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
  121. UA_NodeId variableTypeNodeId = UA_NODEID_NULL;
  122. UA_DataSource timeDataSource;
  123. timeDataSource.handle = NULL;
  124. timeDataSource.read = readCurrentTime;
  125. timeDataSource.write = writeCurrentTime;
  126. UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
  127. parentReferenceNodeId, currentName,
  128. variableTypeNodeId, attr,
  129. timeDataSource, NULL);
  130. }
  131. /** It follows the main server code, making use of the above definitions. */
  132. UA_Boolean running = true;
  133. static void stopHandler(int sign) {
  134. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
  135. running = false;
  136. }
  137. int main(void) {
  138. signal(SIGINT, stopHandler);
  139. signal(SIGTERM, stopHandler);
  140. UA_ServerConfig config = UA_ServerConfig_standard;
  141. UA_ServerNetworkLayer nl =
  142. UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664);
  143. config.networkLayers = &nl;
  144. config.networkLayersSize = 1;
  145. UA_Server *server = UA_Server_new(config);
  146. addCurrentTimeVariable(server);
  147. addValueCallbackToCurrentTimeVariable(server);
  148. addCurrentTimeDataSourceVariable(server);
  149. UA_Server_run(server, &running);
  150. UA_Server_delete(server);
  151. nl.deleteMembers(&nl);
  152. return 0;
  153. }
  154. /**
  155. * DataChange Notifications
  156. * ^^^^^^^^^^^^^^^^^^^^^^^^
  157. * A client that is interested in the current value of a variable does not need
  158. * to regularly poll the variable. Instead, he can use the Subscription
  159. * mechanism to be notified about changes.
  160. *
  161. * Within a Subscription, the client adds so-called MonitoredItems. A DataChange
  162. * MonitoredItem defines a node attribute (usually the value attribute) that is
  163. * monitored for changes. The server internally reads the value in the defined
  164. * interval and generates the appropriate notifications. The three ways of
  165. * updating node values discussed above are all usable in combination with
  166. * notifications. That is because notifications use the standard *Read* service
  167. * to look for value changes. */