ua_client_worker.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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 "ua_util.h"
  5. #include "ua_client.h"
  6. #include "ua_client_internal.h"
  7. static void
  8. asyncServiceTimeoutCheck(UA_Client *client) {
  9. UA_DateTime now = UA_DateTime_nowMonotonic();
  10. /* Timeout occurs, remove the callback */
  11. AsyncServiceCall *ac, *ac_tmp;
  12. LIST_FOREACH_SAFE(ac, &client->asyncServiceCalls, pointers, ac_tmp) {
  13. if(!ac->timeout)
  14. continue;
  15. if(ac->start + (UA_DateTime)(ac->timeout * UA_DATETIME_MSEC) <= now) {
  16. LIST_REMOVE(ac, pointers);
  17. UA_Client_AsyncService_cancel(client, ac, UA_STATUSCODE_BADTIMEOUT);
  18. UA_free(ac);
  19. }
  20. }
  21. }
  22. static void
  23. backgroundConnectivityCallback(UA_Client *client, void *userdata,
  24. UA_UInt32 requestId, const UA_ReadResponse *response) {
  25. if(response->responseHeader.serviceResult == UA_STATUSCODE_BADTIMEOUT) {
  26. if (client->config.inactivityCallback)
  27. client->config.inactivityCallback(client);
  28. }
  29. client->pendingConnectivityCheck = false;
  30. client->lastConnectivityCheck = UA_DateTime_nowMonotonic();
  31. }
  32. static UA_StatusCode
  33. UA_Client_backgroundConnectivity(UA_Client *client) {
  34. if(!client->config.connectivityCheckInterval)
  35. return UA_STATUSCODE_GOOD;
  36. if (client->pendingConnectivityCheck)
  37. return UA_STATUSCODE_GOOD;
  38. UA_DateTime now = UA_DateTime_nowMonotonic();
  39. UA_DateTime nextDate = client->lastConnectivityCheck + (UA_DateTime)(client->config.connectivityCheckInterval * UA_DATETIME_MSEC);
  40. if(now <= nextDate)
  41. return UA_STATUSCODE_GOOD;
  42. UA_ReadRequest request;
  43. UA_ReadRequest_init(&request);
  44. UA_ReadValueId rvid;
  45. UA_ReadValueId_init(&rvid);
  46. rvid.attributeId = UA_ATTRIBUTEID_VALUE;
  47. rvid.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE);
  48. request.nodesToRead = &rvid;
  49. request.nodesToReadSize = 1;
  50. UA_StatusCode retval = __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_READREQUEST],
  51. (UA_ClientAsyncServiceCallback)backgroundConnectivityCallback,
  52. &UA_TYPES[UA_TYPES_READRESPONSE], NULL, NULL);
  53. client->pendingConnectivityCheck = true;
  54. return retval;
  55. }
  56. /**
  57. * Main Client Loop
  58. * ----------------
  59. * Start: Spin up the workers and the network layer
  60. * Iterate: Process repeated callbacks and events in the network layer.
  61. * This part can be driven from an external main-loop in an
  62. * event-driven single-threaded architecture.
  63. * Stop: Stop workers, finish all callbacks, stop the network layer,
  64. * clean up */
  65. static void
  66. clientExecuteRepeatedCallback(UA_Client *client, UA_ApplicationCallback cb,
  67. void *callbackApplication, void *data) {
  68. cb(callbackApplication, data);
  69. /* TODO: Use workers in the client
  70. * UA_WorkQueue_enqueue(&client->workQueue, cb, callbackApplication, data); */
  71. }
  72. UA_StatusCode UA_Client_run_iterate(UA_Client *client, UA_UInt16 timeout) {
  73. // TODO connectivity check & timeout features for the async implementation (timeout == 0)
  74. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  75. #ifdef UA_ENABLE_SUBSCRIPTIONS
  76. UA_StatusCode retvalPublish = UA_Client_Subscriptions_backgroundPublish(client);
  77. if(client->state >= UA_CLIENTSTATE_SESSION && retvalPublish != UA_STATUSCODE_GOOD)
  78. return retvalPublish;
  79. #endif
  80. /* Make sure we have an open channel */
  81. /************************************************************/
  82. /* FIXME: This is a dirty workaround */
  83. if(client->state >= UA_CLIENTSTATE_SECURECHANNEL)
  84. retval = openSecureChannel(client, true);
  85. /* FIXME: Will most likely break somewhere in the future */
  86. /************************************************************/
  87. if(timeout) {
  88. if(retval != UA_STATUSCODE_GOOD)
  89. return retval;
  90. retval = UA_Client_backgroundConnectivity(client);
  91. if(retval != UA_STATUSCODE_GOOD)
  92. return retval;
  93. UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (timeout * UA_DATETIME_MSEC);
  94. retval = receiveServiceResponse(client, NULL, NULL, maxDate, NULL);
  95. if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT)
  96. retval = UA_STATUSCODE_GOOD;
  97. } else {
  98. UA_DateTime now = UA_DateTime_nowMonotonic();
  99. UA_Timer_process(&client->timer, now,
  100. (UA_TimerExecutionCallback)clientExecuteRepeatedCallback, client);
  101. UA_ClientState cs = UA_Client_getState(client);
  102. retval = UA_Client_connect_iterate(client);
  103. /* Connection failed, drop the rest */
  104. if(retval != UA_STATUSCODE_GOOD)
  105. return retval;
  106. if((cs == UA_CLIENTSTATE_SECURECHANNEL) || (cs == UA_CLIENTSTATE_SESSION)) {
  107. /* Check for new data */
  108. retval = receiveServiceResponseAsync(client, NULL, NULL);
  109. } else {
  110. retval = receivePacketAsync(client);
  111. }
  112. }
  113. #ifdef UA_ENABLE_SUBSCRIPTIONS
  114. /* The inactivity check must be done after receiveServiceResponse*/
  115. UA_Client_Subscriptions_backgroundPublishInactivityCheck(client);
  116. #endif
  117. asyncServiceTimeoutCheck(client);
  118. #ifndef UA_ENABLE_MULTITHREADING
  119. /* Process delayed callbacks when all callbacks and network events are
  120. * done */
  121. UA_WorkQueue_manuallyProcessDelayed(&client->workQueue);
  122. #endif
  123. return retval;
  124. }