tutorial_server_datasource.c 7.3 KB

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