tutorial_server_datasource.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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 simplicity, 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 <ua_server.h>
  27. #include <ua_config_default.h>
  28. #include <ua_log_stdout.h>
  29. #include <signal.h>
  30. static void
  31. updateCurrentTime(UA_Server *server) {
  32. UA_DateTime now = UA_DateTime_now();
  33. UA_Variant value;
  34. UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  35. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
  36. UA_Server_writeValue(server, currentNodeId, value);
  37. }
  38. static void
  39. addCurrentTimeVariable(UA_Server *server) {
  40. UA_DateTime now = 0;
  41. UA_VariableAttributes attr = UA_VariableAttributes_default;
  42. attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time");
  43. attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
  44. UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  45. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
  46. UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time");
  47. UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  48. UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
  49. UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
  50. UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
  51. parentReferenceNodeId, currentName,
  52. variableTypeNodeId, attr, NULL, NULL);
  53. updateCurrentTime(server);
  54. }
  55. /**
  56. * Variable Value Callback
  57. * ^^^^^^^^^^^^^^^^^^^^^^^
  58. *
  59. * When a value changes continuously, such as the system time, updating the
  60. * value in a tight loop would take up a lot of resources. Value callbacks allow
  61. * to synchronize a variable value with an external representation. They attach
  62. * callbacks to the variable that are executed before every read and after every
  63. * write operation. */
  64. static void
  65. beforeReadTime(UA_Server *server,
  66. const UA_NodeId *sessionId, void *sessionContext,
  67. const UA_NodeId *nodeid, void *nodeContext,
  68. const UA_NumericRange *range, const UA_DataValue *data) {
  69. UA_DateTime now = UA_DateTime_now();
  70. UA_Variant value;
  71. UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  72. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
  73. UA_Server_writeValue(server, currentNodeId, value);
  74. }
  75. static void
  76. afterWriteTime(UA_Server *server,
  77. const UA_NodeId *sessionId, void *sessionContext,
  78. const UA_NodeId *nodeId, void *nodeContext,
  79. const UA_NumericRange *range, const UA_DataValue *data) {
  80. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  81. "The variable was updated");
  82. }
  83. static void
  84. addValueCallbackToCurrentTimeVariable(UA_Server *server) {
  85. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time");
  86. UA_ValueCallback callback ;
  87. callback.onRead = beforeReadTime;
  88. callback.onWrite = afterWriteTime;
  89. UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
  90. }
  91. /**
  92. * Variable Data Sources
  93. * ^^^^^^^^^^^^^^^^^^^^^
  94. *
  95. * With value callbacks, the value is still stored in the variable node.
  96. * So-called data sources go one step further. The server redirects every read
  97. * and write request to a callback function. Upon reading, the callback provides
  98. * copy of the current value. Internally, the data source needs to implement its
  99. * own memory management. */
  100. static UA_StatusCode
  101. readCurrentTime(UA_Server *server,
  102. const UA_NodeId *sessionId, void *sessionContext,
  103. const UA_NodeId *nodeId, void *nodeContext,
  104. UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
  105. UA_DataValue *dataValue) {
  106. UA_DateTime now = UA_DateTime_now();
  107. UA_Variant_setScalarCopy(&dataValue->value, &now,
  108. &UA_TYPES[UA_TYPES_DATETIME]);
  109. dataValue->hasValue = true;
  110. return UA_STATUSCODE_GOOD;
  111. }
  112. static UA_StatusCode
  113. writeCurrentTime(UA_Server *server,
  114. const UA_NodeId *sessionId, void *sessionContext,
  115. const UA_NodeId *nodeId, void *nodeContext,
  116. const UA_NumericRange *range, const UA_DataValue *data) {
  117. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  118. "Changing the system time is not implemented");
  119. return UA_STATUSCODE_BADINTERNALERROR;
  120. }
  121. static void
  122. addCurrentTimeDataSourceVariable(UA_Server *server) {
  123. UA_VariableAttributes attr = UA_VariableAttributes_default;
  124. attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - data source");
  125. attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
  126. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-datasource");
  127. UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-datasource");
  128. UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  129. UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
  130. UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
  131. UA_DataSource timeDataSource;
  132. timeDataSource.read = readCurrentTime;
  133. timeDataSource.write = writeCurrentTime;
  134. UA_Server_addDataSourceVariableNode(server, currentNodeId, parentNodeId,
  135. parentReferenceNodeId, currentName,
  136. variableTypeNodeId, attr,
  137. timeDataSource, NULL, NULL);
  138. }
  139. /** It follows the main server code, making use of the above definitions. */
  140. UA_Boolean running = true;
  141. static void stopHandler(int sign) {
  142. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
  143. running = false;
  144. }
  145. int main(void) {
  146. signal(SIGINT, stopHandler);
  147. signal(SIGTERM, stopHandler);
  148. UA_ServerConfig *config = UA_ServerConfig_new_default();
  149. UA_Server *server = UA_Server_new(config);
  150. addCurrentTimeVariable(server);
  151. addValueCallbackToCurrentTimeVariable(server);
  152. addCurrentTimeDataSourceVariable(server);
  153. UA_StatusCode retval = UA_Server_run(server, &running);
  154. UA_Server_delete(server);
  155. UA_ServerConfig_delete(config);
  156. return (int)retval;
  157. }