ua_client_worker.c 5.5 KB

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