client_method_async.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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. #include <open62541/client_config_default.h>
  4. #include <open62541/client_highlevel_async.h>
  5. #include <open62541/client_subscriptions.h>
  6. #include <open62541/plugin/log_stdout.h>
  7. #include <open62541/server_config_default.h>
  8. #include <open62541/client_subscriptions.h>
  9. #include <stdlib.h>
  10. #include <signal.h>
  11. UA_Boolean running = true;
  12. static void InitCallMulti(UA_Client* client);
  13. #ifdef UA_ENABLE_METHODCALLS
  14. static void
  15. methodCalled(UA_Client *client, void *userdata, UA_UInt32 requestId,
  16. UA_CallResponse *response) {
  17. UA_UInt32 i;
  18. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "**** CallRequest Response - Req:%u with %u results",
  19. requestId, (UA_UInt32)response->resultsSize);
  20. UA_StatusCode retval = response->responseHeader.serviceResult;
  21. if (retval == UA_STATUSCODE_GOOD) {
  22. for (i = 0; i < response->resultsSize; i++) {
  23. if (response->resultsSize >= i)
  24. retval = response->results[i].statusCode;
  25. else
  26. retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
  27. if (retval != UA_STATUSCODE_GOOD) {
  28. UA_CallResponse_clear(response);
  29. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "**** CallRequest Response - Req: %u (%u) failed", requestId,i);
  30. if (i == response->resultsSize)
  31. return;
  32. else
  33. continue;
  34. }
  35. /* Move the output arguments */
  36. UA_Variant *output = response->results[i].outputArguments;
  37. size_t outputSize = response->results[i].outputArgumentsSize;
  38. response->results[i].outputArguments = NULL;
  39. response->results[i].outputArgumentsSize = 0;
  40. if (retval == UA_STATUSCODE_GOOD) {
  41. printf("---Method call was successful, returned %lu values.\n",
  42. (unsigned long)outputSize);
  43. UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
  44. }
  45. else {
  46. printf("---Method call was unsuccessful, returned %x values.\n",
  47. retval);
  48. }
  49. }
  50. }
  51. else
  52. {
  53. UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "**** CallRequest Response - Req:%u FAILED", requestId);
  54. }
  55. UA_CallResponse_clear(response);
  56. /* We initiate the MultiCall (2 methods within one CallRequest) */
  57. InitCallMulti(client);
  58. }
  59. #ifdef UA_ENABLE_METHODCALLS
  60. /* Workaround because we do not have an API for that yet */
  61. static UA_StatusCode
  62. UA_Client_call_asyncMulti(UA_Client *client,
  63. const UA_NodeId objectId1, const UA_NodeId methodId1, size_t inputSize1, const UA_Variant *input1,
  64. const UA_NodeId objectId2, const UA_NodeId methodId2, size_t inputSize2, const UA_Variant *input2,
  65. UA_ClientAsyncServiceCallback callback, void *userdata, UA_UInt32 *reqId) {
  66. UA_CallRequest request;
  67. UA_CallRequest_init(&request);
  68. UA_CallMethodRequest item[2];
  69. UA_CallMethodRequest_init(&item[0]);
  70. item[0].methodId = methodId1;
  71. item[0].objectId = objectId1;
  72. item[0].inputArguments = (UA_Variant *)(void*)(uintptr_t)input1; // cast const...
  73. item[0].inputArgumentsSize = inputSize1;
  74. UA_CallMethodRequest_init(&item[1]);
  75. item[1].methodId = methodId2;
  76. item[1].objectId = objectId2;
  77. item[1].inputArguments = (UA_Variant *)(void*)(uintptr_t)input2; // cast const...
  78. item[1].inputArgumentsSize = inputSize2;
  79. request.methodsToCall = &item[0];
  80. request.methodsToCallSize = 2;
  81. return __UA_Client_AsyncService(client, &request,
  82. &UA_TYPES[UA_TYPES_CALLREQUEST], callback,
  83. &UA_TYPES[UA_TYPES_CALLRESPONSE], userdata, reqId);
  84. }
  85. /* End Workaround */
  86. static void InitCallMulti(UA_Client* client) {
  87. UA_UInt32 reqId = 0;
  88. UA_Variant input;
  89. UA_Variant_init(&input);
  90. UA_String stringValue = UA_String_fromChars("World 3 (multi)");
  91. UA_Variant_setScalar(&input, &stringValue, &UA_TYPES[UA_TYPES_STRING]);
  92. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "**** Initiating CallRequest 3");
  93. UA_Client_call_asyncMulti(client,
  94. UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
  95. UA_NODEID_NUMERIC(1, 62542), 1, &input,
  96. UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
  97. UA_NODEID_NUMERIC(1, 62541), 1, &input,
  98. (UA_ClientAsyncServiceCallback)methodCalled, NULL, &reqId);
  99. UA_String_clear(&stringValue);
  100. }
  101. #endif
  102. #endif /* UA_ENABLE_METHODCALLS */
  103. static void stopHandler(int sign) {
  104. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Received Ctrl-C");
  105. running = 0;
  106. }
  107. static void
  108. handler_currentTimeChanged(UA_Client *client, UA_UInt32 subId, void *subContext,
  109. UA_UInt32 monId, void *monContext, UA_DataValue *value) {
  110. //UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "currentTime has changed!");
  111. if (UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_DATETIME])) {
  112. UA_DateTime raw_date = *(UA_DateTime *)value->value.data;
  113. UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
  114. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  115. "date is: %02u-%02u-%04u %02u:%02u:%02u.%03u",
  116. dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec);
  117. }
  118. }
  119. static void
  120. deleteSubscriptionCallback(UA_Client *client, UA_UInt32 subscriptionId, void *subscriptionContext) {
  121. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  122. "Subscription Id %u was deleted", subscriptionId);
  123. }
  124. static void
  125. subscriptionInactivityCallback(UA_Client *client, UA_UInt32 subId, void *subContext) {
  126. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Inactivity for subscription %u", subId);
  127. }
  128. static void
  129. stateCallback(UA_Client *client, UA_ClientState clientState) {
  130. switch (clientState) {
  131. case UA_CLIENTSTATE_DISCONNECTED:
  132. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "The client is disconnected");
  133. break;
  134. case UA_CLIENTSTATE_WAITING_FOR_ACK:
  135. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Waiting for ack");
  136. break;
  137. case UA_CLIENTSTATE_CONNECTED:
  138. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  139. "A TCP connection to the server is open");
  140. break;
  141. case UA_CLIENTSTATE_SECURECHANNEL:
  142. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  143. "A SecureChannel to the server is open");
  144. break;
  145. case UA_CLIENTSTATE_SESSION: {
  146. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "A session with the server is open");
  147. /* A new session was created. We need to create the subscription. */
  148. /* Create a subscription */
  149. UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
  150. UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request,
  151. NULL, NULL, deleteSubscriptionCallback);
  152. if (response.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
  153. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  154. "Create subscription succeeded, id %u", response.subscriptionId);
  155. else
  156. return;
  157. /* Add a MonitoredItem */
  158. UA_MonitoredItemCreateRequest monRequest =
  159. UA_MonitoredItemCreateRequest_default(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME));
  160. UA_MonitoredItemCreateResult monResponse =
  161. UA_Client_MonitoredItems_createDataChange(client, response.subscriptionId,
  162. UA_TIMESTAMPSTORETURN_BOTH,
  163. monRequest, NULL, handler_currentTimeChanged, NULL);
  164. if (monResponse.statusCode == UA_STATUSCODE_GOOD)
  165. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  166. "Monitoring UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME', id %u",
  167. monResponse.monitoredItemId);
  168. //TODO: check the existance of the nodes inside these functions (otherwise seg faults)
  169. #ifdef UA_ENABLE_METHODCALLS
  170. UA_UInt32 reqId = 0;
  171. UA_Variant input;
  172. UA_Variant_init(&input);
  173. UA_String stringValue = UA_String_fromChars("World 1");
  174. UA_Variant_setScalar(&input, &stringValue, &UA_TYPES[UA_TYPES_STRING]);
  175. /* Initiate Call 1 */
  176. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "**** Initiating CallRequest 1");
  177. UA_Client_call_async(client,
  178. UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
  179. UA_NODEID_NUMERIC(1, 62541), 1, &input,
  180. methodCalled, NULL, &reqId);
  181. UA_String_clear(&stringValue);
  182. /* Initiate Call 2 */
  183. UA_Variant_init(&input);
  184. stringValue = UA_String_fromChars("World 2");
  185. UA_Variant_setScalar(&input, &stringValue, &UA_TYPES[UA_TYPES_STRING]);
  186. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "**** Initiating CallRequest 2");
  187. UA_Client_call_async(client,
  188. UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
  189. UA_NODEID_NUMERIC(1, 62542), 1, &input,
  190. methodCalled, NULL, &reqId);
  191. UA_String_clear(&stringValue);
  192. #endif /* UA_ENABLE_METHODCALLS */
  193. }
  194. break;
  195. case UA_CLIENTSTATE_SESSION_RENEWED:
  196. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  197. "A session with the server is open (renewed)");
  198. /* The session was renewed. We don't need to recreate the subscription. */
  199. break;
  200. case UA_CLIENTSTATE_SESSION_DISCONNECTED:
  201. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Session disconnected");
  202. break;
  203. }
  204. return;
  205. }
  206. int
  207. main(int argc, char *argv[]) {
  208. signal(SIGINT, stopHandler); /* catches ctrl-c */
  209. UA_Client *client = UA_Client_new();
  210. UA_ClientConfig *cc = UA_Client_getConfig(client);
  211. UA_ClientConfig_setDefault(cc);
  212. /* we use a high timeout because there may be other client and
  213. * processing may take long if many method calls are waiting */
  214. cc->timeout = 60000;
  215. /* Set stateCallback */
  216. cc->stateCallback = stateCallback;
  217. cc->subscriptionInactivityCallback = subscriptionInactivityCallback;
  218. UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
  219. if(retval != UA_STATUSCODE_GOOD) {
  220. UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  221. "Not connected. Retrying to connect in 1 second");
  222. UA_Client_delete(client);
  223. return EXIT_SUCCESS;
  224. }
  225. /* Endless loop runAsync */
  226. while (running) {
  227. UA_Client_run_iterate(client, 100);
  228. }
  229. /* Clean up */
  230. /* Async disconnect kills unprocessed requests */
  231. // UA_Client_disconnect_async (client, &reqId); //can only be used when connected = true
  232. // UA_Client_run_iterate (client, &timedOut);
  233. UA_Client_disconnect(client);
  234. UA_Client_delete(client);
  235. return EXIT_SUCCESS;
  236. }