check_server_callbacks.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. #include <open62541/server_config_default.h>
  5. #include <open62541/plugin/log_stdout.h>
  6. #include <open62541/client_config_default.h>
  7. #include <open62541/client_highlevel.h>
  8. #include <check.h>
  9. #include "thread_wrapper.h"
  10. /* While server initialization, value callbacks are called twice.
  11. * This counter is used to ensure that the deletion of the variable is triggered by the client (not while the server initialization)*/
  12. int counter = 0;
  13. UA_Server *server;
  14. UA_Boolean running;
  15. UA_ServerNetworkLayer nl;
  16. UA_NodeId temperatureNodeId = {1, UA_NODEIDTYPE_NUMERIC, {1001}};
  17. UA_Int32 temperature;
  18. THREAD_HANDLE server_thread;
  19. static void
  20. updateCurrentTime(void) {
  21. UA_DateTime now = UA_DateTime_now();
  22. UA_Variant value;
  23. UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  24. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
  25. UA_Server_writeValue(server, currentNodeId, value);
  26. }
  27. static void
  28. addCurrentTimeVariable(void) {
  29. UA_DateTime now = 0;
  30. UA_VariableAttributes attr = UA_VariableAttributes_default;
  31. attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - value callback");
  32. attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
  33. UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  34. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
  35. UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-value-callback");
  36. UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  37. UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
  38. UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
  39. UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
  40. parentReferenceNodeId, currentName,
  41. variableTypeNodeId, attr, NULL, NULL);
  42. updateCurrentTime();
  43. }
  44. static void
  45. beforeReadTime(UA_Server *tmpserver,
  46. const UA_NodeId *sessionId, void *sessionContext,
  47. const UA_NodeId *nodeid, void *nodeContext,
  48. const UA_NumericRange *range, const UA_DataValue *data) {
  49. updateCurrentTime();
  50. }
  51. static void
  52. afterWriteTime(UA_Server *tmpServer,
  53. const UA_NodeId *sessionId, void *sessionContext,
  54. const UA_NodeId *nodeId, void *nodeContext,
  55. const UA_NumericRange *range, const UA_DataValue *data) {
  56. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "The variable was updated");
  57. }
  58. static void
  59. addValueCallbackToCurrentTimeVariable(void) {
  60. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
  61. UA_ValueCallback callback ;
  62. callback.onRead = beforeReadTime;
  63. callback.onWrite = afterWriteTime;
  64. UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
  65. }
  66. static UA_StatusCode
  67. readTemperature(UA_Server *tmpServer,
  68. const UA_NodeId *sessionId, void *sessionContext,
  69. const UA_NodeId *nodeId, void *nodeContext,
  70. UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
  71. UA_DataValue *dataValue) {
  72. if (counter < 2)
  73. counter++;
  74. else
  75. UA_Server_deleteNode(server, temperatureNodeId, true);
  76. UA_Variant_setScalarCopy(&dataValue->value, &temperature, &UA_TYPES[UA_TYPES_INT32]);
  77. dataValue->hasValue = true;
  78. return UA_STATUSCODE_GOOD;
  79. }
  80. static UA_StatusCode
  81. writeTemperature(UA_Server *tmpServer,
  82. const UA_NodeId *sessionId, void *sessionContext,
  83. const UA_NodeId *nodeId, void *nodeContext,
  84. const UA_NumericRange *range, const UA_DataValue *data) {
  85. temperature = *(UA_Int32 *) data->value.data;
  86. return UA_STATUSCODE_GOOD;
  87. }
  88. static void
  89. addDataSourceVariable(void) {
  90. UA_VariableAttributes attr = UA_VariableAttributes_default;
  91. attr.displayName = UA_LOCALIZEDTEXT("en-US", "Temperature");
  92. attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
  93. UA_DataSource temperatureSource;
  94. temperatureSource.read = readTemperature;
  95. temperatureSource.write = writeTemperature;
  96. UA_StatusCode retval = UA_Server_addDataSourceVariableNode(server, temperatureNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
  97. UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Temperature"),
  98. UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr,
  99. temperatureSource, NULL, NULL);
  100. ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
  101. }
  102. THREAD_CALLBACK(serverloop) {
  103. while(running)
  104. UA_Server_run_iterate(server, true);
  105. return 0;
  106. }
  107. static void setup(void) {
  108. running = true;
  109. server = UA_Server_new();
  110. UA_ServerConfig_setDefault(UA_Server_getConfig(server));
  111. UA_Server_run_startup(server);
  112. addCurrentTimeVariable();
  113. addValueCallbackToCurrentTimeVariable();
  114. addDataSourceVariable();
  115. THREAD_CREATE(server_thread, serverloop);
  116. }
  117. static void teardown(void) {
  118. running = false;
  119. counter = 0;
  120. THREAD_JOIN(server_thread);
  121. UA_Server_run_shutdown(server);
  122. UA_Server_delete(server);
  123. }
  124. START_TEST(client_readValueCallbackAttribute) {
  125. UA_Client *client = UA_Client_new();
  126. UA_ClientConfig_setDefault(UA_Client_getConfig(client));
  127. UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
  128. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  129. UA_Variant val;
  130. UA_Variant_init(&val);
  131. UA_NodeId nodeId = UA_NODEID_STRING(1, "current-time-value-callback");
  132. retval = UA_Client_readValueAttribute(client, nodeId, &val);
  133. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  134. UA_Variant_deleteMembers(&val);
  135. UA_Client_disconnect(client);
  136. UA_Client_delete(client);
  137. }
  138. END_TEST
  139. START_TEST(client_readMultipleAttributes) {
  140. UA_Client *client = UA_Client_new();
  141. UA_ClientConfig_setDefault(UA_Client_getConfig(client));
  142. UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
  143. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  144. UA_ReadRequest request;
  145. UA_ReadRequest_init(&request);
  146. UA_ReadValueId ids[3];
  147. UA_ReadValueId_init(&ids[0]);
  148. ids[0].attributeId = UA_ATTRIBUTEID_DESCRIPTION;
  149. ids[0].nodeId = temperatureNodeId;
  150. UA_ReadValueId_init(&ids[1]);
  151. ids[1].attributeId = UA_ATTRIBUTEID_VALUE;
  152. ids[1].nodeId = temperatureNodeId;
  153. UA_ReadValueId_init(&ids[2]);
  154. ids[2].attributeId = UA_ATTRIBUTEID_BROWSENAME;
  155. ids[2].nodeId = temperatureNodeId;
  156. request.nodesToRead = ids;
  157. request.nodesToReadSize = 3;
  158. UA_ReadResponse response = UA_Client_Service_read(client, request);
  159. retval = response.responseHeader.serviceResult;
  160. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  161. ck_assert_int_eq(response.resultsSize, 3);
  162. ck_assert_uint_eq(response.results[0].status, UA_STATUSCODE_GOOD);
  163. ck_assert_uint_eq(response.results[1].status, UA_STATUSCODE_GOOD);
  164. ck_assert_uint_eq(response.results[2].status, UA_STATUSCODE_BADNODEIDUNKNOWN);
  165. UA_ReadResponse_deleteMembers(&response);
  166. UA_Client_disconnect(client);
  167. UA_Client_delete(client);
  168. }
  169. END_TEST
  170. static Suite* testSuite_immutableNodes(void) {
  171. Suite *s = suite_create("Immutable Nodes");
  172. TCase *valueCallback = tcase_create("ValueCallback");
  173. tcase_add_checked_fixture(valueCallback, setup, teardown);
  174. tcase_add_test(valueCallback, client_readValueCallbackAttribute);
  175. tcase_add_test(valueCallback, client_readMultipleAttributes);
  176. suite_add_tcase(s,valueCallback);
  177. return s;
  178. }
  179. int main(void) {
  180. Suite *s = testSuite_immutableNodes();
  181. SRunner *sr = srunner_create(s);
  182. srunner_set_fork_status(sr, CK_NOFORK);
  183. srunner_run_all(sr, CK_NORMAL);
  184. int number_failed = srunner_ntests_failed(sr);
  185. srunner_free(sr);
  186. return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
  187. }