check_server_callbacks.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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. UA_Boolean deleteNodeWhileWriting;
  19. THREAD_HANDLE server_thread;
  20. static void
  21. updateCurrentTime(void) {
  22. UA_DateTime now = UA_DateTime_now();
  23. UA_Variant value;
  24. UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  25. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
  26. UA_Server_writeValue(server, currentNodeId, value);
  27. }
  28. static void
  29. addCurrentTimeVariable(void) {
  30. UA_DateTime now = 0;
  31. UA_VariableAttributes attr = UA_VariableAttributes_default;
  32. attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - value callback");
  33. attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
  34. UA_Variant_setScalar(&attr.value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
  35. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
  36. UA_QualifiedName currentName = UA_QUALIFIEDNAME(1, "current-time-value-callback");
  37. UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
  38. UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
  39. UA_NodeId variableTypeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);
  40. UA_Server_addVariableNode(server, currentNodeId, parentNodeId,
  41. parentReferenceNodeId, currentName,
  42. variableTypeNodeId, attr, NULL, NULL);
  43. updateCurrentTime();
  44. }
  45. static void
  46. beforeReadTime(UA_Server *tmpserver,
  47. const UA_NodeId *sessionId, void *sessionContext,
  48. const UA_NodeId *nodeid, void *nodeContext,
  49. const UA_NumericRange *range, const UA_DataValue *data) {
  50. updateCurrentTime();
  51. }
  52. static void
  53. afterWriteTime(UA_Server *tmpServer,
  54. const UA_NodeId *sessionId, void *sessionContext,
  55. const UA_NodeId *nodeId, void *nodeContext,
  56. const UA_NumericRange *range, const UA_DataValue *data) {
  57. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "The variable was updated");
  58. }
  59. static void
  60. addValueCallbackToCurrentTimeVariable(void) {
  61. UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
  62. UA_ValueCallback callback ;
  63. callback.onRead = beforeReadTime;
  64. callback.onWrite = afterWriteTime;
  65. UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
  66. }
  67. static UA_StatusCode
  68. readTemperature(UA_Server *tmpServer,
  69. const UA_NodeId *sessionId, void *sessionContext,
  70. const UA_NodeId *nodeId, void *nodeContext,
  71. UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
  72. UA_DataValue *dataValue) {
  73. if (counter < 2)
  74. counter++;
  75. else
  76. UA_Server_deleteNode(server, temperatureNodeId, true);
  77. UA_Variant_setScalarCopy(&dataValue->value, &temperature, &UA_TYPES[UA_TYPES_INT32]);
  78. dataValue->hasValue = true;
  79. return UA_STATUSCODE_GOOD;
  80. }
  81. static UA_StatusCode
  82. writeTemperature(UA_Server *tmpServer,
  83. const UA_NodeId *sessionId, void *sessionContext,
  84. const UA_NodeId *nodeId, void *nodeContext,
  85. const UA_NumericRange *range, const UA_DataValue *data) {
  86. temperature = *(UA_Int32 *) data->value.data;
  87. if (deleteNodeWhileWriting)
  88. UA_Server_deleteNode(server, temperatureNodeId, true);
  89. return UA_STATUSCODE_GOOD;
  90. }
  91. static void
  92. addDataSourceVariable(void) {
  93. UA_VariableAttributes attr = UA_VariableAttributes_default;
  94. attr.displayName = UA_LOCALIZEDTEXT("en-US", "Temperature");
  95. attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
  96. UA_DataSource temperatureSource;
  97. temperatureSource.read = readTemperature;
  98. temperatureSource.write = writeTemperature;
  99. UA_StatusCode retval = UA_Server_addDataSourceVariableNode(server, temperatureNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
  100. UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Temperature"),
  101. UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr,
  102. temperatureSource, NULL, NULL);
  103. ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
  104. }
  105. THREAD_CALLBACK(serverloop) {
  106. while(running)
  107. UA_Server_run_iterate(server, true);
  108. return 0;
  109. }
  110. static void setup(void) {
  111. running = true;
  112. server = UA_Server_new();
  113. UA_ServerConfig_setDefault(UA_Server_getConfig(server));
  114. UA_Server_run_startup(server);
  115. addCurrentTimeVariable();
  116. addValueCallbackToCurrentTimeVariable();
  117. addDataSourceVariable();
  118. THREAD_CREATE(server_thread, serverloop);
  119. }
  120. static void teardown(void) {
  121. running = false;
  122. counter = 0;
  123. THREAD_JOIN(server_thread);
  124. UA_Server_run_shutdown(server);
  125. UA_Server_delete(server);
  126. }
  127. START_TEST(client_readValueCallbackAttribute) {
  128. UA_Client *client = UA_Client_new();
  129. UA_ClientConfig_setDefault(UA_Client_getConfig(client));
  130. UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
  131. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  132. UA_Variant val;
  133. UA_Variant_init(&val);
  134. UA_NodeId nodeId = UA_NODEID_STRING(1, "current-time-value-callback");
  135. retval = UA_Client_readValueAttribute(client, nodeId, &val);
  136. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  137. UA_Variant_deleteMembers(&val);
  138. UA_Client_disconnect(client);
  139. UA_Client_delete(client);
  140. }
  141. END_TEST
  142. START_TEST(client_readMultipleAttributes) {
  143. UA_Client *client = UA_Client_new();
  144. UA_ClientConfig_setDefault(UA_Client_getConfig(client));
  145. UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
  146. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  147. UA_ReadRequest request;
  148. UA_ReadRequest_init(&request);
  149. UA_ReadValueId ids[3];
  150. UA_ReadValueId_init(&ids[0]);
  151. ids[0].attributeId = UA_ATTRIBUTEID_DESCRIPTION;
  152. ids[0].nodeId = temperatureNodeId;
  153. UA_ReadValueId_init(&ids[1]);
  154. ids[1].attributeId = UA_ATTRIBUTEID_VALUE;
  155. ids[1].nodeId = temperatureNodeId;
  156. UA_ReadValueId_init(&ids[2]);
  157. ids[2].attributeId = UA_ATTRIBUTEID_BROWSENAME;
  158. ids[2].nodeId = temperatureNodeId;
  159. request.nodesToRead = ids;
  160. request.nodesToReadSize = 3;
  161. UA_ReadResponse response = UA_Client_Service_read(client, request);
  162. retval = response.responseHeader.serviceResult;
  163. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  164. ck_assert_int_eq(response.resultsSize, 3);
  165. ck_assert_uint_eq(response.results[0].status, UA_STATUSCODE_GOOD);
  166. ck_assert_uint_eq(response.results[1].status, UA_STATUSCODE_GOOD);
  167. ck_assert_uint_eq(response.results[2].status, UA_STATUSCODE_BADNODEIDUNKNOWN);
  168. UA_ReadResponse_deleteMembers(&response);
  169. UA_Client_disconnect(client);
  170. UA_Client_delete(client);
  171. }
  172. END_TEST
  173. START_TEST(client_writeValueCallbackAttribute) {
  174. UA_Client *client = UA_Client_new();
  175. UA_ClientConfig_setDefault(UA_Client_getConfig(client));
  176. UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
  177. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  178. UA_Variant *val = UA_Variant_new();
  179. UA_Int32 value = 77;
  180. UA_Variant_setScalarCopy(val, &value, &UA_TYPES[UA_TYPES_INT32]);
  181. retval = UA_Client_writeValueAttribute(client, temperatureNodeId, val);
  182. #ifdef UA_ENABLE_IMMUTABLE_NODES
  183. ck_assert_uint_eq(retval, UA_STATUSCODE_BADNODEIDUNKNOWN);
  184. #else
  185. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  186. #endif
  187. UA_Variant_delete(val);
  188. UA_Client_disconnect(client);
  189. UA_Client_delete(client);
  190. }
  191. END_TEST
  192. START_TEST(client_writeMultipleAttributes) {
  193. UA_Client *client = UA_Client_new();
  194. UA_ClientConfig_setDefault(UA_Client_getConfig(client));
  195. UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
  196. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  197. UA_Int32 value1 = 23;
  198. UA_WriteRequest wReq;
  199. UA_WriteRequest_init(&wReq);
  200. UA_LocalizedText string = UA_LOCALIZEDTEXT("en-US", "Temperature");
  201. UA_WriteValue wv[2];
  202. UA_WriteValue_init(&wv[0]);
  203. UA_WriteValue_init(&wv[1]);
  204. wReq.nodesToWrite = wv;
  205. wReq.nodesToWriteSize = 2;
  206. wReq.nodesToWrite[0].nodeId = temperatureNodeId;
  207. wReq.nodesToWrite[0].attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
  208. wReq.nodesToWrite[0].value.hasValue = true;
  209. wReq.nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
  210. wReq.nodesToWrite[0].value.value.storageType = UA_VARIANT_DATA_NODELETE;
  211. wReq.nodesToWrite[0].value.value.data = &string;
  212. wReq.nodesToWrite[1].nodeId = temperatureNodeId;
  213. wReq.nodesToWrite[1].attributeId = UA_ATTRIBUTEID_VALUE;
  214. wReq.nodesToWrite[1].value.hasValue = true;
  215. wReq.nodesToWrite[1].value.value.type = &UA_TYPES[UA_TYPES_INT32];
  216. wReq.nodesToWrite[1].value.value.storageType = UA_VARIANT_DATA_NODELETE;
  217. wReq.nodesToWrite[1].value.value.data = &value1;
  218. UA_WriteResponse wResp = UA_Client_Service_write(client, wReq);
  219. ck_assert_uint_eq(wResp.responseHeader.serviceResult,UA_STATUSCODE_GOOD);
  220. ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
  221. UA_WriteResponse_clear(&wResp);
  222. UA_Client_disconnect(client);
  223. UA_Client_delete(client);
  224. }
  225. END_TEST
  226. static Suite* testSuite_immutableNodes(void) {
  227. Suite *s = suite_create("Immutable Nodes");
  228. TCase *valueCallback = tcase_create("ValueCallback");
  229. deleteNodeWhileWriting = UA_FALSE;
  230. tcase_add_checked_fixture(valueCallback, setup, teardown);
  231. tcase_add_test(valueCallback, client_readValueCallbackAttribute);
  232. tcase_add_test(valueCallback, client_readMultipleAttributes);
  233. deleteNodeWhileWriting = UA_TRUE;
  234. tcase_add_test(valueCallback, client_writeValueCallbackAttribute);
  235. tcase_add_test(valueCallback, client_writeMultipleAttributes);
  236. suite_add_tcase(s,valueCallback);
  237. return s;
  238. }
  239. int main(void) {
  240. Suite *s = testSuite_immutableNodes();
  241. SRunner *sr = srunner_create(s);
  242. srunner_set_fork_status(sr, CK_NOFORK);
  243. srunner_run_all(sr, CK_NORMAL);
  244. int number_failed = srunner_ntests_failed(sr);
  245. srunner_free(sr);
  246. return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
  247. }