blob: eedc38804f20b094dcb5406b85181df6796ea9e8 [file] [log] [blame]
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ua_client_internal.h"
static void
asyncServiceTimeoutCheck(UA_Client *client) {
UA_DateTime now = UA_DateTime_nowMonotonic();
/* Timeout occurs, remove the callback */
AsyncServiceCall *ac, *ac_tmp;
LIST_FOREACH_SAFE(ac, &client->asyncServiceCalls, pointers, ac_tmp) {
if(!ac->timeout)
continue;
if(ac->start + (UA_DateTime)(ac->timeout * UA_DATETIME_MSEC) <= now) {
LIST_REMOVE(ac, pointers);
UA_Client_AsyncService_cancel(client, ac, UA_STATUSCODE_BADTIMEOUT);
UA_free(ac);
}
}
}
static void
backgroundConnectivityCallback(UA_Client *client, void *userdata,
UA_UInt32 requestId, const UA_ReadResponse *response) {
if(response->responseHeader.serviceResult == UA_STATUSCODE_BADTIMEOUT) {
if (client->config.inactivityCallback)
client->config.inactivityCallback(client);
}
client->pendingConnectivityCheck = false;
client->lastConnectivityCheck = UA_DateTime_nowMonotonic();
}
static UA_StatusCode
UA_Client_backgroundConnectivity(UA_Client *client) {
if(!client->config.connectivityCheckInterval)
return UA_STATUSCODE_GOOD;
if (client->pendingConnectivityCheck)
return UA_STATUSCODE_GOOD;
UA_DateTime now = UA_DateTime_nowMonotonic();
UA_DateTime nextDate = client->lastConnectivityCheck + (UA_DateTime)(client->config.connectivityCheckInterval * UA_DATETIME_MSEC);
if(now <= nextDate)
return UA_STATUSCODE_GOOD;
UA_ReadRequest request;
UA_ReadRequest_init(&request);
UA_ReadValueId rvid;
UA_ReadValueId_init(&rvid);
rvid.attributeId = UA_ATTRIBUTEID_VALUE;
rvid.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE);
request.nodesToRead = &rvid;
request.nodesToReadSize = 1;
UA_StatusCode retval = __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_READREQUEST],
(UA_ClientAsyncServiceCallback)backgroundConnectivityCallback,
&UA_TYPES[UA_TYPES_READRESPONSE], NULL, NULL);
client->pendingConnectivityCheck = true;
return retval;
}
/**
* Main Client Loop
* ----------------
* Start: Spin up the workers and the network layer
* Iterate: Process repeated callbacks and events in the network layer.
* This part can be driven from an external main-loop in an
* event-driven single-threaded architecture.
* Stop: Stop workers, finish all callbacks, stop the network layer,
* clean up */
static void
clientExecuteRepeatedCallback(UA_Client *client, UA_ApplicationCallback cb,
void *callbackApplication, void *data) {
cb(callbackApplication, data);
/* TODO: Use workers in the client
* UA_WorkQueue_enqueue(&client->workQueue, cb, callbackApplication, data); */
}
UA_StatusCode UA_Client_run_iterate(UA_Client *client, UA_UInt16 timeout) {
// TODO connectivity check & timeout features for the async implementation (timeout == 0)
UA_StatusCode retval = UA_STATUSCODE_GOOD;
#ifdef UA_ENABLE_SUBSCRIPTIONS
UA_StatusCode retvalPublish = UA_Client_Subscriptions_backgroundPublish(client);
if(client->state >= UA_CLIENTSTATE_SESSION && retvalPublish != UA_STATUSCODE_GOOD)
return retvalPublish;
#endif
/* Make sure we have an open channel */
/************************************************************/
/* FIXME: This is a dirty workaround */
if(client->state >= UA_CLIENTSTATE_SECURECHANNEL)
retval = openSecureChannel(client, true);
/* FIXME: Will most likely break somewhere in the future */
/************************************************************/
if(timeout) {
if(retval != UA_STATUSCODE_GOOD)
return retval;
retval = UA_Client_backgroundConnectivity(client);
if(retval != UA_STATUSCODE_GOOD)
return retval;
UA_DateTime maxDate = UA_DateTime_nowMonotonic() + (timeout * UA_DATETIME_MSEC);
retval = receiveServiceResponse(client, NULL, NULL, maxDate, NULL);
if(retval == UA_STATUSCODE_GOODNONCRITICALTIMEOUT)
retval = UA_STATUSCODE_GOOD;
} else {
UA_DateTime now = UA_DateTime_nowMonotonic();
UA_Timer_process(&client->timer, now,
(UA_TimerExecutionCallback)clientExecuteRepeatedCallback, client);
UA_ClientState cs = UA_Client_getState(client);
retval = UA_Client_connect_iterate(client);
/* Connection failed, drop the rest */
if(retval != UA_STATUSCODE_GOOD)
return retval;
if((cs == UA_CLIENTSTATE_SECURECHANNEL) || (cs == UA_CLIENTSTATE_SESSION)) {
/* Check for new data */
retval = receiveServiceResponseAsync(client, NULL, NULL);
} else {
retval = receivePacketAsync(client);
}
}
#ifdef UA_ENABLE_SUBSCRIPTIONS
/* The inactivity check must be done after receiveServiceResponse*/
UA_Client_Subscriptions_backgroundPublishInactivityCheck(client);
#endif
asyncServiceTimeoutCheck(client);
#if UA_MULTITHREADING < 200
/* Process delayed callbacks when all callbacks and network events are
* done */
UA_WorkQueue_manuallyProcessDelayed(&client->workQueue);
#endif
return retval;
}