Forráskód Böngészése

WIP client : timed async service & async highlevel functions with client worker for 0.4 (#1598)

This commit adds asynchronous services to open62541 on client side

* Asynchronous service (highlevel read/write) are available through the API that is defined in
ua_client_highlevel_async.h

* Besides the services, the connection procedure is asynchronous as well.

* A client Application that uses the asynchronous API 
adds requests by calling an asynchronous service (e.g., UA_Client_sendAsyncReadRequest(), UA_Client_sendAsyncBrowseRequest() )
checks for response with UA_Client_run_iterate() repeatedly
and processes the responses with user-defined callbacks
StalderT 6 éve
szülő
commit
047c5e4098

+ 4 - 1
CMakeLists.txt

@@ -370,7 +370,8 @@ set(exported_headers ${PROJECT_BINARY_DIR}/src_generated/ua_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client_config.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client.h
                      ${PROJECT_SOURCE_DIR}/include/ua_client_highlevel.h
-                     ${PROJECT_SOURCE_DIR}/include/ua_client_subscriptions.h)
+                     ${PROJECT_SOURCE_DIR}/include/ua_client_subscriptions.h
+                     ${PROJECT_SOURCE_DIR}/include/ua_client_highlevel_async.h)
 
 set(internal_headers ${PROJECT_SOURCE_DIR}/deps/queue.h
                      ${PROJECT_SOURCE_DIR}/deps/pcg_basic.h
@@ -429,9 +430,11 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 # client
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_connect.c
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client_connect_async.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_discovery.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_highlevel.c
                 ${PROJECT_SOURCE_DIR}/src/client/ua_client_subscriptions.c
+                ${PROJECT_SOURCE_DIR}/src/client/ua_client_worker.c
 
                 # dependencies
                 ${PROJECT_SOURCE_DIR}/deps/libc_time.c

+ 5 - 1
examples/CMakeLists.txt

@@ -62,8 +62,12 @@ add_example(server_ctt server_ctt.c)
 
 add_example(client client.c)
 
+add_example(client_async client_async.c)
+
 add_example(client_connect_loop client_connect_loop.c)
 
+add_example(client_connectivitycheck_loop client_connectivitycheck_loop.c)
+
 if(UA_ENABLE_SUBSCRIPTIONS)
 add_example(client_subscription_loop client_subscription_loop.c)
 endif()
@@ -127,4 +131,4 @@ add_subdirectory(nodeset)
 if(UA_ENABLE_PUBSUB)
     add_example(tutorial_pubsub_connection pubsub/tutorial_pubsub_connection.c)
     add_example(tutorial_pubsub_publish pubsub/tutorial_pubsub_publish.c)
-endif()
+endif()

+ 2 - 2
examples/client.c

@@ -114,7 +114,7 @@ int main(int argc, char *argv[]) {
 
 
     /* The first publish request should return the initial value of the variable */
-    UA_Client_runAsync(client, 1000);
+    UA_Client_run_iterate(client, 1000);
 #endif
 
     /* Read attribute */
@@ -157,7 +157,7 @@ int main(int argc, char *argv[]) {
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     /* Take another look at the.answer */
-    UA_Client_runAsync(client, 100);
+    UA_Client_run_iterate(client, 100);
     /* Delete the subscription */
     if(UA_Client_Subscriptions_deleteSingle(client, subId) == UA_STATUSCODE_GOOD)
         printf("Subscription removed\n");

+ 224 - 0
examples/client_async.c

@@ -0,0 +1,224 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
+#ifndef _DEFAULT_SOURCE
+# define _DEFAULT_SOURCE
+#endif
+/* On older systems we need to define _BSD_SOURCE.
+ * _DEFAULT_SOURCE is an alias for that. */
+#ifndef _BSD_SOURCE
+# define _BSD_SOURCE
+#endif
+
+#include <stdio.h>
+#include "open62541.h"
+
+#ifdef _WIN32
+# include <windows.h>
+# define UA_sleep_ms(X) Sleep(X)
+#else
+# include <unistd.h>
+# define UA_sleep_ms(X) usleep(X * 1000)
+#endif
+
+#define NODES_EXIST
+/* async connection callback, it only gets called after the completion of the whole
+ * connection process*/
+static void
+onConnect (UA_Client *client, void *userdata, UA_UInt32 requestId,
+           void *status) {
+    printf ("Async connect returned with status code %s\n",
+            UA_StatusCode_name (*(UA_StatusCode *) status));
+}
+
+static
+void
+fileBrowsed (UA_Client *client, void *userdata, UA_UInt32 requestId,
+             UA_BrowseResponse *response) {
+    printf ("%-50s%i\n", "Received BrowseResponse for request ", requestId);
+    UA_String us = *(UA_String *) userdata;
+    printf ("---%.*s passed safely \n", (int) us.length, us.data);
+}
+
+/*high-level function callbacks*/
+static
+void
+readValueAttributeCallback (UA_Client *client, void *userdata,
+                            UA_UInt32 requestId, UA_Variant *var) {
+    printf ("%-50s%i\n", "Read value attribute for request", requestId);
+
+    if (UA_Variant_hasScalarType (var, &UA_TYPES[UA_TYPES_INT32])) {
+        UA_Int32 int_val = *(UA_Int32*) var->data;
+        printf ("---%-40s%-8i\n",
+                "Reading the value of node (1, \"the.answer\"):", int_val);
+    }
+
+    /*more type distinctions possible*/
+    return;
+}
+
+static
+void
+attrWritten (UA_Client *client, void *userdata, UA_UInt32 requestId,
+             UA_WriteResponse *response) {
+    /*assuming no data to be retrieved by writing attributes*/
+    printf ("%-50s%i\n", "Wrote value attribute for request ", requestId);
+    UA_WriteResponse_deleteMembers(response);
+}
+
+#ifdef NODES_EXIST
+static void
+methodCalled (UA_Client *client, void *userdata, UA_UInt32 requestId,
+              UA_CallResponse *response) {
+
+    printf ("%-50s%i\n", "Called method for request ", requestId);
+    size_t outputSize;
+    UA_Variant *output;
+    UA_StatusCode retval = response->responseHeader.serviceResult;
+    if (retval == UA_STATUSCODE_GOOD) {
+        if (response->resultsSize == 1)
+            retval = response->results[0].statusCode;
+        else
+            retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
+    }
+    if (retval != UA_STATUSCODE_GOOD) {
+        UA_CallResponse_deleteMembers (response);
+    }
+
+    /* Move the output arguments */
+    output = response->results[0].outputArguments;
+    outputSize = response->results[0].outputArgumentsSize;
+    response->results[0].outputArguments = NULL;
+    response->results[0].outputArgumentsSize = 0;
+
+    if (retval == UA_STATUSCODE_GOOD) {
+        printf ("---Method call was successful, returned %lu values.\n",
+                (unsigned long) outputSize);
+        UA_Array_delete (output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
+    }
+    else {
+        printf ("---Method call was unsuccessful, returned %x values.\n",
+                retval);
+    }
+    UA_CallResponse_deleteMembers (response);
+}
+
+static void
+translateCalled (UA_Client *client, void *userdata, UA_UInt32 requestId,
+                 UA_TranslateBrowsePathsToNodeIdsResponse *response) {
+    printf ("%-50s%i\n", "Translated path for request ", requestId);
+
+    if (response->results[0].targetsSize == 1) {
+        return;
+    }
+    UA_TranslateBrowsePathsToNodeIdsResponse_deleteMembers (response);
+}
+#endif
+
+int
+main (int argc, char *argv[]) {
+    UA_Client *client = UA_Client_new (UA_ClientConfig_default);
+    UA_UInt32 reqId = 0;
+    UA_String userdata = UA_STRING ("userdata");
+
+    UA_BrowseRequest bReq;
+    UA_BrowseRequest_init (&bReq);
+    bReq.requestedMaxReferencesPerNode = 0;
+    bReq.nodesToBrowse = UA_BrowseDescription_new ();
+    bReq.nodesToBrowseSize = 1;
+    bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC (0,
+    UA_NS0ID_OBJECTSFOLDER); /* browse objects folder */
+    bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
+
+    UA_Client_connect_async (client, "opc.tcp://localhost:4840", onConnect,
+                             NULL);
+
+    /*Windows needs time to response*/
+    UA_sleep_ms(100);
+
+    /* What happens if client tries to send request before connected? */
+    UA_Client_sendAsyncBrowseRequest (client, &bReq, fileBrowsed, &userdata,
+                                      &reqId);
+
+    UA_DateTime startTime = UA_DateTime_nowMonotonic();
+    do {
+        /*TODO: fix memory-related bugs if condition not checked*/
+        if (UA_Client_getState (client) == UA_CLIENTSTATE_SESSION) {
+            /* If not connected requests are not sent */
+            UA_Client_sendAsyncBrowseRequest (client, &bReq, fileBrowsed,
+                                              &userdata, &reqId);
+        }
+        /* Requests are processed */
+        UA_BrowseRequest_deleteMembers(&bReq);
+        UA_Client_run_iterate (client, 0);
+        UA_sleep_ms(100);
+
+        /* Break loop if server cannot be connected within 2s -- prevents build timeout */
+        if (UA_DateTime_nowMonotonic() - startTime > 2000 * UA_DATETIME_MSEC)
+            break;
+    }
+    while (reqId < 10);
+
+    /* Demo: high-level functions */
+    UA_Int32 value = 0;
+    UA_Variant myVariant;
+    UA_Variant_init(&myVariant);
+
+    UA_Variant input;
+    UA_Variant_init (&input);
+
+    for (UA_UInt16 i = 0; i < 5; i++) {
+        if (UA_Client_getState (client) == UA_CLIENTSTATE_SESSION) {
+            /* writing and reading value 1 to 5 */
+            UA_Variant_setScalarCopy (&myVariant, &value, &UA_TYPES[UA_TYPES_INT32]);
+            value++;
+            UA_Client_writeValueAttribute_async(client,
+                                                UA_NODEID_STRING (1, "the.answer"),
+                                                &myVariant, attrWritten, NULL,
+                                                &reqId);
+            UA_Variant_deleteMembers (&myVariant);
+
+            UA_Client_readValueAttribute_async(client,
+                                               UA_NODEID_STRING (1, "the.answer"),
+                                               readValueAttributeCallback, NULL,
+                                               &reqId);
+
+//TODO: check the existance of the nodes inside these functions (otherwise seg faults)
+#ifdef NODES_EXIST
+            UA_String stringValue = UA_String_fromChars ("World");
+            UA_Variant_setScalar (&input, &stringValue, &UA_TYPES[UA_TYPES_STRING]);
+
+            UA_Client_call_async(client,
+                                 UA_NODEID_NUMERIC (0, UA_NS0ID_OBJECTSFOLDER),
+                                 UA_NODEID_NUMERIC (1, 62541), 1, &input,
+                                 methodCalled, NULL, &reqId);
+            UA_String_deleteMembers(&stringValue);
+
+    #define pathSize 3
+            char *paths[pathSize] = { "Server", "ServerStatus", "State" };
+            UA_UInt32 ids[pathSize] = { UA_NS0ID_ORGANIZES,
+            UA_NS0ID_HASCOMPONENT, UA_NS0ID_HASCOMPONENT };
+
+            UA_Cient_translateBrowsePathsToNodeIds_async (client, paths, ids,
+                                                          pathSize,
+                                                          translateCalled, NULL,
+                                                          &reqId);
+#endif
+            /* How often UA_Client_run_iterate is called depends on the number of request sent */
+            UA_Client_run_iterate(client, 0);
+            UA_Client_run_iterate(client, 0);
+        }
+    }
+    UA_Client_run_iterate (client, 0);
+
+    /* Async disconnect kills unprocessed requests */
+    // UA_Client_disconnect_async (client, &reqId); //can only be used when connected = true
+    // UA_Client_run_iterate (client, &timedOut);
+    UA_Client_disconnect(client);
+    UA_Client_delete (client);
+
+    return (int) UA_STATUSCODE_GOOD;
+}

+ 73 - 0
examples/client_connectivitycheck_loop.c

@@ -0,0 +1,73 @@
+/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
+
+/* Enable POSIX features */
+#if !defined(_XOPEN_SOURCE) && !defined(_WRS_KERNEL)
+# define _XOPEN_SOURCE 600
+#endif
+#ifndef _DEFAULT_SOURCE
+# define _DEFAULT_SOURCE
+#endif
+/* On older systems we need to define _BSD_SOURCE.
+ * _DEFAULT_SOURCE is an alias for that. */
+#ifndef _BSD_SOURCE
+# define _BSD_SOURCE
+#endif
+
+#include "open62541.h"
+#include <signal.h>
+
+#ifdef _WIN32
+# include <windows.h>
+# define UA_sleep_ms(X) Sleep(X)
+#else
+# include <unistd.h>
+# define UA_sleep_ms(X) usleep(X * 1000)
+#endif
+
+UA_Boolean running = true;
+UA_Logger logger = UA_Log_Stdout;
+
+static void stopHandler(int sign) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Received Ctrl-C");
+    running = 0;
+}
+
+static void
+inactivityCallback (UA_Client *client) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Server Inactivity");
+}
+
+int main(void) {
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
+
+    UA_ClientConfig config = UA_ClientConfig_default;
+    /* Set stateCallback */
+    config.inactivityCallback = inactivityCallback;
+
+    /* Perform a connectivity check every 2 seconds */
+    config.connectivityCheckInterval = 2000;
+
+    UA_Client *client = UA_Client_new(config);
+
+    /* Endless loop runAsync */
+    while (running) {
+        /* if already connected, this will return GOOD and do nothing */
+        /* if the connection is closed/errored, the connection will be reset and then reconnected */
+        /* Alternatively you can also use UA_Client_getState to get the current state */
+        UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_LOG_ERROR(logger, UA_LOGCATEGORY_USERLAND, "Not connected. Retrying to connect in 1 second");
+            /* The connect may timeout after 1 second (see above) or it may fail immediately on network errors */
+            /* E.g. name resolution errors or unreachable network. Thus there should be a small sleep here */
+            UA_sleep_ms(1000);
+            continue;
+        }
+
+        UA_Client_run_iterate(client, 1000);
+    };
+
+    /* Clean up */
+    UA_Client_delete(client); /* Disconnects the client internally */
+    return UA_STATUSCODE_GOOD;
+}

+ 7 - 1
examples/client_subscription_loop.c

@@ -70,6 +70,9 @@ stateCallback (UA_Client *client, UA_ClientState clientState) {
         case UA_CLIENTSTATE_DISCONNECTED:
             UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "The client is disconnected");
         break;
+        case UA_CLIENTSTATE_WAITING_FOR_ACK:
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Waiting for ack");
+        break;
         case UA_CLIENTSTATE_CONNECTED:
             UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "A TCP connection to the server is open");
         break;
@@ -105,6 +108,9 @@ stateCallback (UA_Client *client, UA_ClientState clientState) {
             UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "A session with the server is open (renewed)");
             /* The session was renewed. We don't need to recreate the subscription. */
         break;
+        case UA_CLIENTSTATE_SESSION_DISCONNECTED:
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Session disconnected");
+        break;
     }
     return;
 }
@@ -133,7 +139,7 @@ int main(void) {
             continue;
         }
 
-        UA_Client_runAsync(client, 1000);
+        UA_Client_run_iterate(client, 1000);
     };
 
     /* Clean up */

+ 1 - 0
examples/discovery/client_find_servers.c

@@ -136,6 +136,7 @@ int main(void) {
 
         UA_EndpointDescription *endpointArray = NULL;
         size_t endpointArraySize = 0;
+        //TODO: adapt to the new async getEndpoint
         retval = UA_Client_getEndpoints(client, discoveryUrl, &endpointArraySize, &endpointArray);
         UA_free(discoveryUrl);
         if(retval != UA_STATUSCODE_GOOD) {

+ 1 - 1
examples/tutorial_client_events.c

@@ -158,7 +158,7 @@ int main(int argc, char *argv[]) {
     monId = result.monitoredItemId;
 
     while(running)
-        UA_Client_runAsync(client, 100);
+        UA_Client_run_iterate(client, 100);
 
     /* Delete the subscription */
  cleanup:

+ 52 - 46
include/ua_client.h

@@ -10,7 +10,7 @@
  *    Copyright 2015 (c) Oleksiy Vasylyev
  *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
  *    Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
- *    Copyright 2018 (c) Thomas Stalder
+ *    Copyright 2018 (c) Thomas Stalder, Blue Time Concept SA
  *    Copyright 2018 (c) Kalycito Infotech Private Limited
  */
 
@@ -105,6 +105,11 @@ UA_Client_delete(UA_Client *client);
 UA_StatusCode UA_EXPORT
 UA_Client_connect(UA_Client *client, const char *endpointUrl);
 
+UA_StatusCode UA_EXPORT
+UA_Client_connect_async (UA_Client *client, const char *endpointUrl,
+                         UA_ClientAsyncServiceCallback callback,
+                         void *connected);
+
 /* Connect to the server without creating a session
  *
  * @param client to use
@@ -128,6 +133,9 @@ UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
 UA_StatusCode UA_EXPORT
 UA_Client_disconnect(UA_Client *client);
 
+UA_StatusCode UA_EXPORT
+UA_Client_disconnect_async(UA_Client *client, UA_UInt32 *requestId);
+
 /* Close a connection to the selected server */
 UA_StatusCode UA_EXPORT
 UA_Client_close(UA_Client *client);
@@ -375,13 +383,9 @@ UA_Client_Service_queryNext(UA_Client *client,
 /* Listen on the network and process arriving asynchronous responses in the
  * background. Internal housekeeping and subscription management is done as
  * well. */
-UA_StatusCode UA_EXPORT
-UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout);
 
-typedef void
-(*UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata,
-                                 UA_UInt32 requestId, void *response,
-                                 const UA_DataType *responseType);
+/*UA_StatusCode UA_EXPORT
+UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout);*/
 
 /* Use the type versions of this method. See below. However, the general
  * mechanism of async service calls is explained here.
@@ -395,6 +399,13 @@ typedef void
  * The statusCode received when the client is shutting down is
  * UA_STATUSCODE_BADSHUTDOWN.
  *
+ * The statusCode received when the client don't receive response
+ * after specified config->timeout (in ms) is
+ * UA_STATUSCODE_BADTIMEOUT.
+ *
+ * Instead, you can use __UA_Client_AsyncServiceEx to specify
+ * a custom timeout
+ *
  * The userdata and requestId arguments can be NULL. */
 UA_StatusCode UA_EXPORT
 __UA_Client_AsyncService(UA_Client *client, const void *request,
@@ -403,49 +414,44 @@ __UA_Client_AsyncService(UA_Client *client, const void *request,
                          const UA_DataType *responseType,
                          void *userdata, UA_UInt32 *requestId);
 
-static UA_INLINE UA_StatusCode
-UA_Client_AsyncService_read(UA_Client *client, const UA_ReadRequest *request,
-                            UA_ClientAsyncServiceCallback callback,
-                            void *userdata, UA_UInt32 *requestId) {
-    return __UA_Client_AsyncService(client, (const void*)request,
-                                    &UA_TYPES[UA_TYPES_READREQUEST], callback,
-                                    &UA_TYPES[UA_TYPES_READRESPONSE],
-                                    userdata, requestId);
-}
+/* For async connecting
+ * */
+UA_StatusCode UA_EXPORT
+UA_Client_sendAsyncRequest(UA_Client *client, const void *request,
+        const UA_DataType *requestType, UA_ClientAsyncServiceCallback callback,
+const UA_DataType *responseType, void *userdata, UA_UInt32 *requestId);
 
-static UA_INLINE UA_StatusCode
-UA_Client_AsyncService_write(UA_Client *client, const UA_WriteRequest *request,
-                             UA_ClientAsyncServiceCallback callback,
-                             void *userdata, UA_UInt32 *requestId) {
-    return __UA_Client_AsyncService(client, (const void*)request,
-                                    &UA_TYPES[UA_TYPES_WRITEREQUEST], callback, 
-                                    &UA_TYPES[UA_TYPES_WRITERESPONSE],
-                                    userdata, requestId);
-}
 
-#ifdef UA_ENABLE_METHODCALLS
-    
-static UA_INLINE UA_StatusCode
-UA_Client_AsyncService_call(UA_Client *client, const UA_CallRequest *request,
-                            UA_ClientAsyncServiceCallback callback,
-                            void *userdata, UA_UInt32 *requestId) {
-    return __UA_Client_AsyncService(client, (const void*)request,
-                                    &UA_TYPES[UA_TYPES_CALLREQUEST], callback,
-                                    &UA_TYPES[UA_TYPES_CALLRESPONSE],
-                                    userdata, requestId);
-}
+UA_StatusCode UA_EXPORT
+UA_Client_run_iterate(UA_Client *client, UA_UInt16 timeout);
 
-#endif
+/* Use the type versions of this method. See below. However, the general
+ * mechanism of async service calls is explained here.
+ *
+ * We say that an async service call has been dispatched once this method
+ * returns UA_STATUSCODE_GOOD. If there is an error after an async service has
+ * been dispatched, the callback is called with an "empty" response where the
+ * statusCode has been set accordingly. This is also done if the client is
+ * shutting down and the list of dispatched async services is emptied.
+ *
+ * The statusCode received when the client is shutting down is
+ * UA_STATUSCODE_BADSHUTDOWN.
+ *
+ * The statusCode received when the client don't receive response
+ * after specified timeout (in ms) is
+ * UA_STATUSCODE_BADTIMEOUT.
+ *
+ * The timeout can be disabled by setting timeout to 0
+ *
+ * The userdata and requestId arguments can be NULL. */
+UA_StatusCode UA_EXPORT
+__UA_Client_AsyncServiceEx(UA_Client *client, const void *request,
+                           const UA_DataType *requestType,
+                           UA_ClientAsyncServiceCallback callback,
+                           const UA_DataType *responseType,
+                           void *userdata, UA_UInt32 *requestId,
+                           UA_UInt32 timeout);
 
-static UA_INLINE UA_StatusCode
-UA_Client_AsyncService_browse(UA_Client *client, const UA_BrowseRequest *request,
-                              UA_ClientAsyncServiceCallback callback,
-                              void *userdata, UA_UInt32 *requestId) {
-    return __UA_Client_AsyncService(client, (const void*)request,
-                                    &UA_TYPES[UA_TYPES_BROWSEREQUEST], callback,
-                                    &UA_TYPES[UA_TYPES_BROWSERESPONSE],
-                                    userdata, requestId);
-}
 
 /**
  * .. toctree::

+ 58 - 6
include/ua_client_config.h

@@ -3,6 +3,7 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  *    Copyright 2018 (c) Stefan Profanter, fortiss GmbH
+ *    Copyright 2018 (c) Thomas Stalder, Blue Time Concept SA
  */
 
 #ifndef UA_CLIENT_CONFIG_H
@@ -36,17 +37,37 @@ extern "C" {
  * The :ref:`tutorials` provide a good starting point for this. */
 
 typedef enum {
-    UA_CLIENTSTATE_DISCONNECTED,        /* The client is disconnected */
-    UA_CLIENTSTATE_CONNECTED,           /* A TCP connection to the server is open */
-    UA_CLIENTSTATE_SECURECHANNEL,       /* A SecureChannel to the server is open */
-    UA_CLIENTSTATE_SESSION,             /* A session with the server is open */
-    UA_CLIENTSTATE_SESSION_RENEWED      /* A session with the server is open (renewed) */
+    UA_CLIENTSTATE_DISCONNECTED,         /* The client is disconnected */
+    UA_CLIENTSTATE_WAITING_FOR_ACK,      /* The Client has sent HEL and waiting */
+    UA_CLIENTSTATE_CONNECTED,            /* A TCP connection to the server is open */
+    UA_CLIENTSTATE_SECURECHANNEL,        /* A SecureChannel to the server is open */
+    UA_CLIENTSTATE_SESSION,              /* A session with the server is open */
+    UA_CLIENTSTATE_SESSION_DISCONNECTED, /* Disconnected vs renewed? */
+    UA_CLIENTSTATE_SESSION_RENEWED       /* A session with the server is open (renewed) */
 } UA_ClientState;
 
 
 struct UA_Client;
 typedef struct UA_Client UA_Client;
 
+typedef void (*UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata,
+        UA_UInt32 requestId, void *response);
+/*
+ * Repeated Callbacks
+ * ------------------ */
+typedef UA_StatusCode (*UA_ClientCallback)(UA_Client *client, void *data);
+
+UA_StatusCode
+UA_Client_addRepeatedCallback(UA_Client *Client, UA_ClientCallback callback,
+        void *data, UA_UInt32 interval, UA_UInt64 *callbackId);
+
+UA_StatusCode
+UA_Client_changeRepeatedCallbackInterval(UA_Client *Client,
+        UA_UInt64 callbackId, UA_UInt32 interval);
+
+UA_StatusCode UA_Client_removeRepeatedCallback(UA_Client *Client,
+        UA_UInt64 callbackId);
+
 /**
  * Client Lifecycle callback
  * ^^^^^^^^^^^^^^^^^^^^^^^^^ */
@@ -61,17 +82,25 @@ typedef void (*UA_ClientStateCallback)(UA_Client *client, UA_ClientState clientS
 typedef void (*UA_SubscriptionInactivityCallback)(UA_Client *client, UA_UInt32 subscriptionId, void *subContext);
 #endif
 
+/**
+ * Inactivity callback
+ * ^^^^^^^^^^^^^^^^^^^ */
+
+typedef void (*UA_InactivityCallback)(UA_Client *client);
+
 /**
  * Client Configuration Data
  * ^^^^^^^^^^^^^^^^^^^^^^^^^ */
 
 typedef struct UA_ClientConfig {
-    UA_UInt32 timeout;               /* Sync response timeout in ms */
+    UA_UInt32 timeout;               /* ASync + Sync response timeout in ms */
     UA_UInt32 secureChannelLifeTime; /* Lifetime in ms (then the channel needs
                                         to be renewed) */
     UA_Logger logger;
     UA_ConnectionConfig localConnectionConfig;
     UA_ConnectClientConnection connectionFunc;
+    UA_ConnectClientConnection initConnectionFunc;
+    UA_ClientCallback pollConnectionFunc;
 
     /* Custom DataTypes */
     size_t customDataTypesSize;
@@ -80,9 +109,28 @@ typedef struct UA_ClientConfig {
     /* Callback function */
     UA_ClientStateCallback stateCallback;
 #ifdef UA_ENABLE_SUBSCRIPTIONS
+    /**
+     * When outStandingPublishRequests is greater than 0,
+     * the server automatically create publishRequest when
+     * UA_Client_runAsync is called. If the client don't receive
+     * a publishResponse after :
+     *     (sub->publishingInterval * sub->maxKeepAliveCount) +
+     *     client->config.timeout)
+     * then, the client call subscriptionInactivityCallback
+     * The connection can be closed, this in an attempt to
+     * recreate a healthy connection. */
     UA_SubscriptionInactivityCallback subscriptionInactivityCallback;
 #endif
 
+    /** 
+     * When connectivityCheckInterval is greater than 0,
+     * every connectivityCheckInterval (in ms), a async read request
+     * is performed on the server. inactivityCallback is called
+     * when the client receive no response for this read request
+     * The connection can be closed, this in an attempt to
+     * recreate a healthy connection. */
+    UA_InactivityCallback inactivityCallback;
+
     void *clientContext;
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
@@ -90,6 +138,10 @@ typedef struct UA_ClientConfig {
     /* 0 = background task disabled                    */
     UA_UInt16 outStandingPublishRequests;
 #endif
+   /**
+     * connectivity check interval in ms
+     * 0 = background task disabled */
+    UA_UInt32 connectivityCheckInterval;
 } UA_ClientConfig;
 
 #ifdef __cplusplus

+ 628 - 0
include/ua_client_highlevel_async.h

@@ -0,0 +1,628 @@
+/* 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/.
+ *
+ */
+
+#ifndef UA_CLIENT_HIGHLEVEL_ASYNC_H_
+#define UA_CLIENT_HIGHLEVEL_ASYNC_H_
+#include "ua_client.h"
+
+/*Raw Services
+ * ^^^^^^^^^^^^^^ */
+typedef void (*UA_ClientAsyncReadCallback)(UA_Client *client, void *userdata,
+		UA_UInt32 requestId, UA_ReadResponse *rr);
+static UA_INLINE UA_StatusCode UA_Client_sendAsyncReadRequest(UA_Client *client,
+		UA_ReadRequest *request, UA_ClientAsyncReadCallback readCallback,
+		void *userdata, UA_UInt32 *reqId) {
+	return UA_Client_sendAsyncRequest(client, request,
+			&UA_TYPES[UA_TYPES_READREQUEST],
+			(UA_ClientAsyncServiceCallback) readCallback,
+			&UA_TYPES[UA_TYPES_READRESPONSE], userdata, reqId);
+}
+
+typedef void (*UA_ClientAsyncWriteCallback)(UA_Client *client, void *userdata,
+		UA_UInt32 requestId, UA_WriteResponse *wr);
+static UA_INLINE UA_StatusCode UA_Client_sendAsyncWriteRequest(
+		UA_Client *client, UA_WriteRequest *request,
+		UA_ClientAsyncWriteCallback writeCallback, void *userdata,
+		UA_UInt32 *reqId) {
+	return UA_Client_sendAsyncRequest(client, request,
+			&UA_TYPES[UA_TYPES_WRITEREQUEST],
+			(UA_ClientAsyncServiceCallback) writeCallback,
+			&UA_TYPES[UA_TYPES_WRITERESPONSE], userdata, reqId);
+}
+
+typedef void (*UA_ClientAsyncBrowseCallback)(UA_Client *client, void *userdata,
+		UA_UInt32 requestId, UA_BrowseResponse *wr);
+static UA_INLINE UA_StatusCode UA_Client_sendAsyncBrowseRequest(
+		UA_Client *client, UA_BrowseRequest *request,
+		UA_ClientAsyncBrowseCallback browseCallback, void *userdata,
+		UA_UInt32 *reqId) {
+	return UA_Client_sendAsyncRequest(client, request,
+			&UA_TYPES[UA_TYPES_BROWSEREQUEST],
+			(UA_ClientAsyncServiceCallback) browseCallback,
+			&UA_TYPES[UA_TYPES_BROWSERESPONSE], userdata, reqId);
+}
+
+/**
+ * Read Attribute
+ * ^^^^^^^^^^^^^^ */
+UA_StatusCode UA_EXPORT
+__UA_Client_readAttribute_async(UA_Client *client, const UA_NodeId *nodeId,
+		UA_AttributeId attributeId, const UA_DataType *outDataType,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId);
+
+typedef void (*UA_ClientAsyncReadDataTypeAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_NodeId *var);
+static UA_INLINE UA_StatusCode UA_Client_readDataTypeAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadDataTypeAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_DATATYPE, &UA_TYPES[UA_TYPES_NODEID],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+typedef void (*UA_ClientAsyncReadValueAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_Variant *var);
+static UA_INLINE UA_StatusCode UA_Client_readValueAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadValueAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_VALUE, &UA_TYPES[UA_TYPES_VARIANT],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+typedef void (*UA_ClientAsyncReadNodeIdAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_NodeId *out);
+static UA_INLINE UA_StatusCode UA_Client_readNodeIdAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadNodeIdAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_NODEID, &UA_TYPES[UA_TYPES_NODEID],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadNodeClassAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_NodeClass *out);
+static UA_INLINE UA_StatusCode UA_Client_readNodeClassAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadNodeClassAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_NODECLASS, &UA_TYPES[UA_TYPES_NODECLASS],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadBrowseNameAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_QualifiedName *out);
+static UA_INLINE UA_StatusCode UA_Client_readBrowseNameAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadBrowseNameAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_BROWSENAME, &UA_TYPES[UA_TYPES_QUALIFIEDNAME],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadDisplayNameAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId,
+		UA_LocalizedText *out);
+static UA_INLINE UA_StatusCode UA_Client_readDisplayNameAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadDisplayNameAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_DISPLAYNAME, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadDescriptionAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId,
+		UA_LocalizedText *out);
+static UA_INLINE UA_StatusCode UA_Client_readDescriptionAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadDescriptionAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_DESCRIPTION, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadWriteMaskAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_UInt32 *out);
+static UA_INLINE UA_StatusCode UA_Client_readWriteMaskAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadWriteMaskAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_WRITEMASK, &UA_TYPES[UA_TYPES_UINT32],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadUserWriteMaskAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId, UA_UInt32 *out);
+static UA_INLINE UA_StatusCode UA_Client_readUserWriteMaskAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadUserWriteMaskAttributeCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_USERWRITEMASK, &UA_TYPES[UA_TYPES_UINT32],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadIsAbstractAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_Boolean *out);
+static UA_INLINE UA_StatusCode UA_Client_readIsAbstractAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadIsAbstractAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_ISABSTRACT, &UA_TYPES[UA_TYPES_BOOLEAN],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadSymmetricAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_Boolean *out);
+static UA_INLINE UA_StatusCode UA_Client_readSymmetricAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadSymmetricAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_SYMMETRIC, &UA_TYPES[UA_TYPES_BOOLEAN],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadInverseNameAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId,
+		UA_LocalizedText *out);
+static UA_INLINE UA_StatusCode UA_Client_readInverseNameAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadInverseNameAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_INVERSENAME, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadContainsNoLoopsAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId,
+		UA_Boolean *out);
+static UA_INLINE UA_StatusCode UA_Client_readContainsNoLoopsAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadContainsNoLoopsAttributeCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_CONTAINSNOLOOPS, &UA_TYPES[UA_TYPES_BOOLEAN],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadEventNotifierAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId, UA_Byte *out);
+static UA_INLINE UA_StatusCode UA_Client_readEventNotifierAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadEventNotifierAttributeCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_EVENTNOTIFIER, &UA_TYPES[UA_TYPES_BYTE],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadValueRankAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_Int32 *out);
+static UA_INLINE UA_StatusCode UA_Client_readValueRankAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadValueRankAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_VALUERANK, &UA_TYPES[UA_TYPES_INT32],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadAccessLevelAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId, UA_Byte *out);
+static UA_INLINE UA_StatusCode UA_Client_readAccessLevelAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadAccessLevelAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_ACCESSLEVEL, &UA_TYPES[UA_TYPES_BYTE],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadUserAccessLevelAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId, UA_Byte *out);
+static UA_INLINE UA_StatusCode UA_Client_readUserAccessLevelAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadUserAccessLevelAttributeCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_USERACCESSLEVEL, &UA_TYPES[UA_TYPES_BYTE],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadMinimumSamplingIntervalAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId, UA_Double *out);
+static UA_INLINE UA_StatusCode UA_Client_readMinimumSamplingIntervalAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadMinimumSamplingIntervalAttributeCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, &UA_TYPES[UA_TYPES_DOUBLE],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadHistorizingAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId,
+		UA_Boolean *out);
+static UA_INLINE UA_StatusCode UA_Client_readHistorizingAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadHistorizingAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_HISTORIZING, &UA_TYPES[UA_TYPES_BOOLEAN],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadExecutableAttributeCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_Boolean *out);
+static UA_INLINE UA_StatusCode UA_Client_readExecutableAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadExecutableAttributeCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_EXECUTABLE, &UA_TYPES[UA_TYPES_BOOLEAN],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+typedef void (*UA_ClientAsyncReadUserExecutableAttributeCallback)(
+		UA_Client *client, void *userdata, UA_UInt32 requestId,
+		UA_Boolean *out);
+static UA_INLINE UA_StatusCode UA_Client_readUserExecutableAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		UA_ClientAsyncReadUserExecutableAttributeCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_readAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_USEREXECUTABLE, &UA_TYPES[UA_TYPES_BOOLEAN],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+/**
+ * Write Attribute
+ * ^^^^^^^^^^^^^^ */
+
+UA_StatusCode UA_EXPORT
+__UA_Client_writeAttribute_async(UA_Client *client, const UA_NodeId *nodeId,
+		UA_AttributeId attributeId, const void *in,
+		const UA_DataType *inDataType, UA_ClientAsyncServiceCallback callback,
+		void *userdata, UA_UInt32 *reqId);
+
+static UA_INLINE UA_StatusCode UA_Client_writeValueAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId, const UA_Variant *newValue,
+		UA_ClientAsyncWriteCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_VALUE, newValue, &UA_TYPES[UA_TYPES_VARIANT],
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+static UA_INLINE UA_StatusCode UA_Client_writeNodeIdAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId, const UA_NodeId *outNodeId,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_NODEID, outNodeId, &UA_TYPES[UA_TYPES_NODEID],
+			callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeNodeClassAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_NodeClass *outNodeClass,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_NODECLASS, outNodeClass,
+			&UA_TYPES[UA_TYPES_NODECLASS], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeBrowseNameAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_QualifiedName *outBrowseName,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_BROWSENAME, outBrowseName,
+			&UA_TYPES[UA_TYPES_QUALIFIEDNAME], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeDisplayNameAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_LocalizedText *outDisplayName,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_DISPLAYNAME, outDisplayName,
+			&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeDescriptionAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_LocalizedText *outDescription,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_DESCRIPTION, outDescription,
+			&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeWriteMaskAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_UInt32 *outWriteMask, UA_ClientAsyncServiceCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_WRITEMASK, outWriteMask, &UA_TYPES[UA_TYPES_UINT32],
+			callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeUserWriteMaskAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_UInt32 *outUserWriteMask,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_USERWRITEMASK, outUserWriteMask,
+			&UA_TYPES[UA_TYPES_UINT32], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeIsAbstractAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Boolean *outIsAbstract, UA_ClientAsyncServiceCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_ISABSTRACT, outIsAbstract,
+			&UA_TYPES[UA_TYPES_BOOLEAN], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeSymmetricAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Boolean *outSymmetric, UA_ClientAsyncServiceCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_SYMMETRIC, outSymmetric, &UA_TYPES[UA_TYPES_BOOLEAN],
+			callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeInverseNameAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_LocalizedText *outInverseName,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_INVERSENAME, outInverseName,
+			&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeContainsNoLoopsAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Boolean *outContainsNoLoops,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_CONTAINSNOLOOPS, outContainsNoLoops,
+			&UA_TYPES[UA_TYPES_BOOLEAN], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeEventNotifierAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Byte *outEventNotifier, UA_ClientAsyncServiceCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_EVENTNOTIFIER, outEventNotifier,
+			&UA_TYPES[UA_TYPES_BYTE], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeDataTypeAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId, const UA_NodeId *outDataType,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_DATATYPE, outDataType, &UA_TYPES[UA_TYPES_NODEID],
+			callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeValueRankAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId, const UA_Int32 *outValueRank,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_VALUERANK, outValueRank, &UA_TYPES[UA_TYPES_INT32],
+			callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeAccessLevelAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Byte *outAccessLevel, UA_ClientAsyncServiceCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_ACCESSLEVEL, outAccessLevel,
+			&UA_TYPES[UA_TYPES_BYTE], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeUserAccessLevelAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Byte *outUserAccessLevel,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_USERACCESSLEVEL, outUserAccessLevel,
+			&UA_TYPES[UA_TYPES_BYTE], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeMinimumSamplingIntervalAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Double *outMinimumSamplingInterval,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, outMinimumSamplingInterval,
+			&UA_TYPES[UA_TYPES_DOUBLE], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeHistorizingAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Boolean *outHistorizing,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_HISTORIZING, outHistorizing,
+			&UA_TYPES[UA_TYPES_BOOLEAN], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeExecutableAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Boolean *outExecutable, UA_ClientAsyncServiceCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_EXECUTABLE, outExecutable,
+			&UA_TYPES[UA_TYPES_BOOLEAN], callback, userdata, reqId);
+}
+static UA_INLINE UA_StatusCode UA_Client_writeUserExecutableAttribute_async(
+		UA_Client *client, const UA_NodeId nodeId,
+		const UA_Boolean *outUserExecutable,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_writeAttribute_async(client, &nodeId,
+			UA_ATTRIBUTEID_USEREXECUTABLE, outUserExecutable,
+			&UA_TYPES[UA_TYPES_BOOLEAN], callback, userdata, reqId);
+}
+
+/**
+ * Method Calling
+ * ^^^^^^^^^^^^^^ */
+
+UA_StatusCode UA_EXPORT __UA_Client_call_async(UA_Client *client,
+		const UA_NodeId objectId, const UA_NodeId methodId, size_t inputSize,
+		const UA_Variant *input, UA_ClientAsyncServiceCallback callback,
+		void *userdata, UA_UInt32 *reqId);
+
+typedef void (*UA_ClientAsyncCallCallback)(UA_Client *client, void *userdata,
+		UA_UInt32 requestId, UA_CallResponse *cr);
+static UA_INLINE UA_StatusCode UA_Client_call_async(UA_Client *client,
+		const UA_NodeId objectId, const UA_NodeId methodId, size_t inputSize,
+		const UA_Variant *input, UA_ClientAsyncCallCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+
+	return __UA_Client_call_async(client, objectId, methodId, inputSize, input,
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+/*Node Management
+ * ^^^^^^^^^^^^^*/
+typedef void (*UA_ClientAsyncAddNodesCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId, UA_AddNodesResponse *ar);
+
+UA_StatusCode UA_EXPORT
+__UA_Client_addNode_async(UA_Client *client, const UA_NodeClass nodeClass,
+		const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
+		const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
+		const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
+		const UA_DataType *attributeType, UA_NodeId *outNewNodeId,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId);
+
+static UA_INLINE UA_StatusCode UA_Client_addVariableNode_async(
+		UA_Client *client, const UA_NodeId requestedNewNodeId,
+		const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+		const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+		const UA_VariableAttributes attr, UA_NodeId *outNewNodeId,
+		UA_ClientAsyncAddNodesCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_addNode_async(client, UA_NODECLASS_VARIABLE,
+			requestedNewNodeId, parentNodeId, referenceTypeId, browseName,
+			typeDefinition, (const UA_NodeAttributes*) &attr,
+			&UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], outNewNodeId,
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+static UA_INLINE UA_StatusCode UA_Client_addVariableTypeNode_async(
+		UA_Client *client, const UA_NodeId requestedNewNodeId,
+		const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+		const UA_QualifiedName browseName, const UA_VariableTypeAttributes attr,
+		UA_NodeId *outNewNodeId, UA_ClientAsyncAddNodesCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_addNode_async(client, UA_NODECLASS_VARIABLETYPE,
+			requestedNewNodeId, parentNodeId, referenceTypeId, browseName,
+			UA_NODEID_NULL, (const UA_NodeAttributes*) &attr,
+			&UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES], outNewNodeId,
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+static UA_INLINE UA_StatusCode UA_Client_addObjectNode_async(UA_Client *client,
+		const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
+		const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
+		const UA_NodeId typeDefinition, const UA_ObjectAttributes attr,
+		UA_NodeId *outNewNodeId, UA_ClientAsyncAddNodesCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_addNode_async(client, UA_NODECLASS_OBJECT,
+			requestedNewNodeId, parentNodeId, referenceTypeId, browseName,
+			typeDefinition, (const UA_NodeAttributes*) &attr,
+			&UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], outNewNodeId,
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+static UA_INLINE UA_StatusCode UA_Client_addObjectTypeNode_async(
+		UA_Client *client, const UA_NodeId requestedNewNodeId,
+		const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+		const UA_QualifiedName browseName, const UA_ObjectTypeAttributes attr,
+		UA_NodeId *outNewNodeId, UA_ClientAsyncAddNodesCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_addNode_async(client, UA_NODECLASS_OBJECTTYPE,
+			requestedNewNodeId, parentNodeId, referenceTypeId, browseName,
+			UA_NODEID_NULL, (const UA_NodeAttributes*) &attr,
+			&UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES], outNewNodeId,
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+static UA_INLINE UA_StatusCode UA_Client_addViewNode_async(UA_Client *client,
+		const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
+		const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
+		const UA_ViewAttributes attr, UA_NodeId *outNewNodeId,
+		UA_ClientAsyncAddNodesCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_addNode_async(client, UA_NODECLASS_VIEW,
+			requestedNewNodeId, parentNodeId, referenceTypeId, browseName,
+			UA_NODEID_NULL, (const UA_NodeAttributes*) &attr,
+			&UA_TYPES[UA_TYPES_VIEWATTRIBUTES], outNewNodeId,
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+static UA_INLINE UA_StatusCode UA_Client_addReferenceTypeNode_async(
+		UA_Client *client, const UA_NodeId requestedNewNodeId,
+		const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+		const UA_QualifiedName browseName,
+		const UA_ReferenceTypeAttributes attr, UA_NodeId *outNewNodeId,
+		UA_ClientAsyncAddNodesCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_addNode_async(client, UA_NODECLASS_REFERENCETYPE,
+			requestedNewNodeId, parentNodeId, referenceTypeId, browseName,
+			UA_NODEID_NULL, (const UA_NodeAttributes*) &attr,
+			&UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES], outNewNodeId,
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+static UA_INLINE UA_StatusCode UA_Client_addDataTypeNode_async(
+		UA_Client *client, const UA_NodeId requestedNewNodeId,
+		const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+		const UA_QualifiedName browseName, const UA_DataTypeAttributes attr,
+		UA_NodeId *outNewNodeId, UA_ClientAsyncAddNodesCallback callback,
+		void *userdata, UA_UInt32 *reqId) {
+	return __UA_Client_addNode_async(client, UA_NODECLASS_DATATYPE,
+			requestedNewNodeId, parentNodeId, referenceTypeId, browseName,
+			UA_NODEID_NULL, (const UA_NodeAttributes*) &attr,
+			&UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES], outNewNodeId,
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+static UA_INLINE UA_StatusCode UA_Client_addMethodNode_async(UA_Client *client,
+		const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
+		const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
+		const UA_MethodAttributes attr, UA_NodeId *outNewNodeId,
+		UA_ClientAsyncAddNodesCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_addNode_async(client, UA_NODECLASS_METHOD,
+			requestedNewNodeId, parentNodeId, referenceTypeId, browseName,
+			UA_NODEID_NULL, (const UA_NodeAttributes*) &attr,
+			&UA_TYPES[UA_TYPES_METHODATTRIBUTES], outNewNodeId,
+			(UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+/**
+ * Misc Functionalities
+ * ^^^^^^^^^^^^^^ */
+
+UA_StatusCode UA_EXPORT __UA_Client_translateBrowsePathsToNodeIds_async(
+		UA_Client *client, char *paths[], UA_UInt32 ids[], size_t pathSize,
+		UA_ClientAsyncServiceCallback callback, void *userdata,
+		UA_UInt32 *reqId);
+
+typedef void (*UA_ClientAsyncTranslateCallback)(UA_Client *client,
+		void *userdata, UA_UInt32 requestId,
+		UA_TranslateBrowsePathsToNodeIdsResponse *tr);
+static UA_INLINE UA_StatusCode UA_Cient_translateBrowsePathsToNodeIds_async(
+		UA_Client *client, char **paths, UA_UInt32 *ids, size_t pathSize,
+		UA_ClientAsyncTranslateCallback callback, void *userdata,
+		UA_UInt32 *reqId) {
+	return __UA_Client_translateBrowsePathsToNodeIds_async(client, paths, ids,
+			pathSize, (UA_ClientAsyncServiceCallback) callback, userdata, reqId);
+}
+
+
+#endif /* UA_CLIENT_HIGHLEVEL_ASYNC_H_ */

+ 2 - 1
include/ua_plugin_network.h

@@ -57,6 +57,7 @@ typedef enum {
                                 * is not done */
     UA_CONNECTION_ESTABLISHED  /* The socket is open and the connection
                                 * configured */
+
 } UA_ConnectionState;
 
 struct UA_Connection {
@@ -72,7 +73,7 @@ struct UA_Connection {
     void *handle;                    /* A pointer to internal data */
     UA_ByteString incompleteMessage; /* A half-received message (TCP is a
                                       * streaming protocol) is stored here */
-
+    UA_UInt64 connectCallbackID;     /* Callback Id, for the connect-loop */
     /* Get a buffer for sending */
     UA_StatusCode (*getSendBuffer)(UA_Connection *connection, size_t length,
                                    UA_ByteString *buf);

+ 1 - 1
include/ua_types.h

@@ -10,7 +10,7 @@
  *    Copyright 2015 (c) Nick Goossens
  *    Copyright 2015-2016 (c) Oleksiy Vasylyev
  *    Copyright 2017 (c) Stefan Profanter, fortiss GmbH
- *    Copyright 2017 (c) Thomas Stalder
+ *    Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA
  */
 
 #ifndef UA_TYPES_H_

+ 10 - 9
plugins/ua_config_default.c

@@ -637,19 +637,20 @@ const UA_ClientConfig UA_ClientConfig_default = {
         0, /* .maxMessageSize, 0 -> unlimited */
         0 /* .maxChunkCount, 0 -> unlimited */
     },
-    UA_ClientConnectionTCP, /* .connectionFunc */
-
+    UA_ClientConnectionTCP, /* .connectionFunc (for sync connection) */
+    UA_ClientConnectionTCP_init, /* .initConnectionFunc (for async client) */
+    UA_ClientConnectionTCP_poll, /* .pollConnectionFunc (for async connection) */
     0, /* .customDataTypesSize */
-    NULL, /*.customDataTypes */
+    NULL, /* .customDataTypes */
 
-    NULL, /*.stateCallback */
+    NULL, /* .stateCallback */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
-    NULL, /*.subscriptionInactivityCallback */
+    NULL, /* .subscriptionInactivityCallback */
 #endif
-
-    NULL,  /*.clientContext */
-
+    NULL, /* .inactivityCallback */
+    NULL, /* .clientContext */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
-    10 /* .outStandingPublishRequests */
+    10, /* .outStandingPublishRequests */
 #endif
+    0 /* .connectivityCheckInterval */
 };

+ 218 - 2
plugins/ua_network_tcp.c

@@ -1,5 +1,5 @@
 /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
- * See http://creativecommons.org/publicdomain/zero/1.0/ for more information. 
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  *
  *    Copyright 2016-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
  *    Copyright 2016-2017 (c) Stefan Profanter, fortiss GmbH
@@ -760,6 +760,13 @@ UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port, UA_Logger log
     return nl;
 }
 
+typedef struct TCPClientConnection {
+	struct addrinfo hints, *server;
+	UA_DateTime connStart;
+	char* endpointURL;
+	UA_UInt32 timeout;
+} TCPClientConnection;
+
 /***************************/
 /* Client NetworkLayer TCP */
 /***************************/
@@ -773,6 +780,214 @@ ClientNetworkLayerTCP_close(UA_Connection *connection) {
     connection->state = UA_CONNECTION_CLOSED;
 }
 
+static void
+ClientNetworkLayerTCP_free(UA_Connection *connection) {
+    if (connection->handle){
+        TCPClientConnection *tcpConnection = (TCPClientConnection *)connection->handle;
+        if(tcpConnection->server)
+            freeaddrinfo(tcpConnection->server);
+        free(tcpConnection);
+    }
+}
+
+UA_StatusCode UA_ClientConnectionTCP_poll(UA_Client *client, void *data) {
+    UA_Connection *connection = (UA_Connection*) data;
+
+    if (connection->state == UA_CONNECTION_CLOSED)
+        return UA_STATUSCODE_BADDISCONNECT;
+
+    TCPClientConnection *tcpConnection =
+                    (TCPClientConnection*) connection->handle;
+
+    UA_DateTime connStart = UA_DateTime_nowMonotonic();
+    SOCKET clientsockfd;
+
+    if (connection->state == UA_CONNECTION_ESTABLISHED) {
+            UA_Client_removeRepeatedCallback(client, connection->connectCallbackID);
+            connection->connectCallbackID = 0;
+            return UA_STATUSCODE_GOOD;
+    }
+    if ((UA_Double) (UA_DateTime_nowMonotonic() - tcpConnection->connStart)
+                    > tcpConnection->timeout* UA_DATETIME_MSEC ) {
+            // connection timeout
+            ClientNetworkLayerTCP_close(connection);
+            UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+                            "Timed out");
+            return UA_STATUSCODE_BADDISCONNECT;
+
+    }
+    /* On linux connect may immediately return with ECONNREFUSED but we still want to try to connect */
+    /* Thus use a loop and retry until timeout is reached */
+
+    /* Get a socket */
+    clientsockfd = socket(tcpConnection->server->ai_family,
+                    tcpConnection->server->ai_socktype,
+                    tcpConnection->server->ai_protocol);
+#ifdef _WIN32
+    if(clientsockfd == INVALID_SOCKET) {
+#else
+    if (connection->sockfd < 0) {
+#endif
+            UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+                            "Could not create client socket: %s", strerror(errno__));
+            ClientNetworkLayerTCP_close(connection);
+            return UA_STATUSCODE_BADDISCONNECT;
+    }
+
+    /* Non blocking connect to be able to timeout */
+    if (socket_set_nonblocking(clientsockfd) != UA_STATUSCODE_GOOD) {
+            UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+                            "Could not set the client socket to nonblocking");
+            ClientNetworkLayerTCP_close(connection);
+            return UA_STATUSCODE_BADDISCONNECT;
+    }
+
+    /* Non blocking connect */
+    int error = connect(clientsockfd, tcpConnection->server->ai_addr,
+                    WIN32_INT tcpConnection->server->ai_addrlen);
+
+    if ((error == -1) && (errno__ != ERR_CONNECTION_PROGRESS)) {
+            ClientNetworkLayerTCP_close(connection);
+            UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+                            "Connection to  failed with error: %s", strerror(errno__));
+            return UA_STATUSCODE_BADDISCONNECT;
+    }
+
+    /* Use select to wait and check if connected */
+    if (error == -1 && (errno__ == ERR_CONNECTION_PROGRESS)) {
+        /* connection in progress. Wait until connected using select */
+
+        UA_UInt32 timeSinceStart =
+                        (UA_UInt32) ((UA_Double) (UA_DateTime_nowMonotonic() - connStart)
+                                        * UA_DATETIME_MSEC);
+
+        fd_set fdset;
+        FD_ZERO(&fdset);
+        UA_fd_set(clientsockfd, &fdset);
+        UA_UInt32 timeout_usec = (tcpConnection->timeout - timeSinceStart)
+                        * 1000;
+        struct timeval tmptv = { (long int) (timeout_usec / 1000000),
+                        (long int) (timeout_usec % 1000000) };
+
+        int resultsize = select((UA_Int32) (clientsockfd + 1), NULL, &fdset,
+        NULL, &tmptv);
+
+        if (resultsize == 1) {
+            /* Windows does not have any getsockopt equivalent and it is not needed there */
+#ifdef _WIN32
+            connection->sockfd = clientsockfd;
+            connection->state = UA_CONNECTION_ESTABLISHED;
+            return UA_STATUSCODE_GOOD;
+#else
+            OPTVAL_TYPE so_error;
+            socklen_t len = sizeof so_error;
+
+            int ret = getsockopt(clientsockfd, SOL_SOCKET, SO_ERROR, &so_error,
+                            &len);
+
+            if (ret != 0 || so_error != 0) {
+                /* on connection refused we should still try to connect */
+                /* connection refused happens on localhost or local ip without timeout */
+                if (so_error != ECONNREFUSED) {
+                        // general error
+                        ClientNetworkLayerTCP_close(connection);
+                        UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+                                        "Connection to failed with error: %s",
+                                        strerror(ret == 0 ? so_error : errno__));
+                        return UA_STATUSCODE_BADDISCONNECT;
+                }
+                /* wait until we try a again. Do not make this too small, otherwise the
+                 * timeout is somehow wrong */
+
+        } else {
+                connection->sockfd = clientsockfd;
+                connection->state = UA_CONNECTION_ESTABLISHED;
+                return UA_STATUSCODE_GOOD;
+            }
+#endif
+        }
+    } else {
+        connection->state = UA_CONNECTION_ESTABLISHED;
+        return UA_STATUSCODE_GOOD;
+    }
+
+#ifdef SO_NOSIGPIPE
+    int val = 1;
+    int sso_result = setsockopt(connection->sockfd, SOL_SOCKET,
+                    SO_NOSIGPIPE, (void*)&val, sizeof(val));
+    if(sso_result < 0)
+    UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_NETWORK,
+                    "Couldn't set SO_NOSIGPIPE");
+#endif
+
+    return UA_STATUSCODE_GOOD;
+
+}
+
+UA_Connection UA_ClientConnectionTCP_init(UA_ConnectionConfig conf,
+		const char *endpointUrl, const UA_UInt32 timeout,
+                UA_Logger logger) {
+    UA_Connection connection;
+    memset(&connection, 0, sizeof(UA_Connection));
+
+    connection.state = UA_CONNECTION_OPENING;
+    connection.localConf = conf;
+    connection.remoteConf = conf;
+    connection.send = connection_write;
+    connection.recv = connection_recv;
+    connection.close = ClientNetworkLayerTCP_close;
+    connection.free = ClientNetworkLayerTCP_free;
+    connection.getSendBuffer = connection_getsendbuffer;
+    connection.releaseSendBuffer = connection_releasesendbuffer;
+    connection.releaseRecvBuffer = connection_releaserecvbuffer;
+
+    TCPClientConnection *tcpClientConnection = (TCPClientConnection*) malloc(
+                    sizeof(TCPClientConnection));
+    connection.handle = (void*) tcpClientConnection;
+    tcpClientConnection->timeout = timeout;
+    UA_String endpointUrlString = UA_STRING((char*) (uintptr_t) endpointUrl);
+    UA_String hostnameString = UA_STRING_NULL;
+    UA_String pathString = UA_STRING_NULL;
+    UA_UInt16 port = 0;
+    char hostname[512];
+    tcpClientConnection->connStart = UA_DateTime_nowMonotonic();
+
+    UA_StatusCode parse_retval = UA_parseEndpointUrl(&endpointUrlString,
+                    &hostnameString, &port, &pathString);
+    if (parse_retval != UA_STATUSCODE_GOOD || hostnameString.length > 511) {
+            UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                            "Server url is invalid: %s", endpointUrl);
+            return connection;
+    }
+    memcpy(hostname, hostnameString.data, hostnameString.length);
+    hostname[hostnameString.length] = 0;
+
+    if (port == 0) {
+            port = 4840;
+            UA_LOG_INFO(logger, UA_LOGCATEGORY_NETWORK,
+                            "No port defined, using default port %d", port);
+    }
+
+    memset(&tcpClientConnection->hints, 0, sizeof(tcpClientConnection->hints));
+    tcpClientConnection->hints.ai_family = AF_UNSPEC;
+    tcpClientConnection->hints.ai_socktype = SOCK_STREAM;
+    char portStr[6];
+#ifndef _MSC_VER
+    snprintf(portStr, 6, "%d", port);
+#else
+    _snprintf_s(portStr, 6, _TRUNCATE, "%d", port);
+#endif
+    int error = getaddrinfo(hostname, portStr, &tcpClientConnection->hints,
+                    &tcpClientConnection->server);
+    if (error != 0 || !tcpClientConnection->server) {
+            UA_LOG_WARNING(logger, UA_LOGCATEGORY_NETWORK,
+                            "DNS lookup of %s failed with error %s", hostname,
+                            gai_strerror(error));
+            return connection;
+    }
+    return connection;
+}
+
 UA_Connection
 UA_ClientConnectionTCP(UA_ConnectionConfig conf,
                        const char *endpointUrl, const UA_UInt32 timeout,
@@ -796,10 +1011,11 @@ UA_ClientConnectionTCP(UA_ConnectionConfig conf,
     connection.send = connection_write;
     connection.recv = connection_recv;
     connection.close = ClientNetworkLayerTCP_close;
-    connection.free = NULL;
+    connection.free = ClientNetworkLayerTCP_free;
     connection.getSendBuffer = connection_getsendbuffer;
     connection.releaseSendBuffer = connection_releasesendbuffer;
     connection.releaseRecvBuffer = connection_releaserecvbuffer;
+    connection.handle = NULL;
 
     UA_String endpointUrlString = UA_STRING((char*)(uintptr_t)endpointUrl);
     UA_String hostnameString = UA_STRING_NULL;

+ 5 - 1
plugins/ua_network_tcp.h

@@ -19,8 +19,12 @@ UA_ServerNetworkLayer UA_EXPORT
 UA_ServerNetworkLayerTCP(UA_ConnectionConfig conf, UA_UInt16 port, UA_Logger logger);
 
 UA_Connection UA_EXPORT
-UA_ClientConnectionTCP(UA_ConnectionConfig conf, const char *endpointUrl, const UA_UInt32 timeout, UA_Logger logger);
+UA_ClientConnectionTCP(UA_ConnectionConfig conf, const char *endpointUrl, const UA_UInt32 timeout,
+                       UA_Logger logger);
 
+UA_StatusCode UA_ClientConnectionTCP_poll(UA_Client *client, void *data);
+UA_Connection UA_EXPORT UA_ClientConnectionTCP_init(UA_ConnectionConfig conf,
+                const char *endpointUrl, const UA_UInt32 timeout, UA_Logger logger);
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 110 - 26
src/client/ua_client.c

@@ -43,6 +43,15 @@ UA_Client_init(UA_Client* client, UA_ClientConfig config) {
     client->config = config;
     if(client->config.stateCallback)
         client->config.stateCallback(client, client->state);
+    /* Catch error during async connection */
+    client->connectStatus = UA_STATUSCODE_GOOD;
+
+    /* Needed by async client */
+    UA_Timer_init(&client->timer);
+
+#ifndef UA_ENABLE_MULTITHREADING
+    SLIST_INIT(&client->delayedClientCallbacks);
+#endif
 }
 
 UA_Client *
@@ -108,6 +117,15 @@ UA_Client_secure_init(UA_Client* client, UA_ClientConfig config,
     if(client->config.stateCallback)
         client->config.stateCallback(client, client->state);
 
+    /* Catch error during async connection */
+    client->connectStatus = UA_STATUSCODE_GOOD;
+
+    /* Needed by async client */
+    UA_Timer_init(&client->timer);
+
+#ifndef UA_ENABLE_MULTITHREADING
+    SLIST_INIT(&client->delayedClientCallbacks);
+#endif
     /* Verify remote certificate if trust list given to the application */
     if(trustListSize > 0) {
         retval = client->channel.securityPolicy->certificateVerification->
@@ -195,6 +213,8 @@ UA_Client_deleteMembers(UA_Client* client) {
     /* Commented as UA_SecureChannel_deleteMembers already done
      * in UA_Client_disconnect function */
     //UA_SecureChannel_deleteMembersCleanup(&client->channel);
+    if (client->connection.free)
+        client->connection.free(&client->connection);
     UA_Connection_deleteMembers(&client->connection);
     if(client->endpointUrl.data)
         UA_String_deleteMembers(&client->endpointUrl);
@@ -212,6 +232,9 @@ UA_Client_deleteMembers(UA_Client* client) {
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     UA_Client_Subscriptions_clean(client);
 #endif
+
+    /* Delete the timed work */
+    UA_Timer_deleteMembers(&client->timer);
 }
 
 void
@@ -348,7 +371,7 @@ processAsyncResponse(UA_Client *client, UA_UInt32 requestId, const UA_NodeId *re
     }
 
     /* Call the callback */
-    ac->callback(client, ac->userdata, requestId, response, ac->responseType);
+    ac->callback(client, ac->userdata, requestId, response);
     UA_deleteMembers(response, ac->responseType);
 
     /* Remove the callback */
@@ -522,6 +545,44 @@ __UA_Client_Service(UA_Client *client, const void *request,
         respHeader->serviceResult = retval;
 }
 
+UA_StatusCode
+receiveServiceResponseAsync(UA_Client *client, void *response,
+                             const UA_DataType *responseType) {
+    SyncResponseDescription rd = { client, false, 0, response, responseType };
+
+    UA_StatusCode retval = UA_Connection_receiveChunksNonBlocking(
+            &client->connection, &rd, client_processChunk);
+    /*let client run when non critical timeout*/
+    if(retval != UA_STATUSCODE_GOOD
+            && retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT) {
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
+            setClientState(client, UA_CLIENTSTATE_DISCONNECTED);
+        UA_Client_close(client);
+    }
+    return retval;
+}
+
+UA_StatusCode
+receivePacketAsync(UA_Client *client) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if (UA_Client_getState(client) == UA_CLIENTSTATE_DISCONNECTED ||
+            UA_Client_getState(client) == UA_CLIENTSTATE_WAITING_FOR_ACK) {
+        retval = UA_Connection_receiveChunksNonBlocking(
+                &client->connection, client, client->ackResponseCallback);
+    }
+    else if(UA_Client_getState(client) == UA_CLIENTSTATE_CONNECTED) {
+        retval = UA_Connection_receiveChunksNonBlocking(
+                &client->connection, client,
+                client->openSecureChannelResponseCallback);
+    }
+    if(retval != UA_STATUSCODE_GOOD && retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT) {
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
+            setClientState(client, UA_CLIENTSTATE_DISCONNECTED);
+        UA_Client_close(client);
+    }
+    return retval;
+}
+
 void
 UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac,
                               UA_StatusCode statusCode) {
@@ -531,7 +592,7 @@ UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac,
     UA_init(resp, ac->responseType);
     ((UA_ResponseHeader*)resp)->serviceResult = statusCode;
 
-    ac->callback(client, ac->userdata, ac->requestId, resp, ac->responseType);
+    ac->callback(client, ac->userdata, ac->requestId, resp);
 
     /* Clean up the response. Users might move data into it. For whatever reasons. */
     UA_deleteMembers(resp, ac->responseType);
@@ -547,11 +608,12 @@ void UA_Client_AsyncService_removeAll(UA_Client *client, UA_StatusCode statusCod
 }
 
 UA_StatusCode
-__UA_Client_AsyncService(UA_Client *client, const void *request,
-                         const UA_DataType *requestType,
-                         UA_ClientAsyncServiceCallback callback,
-                         const UA_DataType *responseType,
-                         void *userdata, UA_UInt32 *requestId) {
+__UA_Client_AsyncServiceEx(UA_Client *client, const void *request,
+                           const UA_DataType *requestType,
+                           UA_ClientAsyncServiceCallback callback,
+                           const UA_DataType *responseType,
+                           void *userdata, UA_UInt32 *requestId,
+                           UA_UInt32 timeout) {
     /* Prepare the entry for the linked list */
     AsyncServiceCall *ac = (AsyncServiceCall*)UA_malloc(sizeof(AsyncServiceCall));
     if(!ac)
@@ -559,6 +621,7 @@ __UA_Client_AsyncService(UA_Client *client, const void *request,
     ac->callback = callback;
     ac->responseType = responseType;
     ac->userdata = userdata;
+    ac->timeout = timeout;
 
     /* Call the service and set the requestId */
     UA_StatusCode retval = sendSymmetricServiceRequest(client, request, requestType, &ac->requestId);
@@ -567,6 +630,8 @@ __UA_Client_AsyncService(UA_Client *client, const void *request,
         return retval;
     }
 
+    ac->start = UA_DateTime_nowMonotonic();
+
     /* Store the entry for async processing */
     LIST_INSERT_HEAD(&client->asyncServiceCalls, ac, pointers);
     if(requestId)
@@ -575,24 +640,43 @@ __UA_Client_AsyncService(UA_Client *client, const void *request,
 }
 
 UA_StatusCode
-UA_Client_runAsync(UA_Client *client, UA_UInt16 timeout) {
-    /* TODO: Call repeated jobs that are scheduled */
-#ifdef UA_ENABLE_SUBSCRIPTIONS
-    UA_StatusCode retvalPublish = UA_Client_Subscriptions_backgroundPublish(client);
-    if (retvalPublish != UA_STATUSCODE_GOOD)
-        return retvalPublish;
-#endif
-    UA_StatusCode retval = UA_Client_manuallyRenewSecureChannel(client);
-    if (retval != UA_STATUSCODE_GOOD)
-        return retval;
+__UA_Client_AsyncService(UA_Client *client, const void *request,
+                         const UA_DataType *requestType,
+                         UA_ClientAsyncServiceCallback callback,
+                         const UA_DataType *responseType,
+                         void *userdata, UA_UInt32 *requestId) {
+    return __UA_Client_AsyncServiceEx(client, request, requestType, callback,
+                                      responseType, userdata, requestId,
+                                      client->config.timeout);
+}
 
-    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;
-#ifdef UA_ENABLE_SUBSCRIPTIONS
-    /* The inactivity check must be done after receiveServiceResponse */
-    UA_Client_Subscriptions_backgroundPublishInactivityCheck(client);
-#endif
-    return retval;
+
+UA_StatusCode
+UA_Client_sendAsyncRequest(UA_Client *client, const void *request,
+                           const UA_DataType *requestType,
+                           UA_ClientAsyncServiceCallback callback,
+                           const UA_DataType *responseType, void *userdata,
+                           UA_UInt32 *requestId) {
+    if (UA_Client_getState(client) < UA_CLIENTSTATE_SECURECHANNEL) {
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                    "Cient must be connected to send high-level requests");
+        return UA_STATUSCODE_GOOD;
+    }
+    return __UA_Client_AsyncService(client, request, requestType, callback,
+                                    responseType, userdata, requestId);
+}
+
+UA_StatusCode
+UA_Client_addRepeatedCallback(UA_Client *Client, UA_ClientCallback callback,
+                              void *data, UA_UInt32 interval,
+                              UA_UInt64 *callbackId) {
+    return UA_Timer_addRepeatedCallback(&Client->timer,
+                                        (UA_TimerCallback) callback, data,
+                                        interval, callbackId);
+}
+
+
+UA_StatusCode
+UA_Client_removeRepeatedCallback(UA_Client *Client, UA_UInt64 callbackId) {
+    return UA_Timer_removeRepeatedCallback(&Client->timer, callbackId);
 }

+ 10 - 6
src/client/ua_client_connect.c

@@ -259,7 +259,7 @@ openSecureChannel(UA_Client *client, UA_Boolean renew) {
  * @param  channel      current channel in which the client runs
  * @param  response     create session response from the server
  * @return Returns an error code or UA_STATUSCODE_GOOD. */
-static UA_StatusCode
+UA_StatusCode
 checkClientSignature(const UA_SecureChannel *channel, const UA_CreateSessionResponse *response) {
     if(channel == NULL || response == NULL)
         return UA_STATUSCODE_BADINTERNALERROR;
@@ -298,7 +298,7 @@ checkClientSignature(const UA_SecureChannel *channel, const UA_CreateSessionResp
  * @param  channel      current channel in which the client runs
  * @param  request      activate session request message to server
  * @return Returns an error or UA_STATUSCODE_GOOD */
-static UA_StatusCode
+UA_StatusCode
 signActivateSessionRequest(UA_SecureChannel *channel,
                            UA_ActivateSessionRequest *request) {
     if(channel == NULL || request == NULL)
@@ -748,13 +748,17 @@ UA_Client_disconnect(UA_Client *client) {
     }
 
     /* Close the TCP connection */
-    if(client->connection.state != UA_CONNECTION_CLOSED)
-        client->connection.close(&client->connection);
+    if(client->connection.state != UA_CONNECTION_CLOSED
+            && client->connection.state != UA_CONNECTION_OPENING)
+        /*UA_ClientConnectionTCP_init sets initial state to opening */
+        if(client->connection.close != NULL)
+            client->connection.close(&client->connection);
+
 
 #ifdef UA_ENABLE_SUBSCRIPTIONS
 // TODO REMOVE WHEN UA_SESSION_RECOVERY IS READY
-        /* We need to clean up the subscriptions */
-        UA_Client_Subscriptions_clean(client);
+    /* We need to clean up the subscriptions */
+    UA_Client_Subscriptions_clean(client);
 #endif
 
     setClientState(client, UA_CLIENTSTATE_DISCONNECTED);

+ 680 - 0
src/client/ua_client_connect_async.c

@@ -0,0 +1,680 @@
+/* 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.h"
+#include "ua_client_internal.h"
+#include "ua_transport_generated.h"
+#include "ua_transport_generated_handling.h"
+#include "ua_transport_generated_encoding_binary.h"
+#include "ua_types_encoding_binary.h"
+#include "ua_types_generated_encoding_binary.h"
+
+#define UA_MINMESSAGESIZE                8192
+#define UA_SESSION_LOCALNONCELENGTH      32
+#define MAX_DATA_SIZE 4096
+
+/* Asynchronous client connection
+ * To prepare an async connection, UA_Client_connectAsync() is called, which does not connect the
+ * client directly. UA_Client_run_iterate() takes care of actually connecting the client:
+ * if client is disconnected:
+ *      send hello msg and set the client state to be WAITING_FOR_ACK
+ *      (see UA_Client_connect_iterate())
+ * if client is waiting for the ACK:
+ *      call the non-blocking receiving function and register processACKResponseAsync() as its callback
+ *      (see receivePacketAsync())
+ * if ACK is processed (callback called):
+ *      processACKResponseAsync() calls openSecureChannelAsync() at the end, which prepares the request
+ *      to open secure channel and the client is connected
+ * if client is connected:
+ *      call the non-blocking receiving function and register processOPNResponse() as its callback
+ *      (see receivePacketAsync())
+ * if OPN-request processed (callback called)
+ *      send session request, where the session response is put into a normal AsyncServiceCall, and when
+ *      called, request to activate session is sent, where its response is again put into an AsyncServiceCall
+ * in the very last step responseActivateSession():
+ *      the user defined callback that is passed into UA_Client_connectAsync() is called and the
+ *      async connection finalized.
+ * */
+
+/***********************/
+/* Open the Connection */
+/***********************/
+static UA_StatusCode
+openSecureChannelAsync(UA_Client *client, UA_Boolean renew);
+
+static UA_StatusCode
+requestSession(UA_Client *client, UA_UInt32 *requestId);
+
+static UA_StatusCode
+requestGetEndpoints(UA_Client *client, UA_UInt32 *requestId);
+
+/*receives hello ack, opens secure channel*/
+static UA_StatusCode
+processACKResponseAsync(void *application, UA_Connection *connection,
+                         UA_ByteString *chunk) {
+    UA_Client *client = (UA_Client*)application;
+
+    /* Decode the message */
+    size_t offset = 0;
+    UA_TcpMessageHeader messageHeader;
+    UA_TcpAcknowledgeMessage ackMessage;
+    client->connectStatus = UA_TcpMessageHeader_decodeBinary (chunk, &offset,
+                                                              &messageHeader);
+    client->connectStatus |= UA_TcpAcknowledgeMessage_decodeBinary(
+            chunk, &offset, &ackMessage);
+    if (client->connectStatus != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                     "Decoding ACK message failed");
+        return client->connectStatus;
+    }
+
+    /* Store remote connection settings and adjust local configuration to not
+     * exceed the limits */
+    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                 "Received ACK message");
+    connection->remoteConf.maxChunkCount = ackMessage.maxChunkCount; /* may be zero -> unlimited */
+    connection->remoteConf.maxMessageSize = ackMessage.maxMessageSize; /* may be zero -> unlimited */
+    connection->remoteConf.protocolVersion = ackMessage.protocolVersion;
+    connection->remoteConf.sendBufferSize = ackMessage.sendBufferSize;
+    connection->remoteConf.recvBufferSize = ackMessage.receiveBufferSize;
+    if (connection->remoteConf.recvBufferSize
+            < connection->localConf.sendBufferSize)
+        connection->localConf.sendBufferSize =
+                connection->remoteConf.recvBufferSize;
+    if (connection->remoteConf.sendBufferSize
+            < connection->localConf.recvBufferSize)
+        connection->localConf.recvBufferSize =
+                connection->remoteConf.sendBufferSize;
+    connection->state = UA_CONNECTION_ESTABLISHED;
+
+    client->state = UA_CLIENTSTATE_CONNECTED;
+
+    /* Open a SecureChannel. TODO: Select with endpoint  */
+    client->channel.connection = &client->connection;
+    client->connectStatus = openSecureChannelAsync(client, false);
+    return client->connectStatus;
+}
+
+static UA_StatusCode
+sendHELMessage(UA_Client *client) {
+    /* Get a buffer */
+    UA_ByteString message;
+    UA_Connection *conn = &client->connection;
+    client->connectStatus = conn->getSendBuffer(conn, UA_MINMESSAGESIZE,
+                                                &message);
+
+    if (client->connectStatus != UA_STATUSCODE_GOOD)
+        return client->connectStatus;
+
+    /* Prepare the HEL message and encode at offset 8 */
+    UA_TcpHelloMessage hello;
+    UA_String_copy(&client->endpointUrl, &hello.endpointUrl); /* must be less than 4096 bytes */
+    hello.maxChunkCount = conn->localConf.maxChunkCount;
+    hello.maxMessageSize = conn->localConf.maxMessageSize;
+    hello.protocolVersion = conn->localConf.protocolVersion;
+    hello.receiveBufferSize = conn->localConf.recvBufferSize;
+    hello.sendBufferSize = conn->localConf.sendBufferSize;
+
+    UA_Byte *bufPos = &message.data[8]; /* skip the header */
+    const UA_Byte *bufEnd = &message.data[message.length];
+    client->connectStatus = UA_TcpHelloMessage_encodeBinary(&hello, &bufPos,
+                                                            bufEnd);
+    UA_TcpHelloMessage_deleteMembers (&hello);
+
+    /* Encode the message header at offset 0 */
+    UA_TcpMessageHeader messageHeader;
+    messageHeader.messageTypeAndChunkType = UA_CHUNKTYPE_FINAL
+            + UA_MESSAGETYPE_HEL;
+    messageHeader.messageSize = (UA_UInt32) ((uintptr_t)bufPos
+            - (uintptr_t)message.data);
+    bufPos = message.data;
+    client->connectStatus |= UA_TcpMessageHeader_encodeBinary(&messageHeader,
+                                                              &bufPos,
+                                                              bufEnd);
+    if (client->connectStatus != UA_STATUSCODE_GOOD) {
+        conn->releaseSendBuffer(conn, &message);
+        return client->connectStatus;
+    }
+
+    /* Send the HEL message */
+    message.length = messageHeader.messageSize;
+    client->connectStatus = conn->send (conn, &message);
+
+    if (client->connectStatus != UA_STATUSCODE_GOOD) {
+        UA_LOG_INFO(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                    "Sending HEL failed");
+        return client->connectStatus;
+    }
+    UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                 "Sent HEL message");
+    setClientState(client, UA_CLIENTSTATE_WAITING_FOR_ACK);
+    return client->connectStatus;
+}
+
+static UA_StatusCode
+processDecodedOPNResponseAsync(void *application, UA_SecureChannel *channel,
+                                UA_MessageType messageType,
+                                UA_UInt32 requestId,
+                                const UA_ByteString *message) {
+    /* Does the request id match? */
+    UA_Client *client = (UA_Client*)application;
+    if (requestId != client->requestId)
+        return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+
+    /* Is the content of the expected type? */
+    size_t offset = 0;
+    UA_NodeId responseId;
+    UA_NodeId expectedId = UA_NODEID_NUMERIC(
+            0, UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE].binaryEncodingId);
+    UA_StatusCode retval = UA_NodeId_decodeBinary(message, &offset,
+                                                  &responseId);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    if(!UA_NodeId_equal(&responseId, &expectedId)) {
+        UA_NodeId_deleteMembers(&responseId);
+        return UA_STATUSCODE_BADCOMMUNICATIONERROR;
+    }
+    UA_NodeId_deleteMembers (&responseId);
+
+    /* Decode the response */
+    UA_OpenSecureChannelResponse response;
+    retval = UA_OpenSecureChannelResponse_decodeBinary(message, &offset,
+                                                       &response);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Response.securityToken.revisedLifetime is UInt32 we need to cast it to
+     * DateTime=Int64 we take 75% of lifetime to start renewing as described in
+     * standard */
+    client->nextChannelRenewal = UA_DateTime_nowMonotonic()
+            + (UA_DateTime) (response.securityToken.revisedLifetime
+                    * (UA_Double) UA_DATETIME_MSEC * 0.75);
+
+    /* Replace the token and nonce */
+    UA_ChannelSecurityToken_deleteMembers(&client->channel.securityToken);
+    UA_ByteString_deleteMembers(&client->channel.remoteNonce);
+    client->channel.securityToken = response.securityToken;
+    client->channel.remoteNonce = response.serverNonce;
+    UA_ResponseHeader_deleteMembers(&response.responseHeader); /* the other members were moved */
+    if(client->channel.state == UA_SECURECHANNELSTATE_OPEN)
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "SecureChannel renewed");
+    else
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "SecureChannel opened");
+    client->channel.state = UA_SECURECHANNELSTATE_OPEN;
+    return UA_STATUSCODE_GOOD;
+}
+
+static UA_StatusCode processOPNResponse
+    (void *application, UA_Connection *connection,
+                    UA_ByteString *chunk) {
+    UA_Client *client = (UA_Client*) application;
+    UA_StatusCode retval = UA_SecureChannel_processChunk (
+            &client->channel, chunk, processDecodedOPNResponseAsync, client);
+    client->connectStatus = retval;
+    if(retval != UA_STATUSCODE_GOOD) {
+        return retval;
+    }
+    setClientState(client, UA_CLIENTSTATE_SECURECHANNEL);
+    retval |= UA_SecureChannel_generateNewKeys(&client->channel);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    /* Following requests and responses */
+    UA_UInt32 reqId;
+    if(client->endpointsHandshake)
+        retval = requestGetEndpoints (client, &reqId);
+    else
+        retval = requestSession (client, &reqId);
+
+    client->connectStatus = retval;
+    return retval;
+
+}
+
+/* OPN messges to renew the channel are sent asynchronous */
+static UA_StatusCode
+openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
+    /* Check if sc is still valid */
+    if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic () > 0)
+        return UA_STATUSCODE_GOOD;
+
+    UA_Connection *conn = &client->connection;
+    if(conn->state != UA_CONNECTION_ESTABLISHED)
+        return UA_STATUSCODE_BADSERVERNOTCONNECTED;
+
+    /* Prepare the OpenSecureChannelRequest */
+    UA_OpenSecureChannelRequest opnSecRq;
+    UA_OpenSecureChannelRequest_init(&opnSecRq);
+    opnSecRq.requestHeader.timestamp = UA_DateTime_now();
+    opnSecRq.requestHeader.authenticationToken = client->authenticationToken;
+    if(renew) {
+        opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "Requesting to renew the SecureChannel");
+    } else {
+        opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
+        UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                     "Requesting to open a SecureChannel");
+    }
+    opnSecRq.securityMode = client->channel.securityMode;
+
+    opnSecRq.clientNonce = client->channel.localNonce;
+    opnSecRq.requestedLifetime = client->config.secureChannelLifeTime;
+
+    /* Prepare the entry for the linked list */
+    UA_UInt32 requestId = ++client->requestId;
+    AsyncServiceCall *ac = NULL;
+    if(renew) {
+        ac = (AsyncServiceCall*)UA_malloc(sizeof(AsyncServiceCall));
+        if (!ac)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        ac->callback =
+                (UA_ClientAsyncServiceCallback) processDecodedOPNResponseAsync;
+        ac->responseType = &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE];
+        ac->requestId = requestId;
+        ac->userdata = NULL;
+    }
+
+    /* Send the OPN message */
+    UA_StatusCode retval = UA_SecureChannel_sendAsymmetricOPNMessage (
+            &client->channel, requestId, &opnSecRq,
+            &UA_TYPES[UA_TYPES_OPENSECURECHANNELREQUEST]);
+    client->connectStatus = retval;
+
+    if(retval != UA_STATUSCODE_GOOD) {
+        client->connectStatus = retval;
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                      "Sending OPN message failed with error %s",
+                      UA_StatusCode_name(retval));
+        UA_Client_close(client);
+        if(renew)
+            UA_free(ac);
+        return retval;
+    }
+
+    UA_LOG_DEBUG (client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
+                  "OPN message sent");
+
+    /* Store the entry for async processing and return */
+    if(renew) {
+        LIST_INSERT_HEAD(&client->asyncServiceCalls, ac, pointers);
+        return retval;
+    }
+    return retval;
+}
+
+static void
+responseActivateSession(UA_Client *client, void *userdata, UA_UInt32 requestId,
+                        void *response) {
+    UA_ActivateSessionResponse *activateResponse =
+            (UA_ActivateSessionResponse *) response;
+    if(activateResponse->responseHeader.serviceResult) {
+        UA_LOG_ERROR(
+                client->config.logger,
+                UA_LOGCATEGORY_CLIENT,
+                "ActivateSession failed with error code %s",
+                UA_StatusCode_name(activateResponse->responseHeader.serviceResult));
+    }
+    client->connection.state = UA_CONNECTION_ESTABLISHED;
+    setClientState(client, UA_CLIENTSTATE_SESSION);
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+        /* A new session has been created. We need to clean up the subscriptions */
+        UA_Client_Subscriptions_clean(client);
+#endif
+
+     /* call onConnect (client_async.c) callback */
+    AsyncServiceCall ac = client->asyncConnectCall;
+
+    ac.callback(client, ac.userdata, requestId + 1,
+                &activateResponse->responseHeader.serviceResult);
+}
+
+static UA_StatusCode
+requestActivateSession (UA_Client *client, UA_UInt32 *requestId) {
+    UA_ActivateSessionRequest request;
+    UA_ActivateSessionRequest_init(&request);
+    request.requestHeader.requestHandle = ++client->requestHandle;
+    request.requestHeader.timestamp = UA_DateTime_now ();
+    request.requestHeader.timeoutHint = 600000;
+
+    /* Manual ExtensionObject encoding of the identityToken */
+    if (client->authenticationMethod == UA_CLIENTAUTHENTICATION_NONE) {
+        UA_AnonymousIdentityToken* identityToken =
+                UA_AnonymousIdentityToken_new();
+        UA_AnonymousIdentityToken_init (identityToken);
+        UA_String_copy(&client->token.policyId, &identityToken->policyId);
+        request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
+        request.userIdentityToken.content.decoded.type =
+                &UA_TYPES[UA_TYPES_ANONYMOUSIDENTITYTOKEN];
+        request.userIdentityToken.content.decoded.data = identityToken;
+    } else {
+        UA_UserNameIdentityToken* identityToken =
+                UA_UserNameIdentityToken_new();
+        UA_UserNameIdentityToken_init (identityToken);
+        UA_String_copy(&client->token.policyId, &identityToken->policyId);
+        UA_String_copy(&client->username, &identityToken->userName);
+        UA_String_copy(&client->password, &identityToken->password);
+        request.userIdentityToken.encoding = UA_EXTENSIONOBJECT_DECODED;
+        request.userIdentityToken.content.decoded.type =
+                &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN];
+        request.userIdentityToken.content.decoded.data = identityToken;
+    }
+    /* This function call is to prepare a client signature */
+    if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN ||
+       client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
+        signActivateSessionRequest(&client->channel, &request);
+    }
+
+    UA_StatusCode retval = UA_Client_sendAsyncRequest (
+            client, &request, &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST],
+            (UA_ClientAsyncServiceCallback) responseActivateSession,
+            &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE], NULL, requestId);
+    UA_ActivateSessionRequest_deleteMembers(&request);
+    client->connectStatus = retval;
+    return retval;
+}
+
+/* Combination of UA_Client_getEndpointsInternal and getEndpoints */
+static void
+responseGetEndpoints(UA_Client *client, void *userdata, UA_UInt32 requestId,
+                     void *response) {
+    UA_EndpointDescription* endpointArray = NULL;
+    size_t endpointArraySize = 0;
+    UA_GetEndpointsResponse* resp;
+    resp = (UA_GetEndpointsResponse*)response;
+
+    if (resp->responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+        client->connectStatus = resp->responseHeader.serviceResult;
+        UA_LOG_ERROR (client->config.logger, UA_LOGCATEGORY_CLIENT,
+                      "GetEndpointRequest failed with error code %s",
+                      UA_StatusCode_name (client->connectStatus));
+        UA_GetEndpointsResponse_deleteMembers(resp);
+        return;
+    }
+    endpointArray = resp->endpoints;
+    endpointArraySize = resp->endpointsSize;
+    resp->endpoints = NULL;
+    resp->endpointsSize = 0;
+
+    UA_Boolean endpointFound = false;
+    UA_Boolean tokenFound = false;
+    UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
+    UA_String binaryTransport = UA_STRING("http://opcfoundation.org/UA-Profile/"
+                                          "Transport/uatcp-uasc-uabinary");
+
+    // TODO: compare endpoint information with client->endpointUri
+    for(size_t i = 0; i < endpointArraySize; ++i) {
+        UA_EndpointDescription* endpoint = &endpointArray[i];
+        /* look out for binary transport endpoints */
+        /* Note: Siemens returns empty ProfileUrl, we will accept it as binary */
+        if(endpoint->transportProfileUri.length != 0
+                && !UA_String_equal (&endpoint->transportProfileUri,
+                                     &binaryTransport))
+            continue;
+
+        /* Look for an endpoint corresponding to the client security policy */
+        if(!UA_String_equal(&endpoint->securityPolicyUri, &client->securityPolicy.policyUri))
+            continue;
+
+        endpointFound = true;
+
+        /* Look for a user token policy with an anonymous token */
+        for(size_t j = 0; j < endpoint->userIdentityTokensSize; ++j) {
+            UA_UserTokenPolicy* userToken = &endpoint->userIdentityTokens[j];
+
+            /* Usertokens also have a security policy... */
+            if(userToken->securityPolicyUri.length > 0
+                    && !UA_String_equal(&userToken->securityPolicyUri,
+                                         &securityNone))
+                continue;
+
+            /* UA_CLIENTAUTHENTICATION_NONE == UA_USERTOKENTYPE_ANONYMOUS
+             * UA_CLIENTAUTHENTICATION_USERNAME == UA_USERTOKENTYPE_USERNAME
+             * TODO: Check equivalence for other types when adding the support */
+            if((int)client->authenticationMethod
+                    != (int)userToken->tokenType)
+                continue;
+
+            /* Endpoint with matching usertokenpolicy found */
+            tokenFound = true;
+            UA_UserTokenPolicy_deleteMembers(&client->token);
+            UA_UserTokenPolicy_copy(userToken, &client->token);
+            break;
+        }
+    }
+
+    UA_Array_delete(endpointArray, endpointArraySize,
+                    &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
+
+    if(!endpointFound) {
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
+                      "No suitable endpoint found");
+        client->connectStatus = UA_STATUSCODE_BADINTERNALERROR;
+    } else if(!tokenFound) {
+        UA_LOG_ERROR(
+                client->config.logger, UA_LOGCATEGORY_CLIENT,
+                "No suitable UserTokenPolicy found for the possible endpoints");
+        client->connectStatus = UA_STATUSCODE_BADINTERNALERROR;
+    }
+    requestSession(client, &requestId);
+}
+
+static UA_StatusCode
+requestGetEndpoints(UA_Client *client, UA_UInt32 *requestId) {
+    UA_GetEndpointsRequest request;
+    UA_GetEndpointsRequest_init(&request);
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+    /* assume the endpointurl outlives the service call */
+    UA_String_copy (&client->endpointUrl, &request.endpointUrl);
+
+    client->connectStatus = UA_Client_sendAsyncRequest(
+            client, &request, &UA_TYPES[UA_TYPES_GETENDPOINTSREQUEST],
+            (UA_ClientAsyncServiceCallback) responseGetEndpoints,
+            &UA_TYPES[UA_TYPES_GETENDPOINTSRESPONSE], NULL, requestId);
+    UA_GetEndpointsRequest_deleteMembers(&request);
+    return client->connectStatus;
+
+}
+
+static void
+responseSessionCallback(UA_Client *client, void *userdata, UA_UInt32 requestId,
+                        void *response) {
+    UA_CreateSessionResponse *sessionResponse =
+            (UA_CreateSessionResponse *)response;
+    UA_NodeId_copy(&sessionResponse->authenticationToken,
+                   &client->authenticationToken);
+    requestActivateSession(client, &requestId);
+}
+
+static UA_StatusCode
+requestSession(UA_Client *client, UA_UInt32 *requestId) {
+    UA_CreateSessionRequest request;
+    UA_CreateSessionRequest_init(&request);
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN ||
+       client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
+        if(client->channel.localNonce.length != UA_SESSION_LOCALNONCELENGTH) {
+           UA_ByteString_deleteMembers(&client->channel.localNonce);
+            retval = UA_ByteString_allocBuffer(&client->channel.localNonce,
+                                               UA_SESSION_LOCALNONCELENGTH);
+            if(retval != UA_STATUSCODE_GOOD)
+                return retval;
+        }
+
+        retval = client->channel.securityPolicy->symmetricModule.
+                 generateNonce(client->channel.securityPolicy, &client->channel.localNonce);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+    }
+
+    request.requestHeader.requestHandle = ++client->requestHandle;
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+    UA_ByteString_copy(&client->channel.localNonce, &request.clientNonce);
+    request.requestedSessionTimeout = 1200000;
+    request.maxResponseMessageSize = UA_INT32_MAX;
+    UA_String_copy(&client->endpointUrl, &request.endpointUrl);
+
+    retval = UA_Client_sendAsyncRequest (
+            client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST],
+            (UA_ClientAsyncServiceCallback) responseSessionCallback,
+            &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE], NULL, requestId);
+    UA_CreateSessionRequest_deleteMembers(&request);
+    client->connectStatus = retval;
+    return client->connectStatus;
+}
+
+UA_StatusCode
+UA_Client_connectInternalAsync(UA_Client *client, const char *endpointUrl,
+                               UA_ClientAsyncServiceCallback callback,
+                               void *userdata, UA_Boolean endpointsHandshake,
+                               UA_Boolean createNewSession) {
+    if(client->state >= UA_CLIENTSTATE_WAITING_FOR_ACK)
+        return UA_STATUSCODE_GOOD;
+    UA_ChannelSecurityToken_init(&client->channel.securityToken);
+    client->channel.state = UA_SECURECHANNELSTATE_FRESH;
+    /* Set up further callback function to handle secure channel and session establishment  */
+    client->ackResponseCallback = processACKResponseAsync;
+    client->openSecureChannelResponseCallback = processOPNResponse;
+    client->endpointsHandshake = endpointsHandshake;
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    client->connection = client->config.initConnectionFunc(
+            client->config.localConnectionConfig, endpointUrl,
+            client->config.timeout, client->config.logger);
+    if(client->connection.state != UA_CONNECTION_OPENING) {
+        retval = UA_STATUSCODE_BADCONNECTIONCLOSED;
+        goto cleanup;
+    }
+
+    UA_String_deleteMembers(&client->endpointUrl);
+    client->endpointUrl = UA_STRING_ALLOC(endpointUrl);
+    if(!client->endpointUrl.data) {
+        retval = UA_STATUSCODE_BADOUTOFMEMORY;
+        goto cleanup;
+    }
+
+    client->asyncConnectCall.callback = callback;
+    client->asyncConnectCall.userdata = userdata;
+
+    if(!client->connection.connectCallbackID) {
+        retval = UA_Client_addRepeatedCallback(
+                     client, client->config.pollConnectionFunc, &client->connection, 100,
+                     &client->connection.connectCallbackID);
+    }
+
+    retval |= UA_SecureChannel_generateLocalNonce(&client->channel);
+
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    /* Delete async service. TODO: Move this from connect to the disconnect/cleanup phase */
+    UA_Client_AsyncService_removeAll(client, UA_STATUSCODE_BADSHUTDOWN);
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+    client->currentlyOutStandingPublishRequests = 0;
+#endif
+
+    UA_NodeId_deleteMembers(&client->authenticationToken);
+
+    /* Generate new local and remote key */
+    retval = UA_SecureChannel_generateNewKeys(&client->channel);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+
+    return retval;
+
+    cleanup: UA_Client_close(client);
+        return retval;
+}
+
+UA_StatusCode
+UA_Client_connect_iterate(UA_Client *client) {
+    if (client->connection.state == UA_CONNECTION_ESTABLISHED){
+        if (client->state < UA_CLIENTSTATE_WAITING_FOR_ACK)
+            return sendHELMessage(client);
+    }
+
+    /* If server is not connected */
+    if (client->connection.state == UA_CONNECTION_CLOSED) {
+        client->connectStatus = UA_STATUSCODE_BADCONNECTIONCLOSED;
+        UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_NETWORK,
+                     "No connection to server.");
+    }
+
+    return client->connectStatus;
+}
+
+UA_StatusCode
+UA_Client_connect_async(UA_Client *client, const char *endpointUrl,
+                        UA_ClientAsyncServiceCallback callback,
+                        void *userdata) {
+    return UA_Client_connectInternalAsync(client, endpointUrl, callback,
+            userdata, UA_TRUE, UA_TRUE);
+}
+
+/* Async disconnection */
+static void
+sendCloseSecureChannelAsync(UA_Client *client, void *userdata,
+                             UA_UInt32 requestId, void *response) {
+    UA_NodeId_deleteMembers (&client->authenticationToken);
+    client->requestHandle = 0;
+
+    UA_SecureChannel *channel = &client->channel;
+    UA_CloseSecureChannelRequest request;
+    UA_CloseSecureChannelRequest_init(&request);
+    request.requestHeader.requestHandle = ++client->requestHandle;
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+    request.requestHeader.authenticationToken = client->authenticationToken;
+    UA_SecureChannel_sendSymmetricMessage(
+            channel, ++client->requestId, UA_MESSAGETYPE_CLO, &request,
+            &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]);
+    UA_SecureChannel_deleteMembersCleanup(&client->channel);
+}
+
+static void
+sendCloseSessionAsync(UA_Client *client, UA_UInt32 *requestId) {
+    UA_CloseSessionRequest request;
+    UA_CloseSessionRequest_init(&request);
+
+    request.requestHeader.timestamp = UA_DateTime_now();
+    request.requestHeader.timeoutHint = 10000;
+    request.deleteSubscriptions = true;
+
+    UA_Client_sendAsyncRequest(
+            client, &request, &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST],
+            (UA_ClientAsyncServiceCallback) sendCloseSecureChannelAsync,
+            &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE], NULL, requestId);
+
+}
+
+UA_StatusCode
+UA_Client_disconnect_async(UA_Client *client, UA_UInt32 *requestId) {
+    /* Is a session established? */
+    if (client->state == UA_CLIENTSTATE_SESSION) {
+        client->state = UA_CLIENTSTATE_SESSION_DISCONNECTED;
+        sendCloseSessionAsync(client, requestId);
+    }
+
+    /* Close the TCP connection
+     * shutdown and close (in tcp.c) are already async*/
+    if (client->state >= UA_CLIENTSTATE_CONNECTED)
+        client->connection.close(&client->connection);
+    else
+        UA_Client_removeRepeatedCallback(client, client->connection.connectCallbackID);
+
+#ifdef UA_ENABLE_SUBSCRIPTIONS
+// TODO REMOVE WHEN UA_SESSION_RECOVERY IS READY
+    /* We need to clean up the subscriptions */
+    UA_Client_Subscriptions_clean(client);
+#endif
+
+    setClientState(client, UA_CLIENTSTATE_DISCONNECTED);
+    return UA_STATUSCODE_GOOD;
+}

+ 204 - 0
src/client/ua_client_highlevel.c

@@ -10,7 +10,9 @@
  */
 
 #include "ua_client.h"
+#include "ua_client_internal.h"
 #include "ua_client_highlevel.h"
+#include "ua_client_highlevel_async.h"
 #include "ua_util.h"
 
 UA_StatusCode
@@ -454,3 +456,205 @@ UA_Client_readArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId
     UA_ReadResponse_deleteMembers(&response);
     return retval;
 }
+/*Async Functions*/
+
+/*highlevel callbacks
+ * used to hide response handling details*/
+static
+void ValueAttributeRead(UA_Client *client, void *userdata, UA_UInt32 requestId,
+        void *response) {
+
+    if (response == NULL) {
+        return;
+    }
+
+    CustomCallback *cc;
+    LIST_FOREACH(cc, &client->customCallbacks, pointers)
+    {
+        if (cc->callbackId == requestId)
+            break;
+    }
+    if (!cc)
+        return;
+
+    UA_ReadResponse rr = *(UA_ReadResponse *) response;
+    if (rr.results[0].status != UA_STATUSCODE_GOOD)
+        UA_ReadResponse_deleteMembers((UA_ReadResponse*) response);
+
+    UA_Variant out;
+    UA_Variant_init(&out);
+    UA_DataValue *res = rr.results;
+    if (!res->hasValue) {
+        return;
+    }
+
+    /*__UA_Client_readAttribute*/
+    memcpy(&out, &res->value, sizeof(UA_Variant));
+    /* Copy value into out */
+    if (cc->attributeId == UA_ATTRIBUTEID_VALUE) {
+        memcpy(&out, &res->value, sizeof(UA_Variant));
+        UA_Variant_init(&res->value);
+    } else if (cc->attributeId == UA_ATTRIBUTEID_NODECLASS) {
+        memcpy(&out, (UA_NodeClass*) res->value.data, sizeof(UA_NodeClass));
+    } else if (UA_Variant_isScalar(&res->value)
+            && res->value.type == cc->outDataType) {
+        memcpy(&out, res->value.data, res->value.type->memSize);
+        UA_free(res->value.data);
+        res->value.data = NULL;
+    }
+
+    //use callbackId to find the right custom callback
+    cc->callback(client, userdata, requestId, &out);
+    LIST_REMOVE(cc, pointers);
+    UA_free(cc);
+    UA_ReadResponse_deleteMembers((UA_ReadResponse*) response);
+    UA_Variant_deleteMembers(&out);
+}
+
+/*Read Attributes*/
+UA_StatusCode __UA_Client_readAttribute_async(UA_Client *client,
+        const UA_NodeId *nodeId, UA_AttributeId attributeId,
+        const UA_DataType *outDataType, UA_ClientAsyncServiceCallback callback,
+        void *userdata, UA_UInt32 *reqId) {
+    UA_ReadValueId item;
+    UA_ReadValueId_init(&item);
+    item.nodeId = *nodeId;
+    item.attributeId = attributeId;
+    UA_ReadRequest request;
+    UA_ReadRequest_init(&request);
+    request.nodesToRead = &item;
+    request.nodesToReadSize = 1;
+
+    __UA_Client_AsyncService(client, &request, &UA_TYPES[UA_TYPES_READREQUEST],
+            ValueAttributeRead, &UA_TYPES[UA_TYPES_READRESPONSE], userdata,
+            reqId);
+
+    CustomCallback *cc = (CustomCallback*) UA_malloc(sizeof(CustomCallback));
+    if (!cc)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    cc->callback = callback;
+    cc->callbackId = *reqId;
+
+    cc->attributeId = attributeId;
+    cc->outDataType = outDataType;
+
+    LIST_INSERT_HEAD(&client->customCallbacks, cc, pointers);
+
+    return UA_STATUSCODE_GOOD;
+}
+
+/*Write Attributes*/
+UA_StatusCode __UA_Client_writeAttribute_async(UA_Client *client,
+        const UA_NodeId *nodeId, UA_AttributeId attributeId, const void *in,
+        const UA_DataType *inDataType, UA_ClientAsyncServiceCallback callback,
+        void *userdata, UA_UInt32 *reqId) {
+    if (!in)
+        return UA_STATUSCODE_BADTYPEMISMATCH;
+
+    UA_WriteValue wValue;
+    UA_WriteValue_init(&wValue);
+    wValue.nodeId = *nodeId;
+    wValue.attributeId = attributeId;
+    if (attributeId == UA_ATTRIBUTEID_VALUE)
+        wValue.value.value = *(const UA_Variant*) in;
+    else
+        /* hack. is never written into. */
+        UA_Variant_setScalar(&wValue.value.value, (void*) (uintptr_t) in,
+                inDataType);
+    wValue.value.hasValue = true;
+    UA_WriteRequest wReq;
+    UA_WriteRequest_init(&wReq);
+    wReq.nodesToWrite = &wValue;
+    wReq.nodesToWriteSize = 1;
+
+    return __UA_Client_AsyncService(client, &wReq,
+            &UA_TYPES[UA_TYPES_WRITEREQUEST], callback,
+            &UA_TYPES[UA_TYPES_WRITERESPONSE], userdata, reqId);
+}
+
+/*Node Management*/
+
+UA_StatusCode UA_EXPORT
+__UA_Client_addNode_async(UA_Client *client, const UA_NodeClass nodeClass,
+        const UA_NodeId requestedNewNodeId, const UA_NodeId parentNodeId,
+        const UA_NodeId referenceTypeId, const UA_QualifiedName browseName,
+        const UA_NodeId typeDefinition, const UA_NodeAttributes *attr,
+        const UA_DataType *attributeType, UA_NodeId *outNewNodeId,
+        UA_ClientAsyncServiceCallback callback, void *userdata,
+        UA_UInt32 *reqId) {
+    UA_AddNodesRequest request;
+    UA_AddNodesRequest_init(&request);
+    UA_AddNodesItem item;
+    UA_AddNodesItem_init(&item);
+    item.parentNodeId.nodeId = parentNodeId;
+    item.referenceTypeId = referenceTypeId;
+    item.requestedNewNodeId.nodeId = requestedNewNodeId;
+    item.browseName = browseName;
+    item.nodeClass = nodeClass;
+    item.typeDefinition.nodeId = typeDefinition;
+    item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE;
+    item.nodeAttributes.content.decoded.type = attributeType;
+    item.nodeAttributes.content.decoded.data = (void*) (uintptr_t) attr; // hack. is not written into.
+    request.nodesToAdd = &item;
+    request.nodesToAddSize = 1;
+
+    return __UA_Client_AsyncService(client, &request,
+            &UA_TYPES[UA_TYPES_ADDNODESREQUEST], callback,
+            &UA_TYPES[UA_TYPES_ADDNODESRESPONSE], userdata, reqId);
+
+}
+
+/*Misc Highlevel Functions*/
+UA_StatusCode __UA_Client_call_async(UA_Client *client,
+        const UA_NodeId objectId, const UA_NodeId methodId, size_t inputSize,
+        const UA_Variant *input, UA_ClientAsyncServiceCallback callback,
+        void *userdata, UA_UInt32 *reqId) {
+
+    UA_CallRequest request;
+    UA_CallRequest_init(&request);
+    UA_CallMethodRequest item;
+    UA_CallMethodRequest_init(&item);
+    item.methodId = methodId;
+    item.objectId = objectId;
+    item.inputArguments = (UA_Variant *) (void*) (uintptr_t) input; // cast const...
+    item.inputArgumentsSize = inputSize;
+    request.methodsToCall = &item;
+    request.methodsToCallSize = 1;
+
+    return __UA_Client_AsyncService(client, &request,
+            &UA_TYPES[UA_TYPES_CALLREQUEST], callback,
+            &UA_TYPES[UA_TYPES_CALLRESPONSE], userdata, reqId);
+}
+
+UA_StatusCode __UA_Client_translateBrowsePathsToNodeIds_async(UA_Client *client,
+        char *paths[], UA_UInt32 ids[], size_t pathSize,
+        UA_ClientAsyncServiceCallback callback, void *userdata,
+        UA_UInt32 *reqId) {
+
+    UA_BrowsePath browsePath;
+    UA_BrowsePath_init(&browsePath);
+    browsePath.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+    browsePath.relativePath.elements = (UA_RelativePathElement*) UA_Array_new(
+            pathSize, &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
+    if (!browsePath.relativePath.elements)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    browsePath.relativePath.elementsSize = pathSize;
+
+    UA_TranslateBrowsePathsToNodeIdsRequest request;
+    UA_TranslateBrowsePathsToNodeIdsRequest_init(&request);
+    request.browsePaths = &browsePath;
+    request.browsePathsSize = 1;
+
+    UA_StatusCode retval = __UA_Client_AsyncService(client, &request,
+            &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSREQUEST], callback,
+            &UA_TYPES[UA_TYPES_TRANSLATEBROWSEPATHSTONODEIDSRESPONSE], userdata,
+            reqId);
+    if (retval != UA_STATUSCODE_GOOD) {
+        UA_Array_delete(browsePath.relativePath.elements,
+                browsePath.relativePath.elementsSize,
+                &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
+        return retval;
+    }
+    UA_BrowsePath_deleteMembers(&browsePath);
+    return retval;
+}

+ 71 - 3
src/client/ua_client_internal.h

@@ -17,6 +17,7 @@
 #include "ua_client_highlevel.h"
 #include "ua_client_subscriptions.h"
 #include "../../deps/queue.h"
+#include "ua_timer.h"
 
 /**************************/
 /* Subscriptions Handling */
@@ -78,6 +79,16 @@ UA_Client_Subscriptions_backgroundPublishInactivityCheck(UA_Client *client);
 
 #endif /* UA_ENABLE_SUBSCRIPTIONS */
 
+/**************/
+/* Encryption */
+/**************/
+
+UA_StatusCode
+checkClientSignature(const UA_SecureChannel *channel, const UA_CreateSessionResponse *response);
+
+UA_StatusCode
+signActivateSessionRequest(UA_SecureChannel *channel,
+                           UA_ActivateSessionRequest *request);
 /**********/
 /* Client */
 /**********/
@@ -88,6 +99,9 @@ typedef struct AsyncServiceCall {
     UA_ClientAsyncServiceCallback callback;
     const UA_DataType *responseType;
     void *userdata;
+    UA_DateTime start;
+    UA_UInt32 timeout;
+    void *responsedata;
 } AsyncServiceCall;
 
 void UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac,
@@ -95,6 +109,23 @@ void UA_Client_AsyncService_cancel(UA_Client *client, AsyncServiceCall *ac,
 
 void UA_Client_AsyncService_removeAll(UA_Client *client, UA_StatusCode statusCode);
 
+typedef struct CustomCallback {
+    LIST_ENTRY(CustomCallback)
+    pointers;
+    //to find the correct callback
+    UA_UInt32 callbackId;
+
+    UA_ClientAsyncServiceCallback callback;
+
+    UA_AttributeId attributeId;
+    const UA_DataType *outDataType;
+} CustomCallback;
+
+typedef enum {
+    UA_CHUNK_COMPLETED,
+    UA_CHUNK_NOT_COMPLETED
+} UA_ChunkState;
+
 typedef enum {
     UA_CLIENTAUTHENTICATION_NONE,
     UA_CLIENTAUTHENTICATION_USERNAME
@@ -105,6 +136,8 @@ struct UA_Client {
     UA_ClientState state;
 
     UA_ClientConfig config;
+    UA_Timer timer;
+    UA_StatusCode connectStatus;
 
     /* Connection */
     UA_Connection connection;
@@ -125,10 +158,19 @@ struct UA_Client {
     UA_UserTokenPolicy token;
     UA_NodeId authenticationToken;
     UA_UInt32 requestHandle;
+    /* Connection Establishment (async) */
+    UA_Connection_processChunk ackResponseCallback;
+    UA_Connection_processChunk openSecureChannelResponseCallback;
+    UA_Boolean endpointsHandshake;
 
     /* Async Service */
+    AsyncServiceCall asyncConnectCall;
     LIST_HEAD(ListOfAsyncServiceCall, AsyncServiceCall) asyncServiceCalls;
+    /*When using highlevel functions these are the callbacks that can be accessed by the user*/
+    LIST_HEAD(ListOfCustomCallback, CustomCallback) customCallbacks;
 
+    /* Delayed callbacks */
+    SLIST_HEAD(DelayedClientCallbacksList, UA_DelayedClientCallback) delayedClientCallbacks;
     /* Subscriptions */
 #ifdef UA_ENABLE_SUBSCRIPTIONS
     UA_UInt32 monitoredItemHandles;
@@ -136,6 +178,10 @@ struct UA_Client {
     LIST_HEAD(ListOfClientSubscriptionItems, UA_Client_Subscription) subscriptions;
     UA_UInt16 currentlyOutStandingPublishRequests;
 #endif
+
+    /* Connectivity check */
+    UA_DateTime lastConnectivityCheck;
+    UA_Boolean pendingConnectivityCheck;
 };
 
 void
@@ -146,13 +192,35 @@ UA_Client_connectInternal(UA_Client *client, const char *endpointUrl,
                           UA_Boolean endpointsHandshake, UA_Boolean createNewSession);
 
 UA_StatusCode
-UA_Client_getEndpointsInternal(UA_Client *client, size_t* endpointDescriptionsSize,
+UA_Client_connectInternalAsync(UA_Client *client, const char *endpointUrl,
+                               UA_ClientAsyncServiceCallback callback,
+                               void *connected, UA_Boolean endpointsHandshake,
+                               UA_Boolean createNewSession);
+
+UA_StatusCode
+UA_Client_getEndpointsInternal(UA_Client *client,
+                               size_t* endpointDescriptionsSize,
                                UA_EndpointDescription** endpointDescriptions);
 
 /* Receive and process messages until a synchronous message arrives or the
  * timout finishes */
 UA_StatusCode
-receiveServiceResponse(UA_Client *client, void *response, const UA_DataType *responseType,
-                       UA_DateTime maxDate, UA_UInt32 *synchronousRequestId);
+receivePacketAsync(UA_Client *client);
+
+UA_StatusCode
+receiveServiceResponse(UA_Client *client, void *response,
+                       const UA_DataType *responseType, UA_DateTime maxDate,
+                       UA_UInt32 *synchronousRequestId);
 
+UA_StatusCode
+receiveServiceResponseAsync(UA_Client *client, void *response,
+                             const UA_DataType *responseType);
+void
+UA_Client_workerCallback(UA_Client *client, UA_ClientCallback callback,
+                         void *data);
+UA_StatusCode
+UA_Client_delayedCallback(UA_Client *client, UA_ClientCallback callback,
+                          void *data);
+UA_StatusCode
+UA_Client_connect_iterate (UA_Client *client);
 #endif /* UA_CLIENT_INTERNAL_H_ */

+ 8 - 6
src/client/ua_client_subscriptions.c

@@ -690,7 +690,7 @@ UA_Client_Subscriptions_processPublishResponse(UA_Client *client, UA_PublishRequ
 
 static void
 processPublishResponseAsync(UA_Client *client, void *userdata, UA_UInt32 requestId,
-                            void *response, const UA_DataType *responseType) {
+                            void *response) {
     UA_PublishRequest *req = (UA_PublishRequest*)userdata;
     UA_PublishResponse *res = (UA_PublishResponse*)response;
 
@@ -737,7 +737,7 @@ UA_Client_Subscriptions_backgroundPublishInactivityCheck(UA_Client *client) {
             /* Reset activity */
             sub->lastActivity = UA_DateTime_nowMonotonic();
 
-            if (client->config.subscriptionInactivityCallback)
+            if(client->config.subscriptionInactivityCallback)
                 client->config.subscriptionInactivityCallback(client, sub->subscriptionId, sub->context);
             UA_LOG_ERROR(client->config.logger, UA_LOGCATEGORY_CLIENT,
                          "Inactivity for Subscription %u.", sub->subscriptionId);
@@ -767,10 +767,12 @@ UA_Client_Subscriptions_backgroundPublish(UA_Client *client) {
     
         UA_UInt32 requestId;
         client->currentlyOutStandingPublishRequests++;
-        retval = __UA_Client_AsyncService(client, request, &UA_TYPES[UA_TYPES_PUBLISHREQUEST],
-                                          processPublishResponseAsync,
-                                          &UA_TYPES[UA_TYPES_PUBLISHRESPONSE],
-                                          (void*)request, &requestId);
+
+        /* Disable the timeout, it is treat in UA_Client_Subscriptions_backgroundPublishInactivityCheck */
+        retval = __UA_Client_AsyncServiceEx(client, request, &UA_TYPES[UA_TYPES_PUBLISHREQUEST],
+                                            processPublishResponseAsync,
+                                            &UA_TYPES[UA_TYPES_PUBLISHRESPONSE],
+                                            (void*)request, &requestId, 0);
         if(retval != UA_STATUSCODE_GOOD) {
             UA_PublishRequest_delete(request);
             return retval;

+ 213 - 0
src/client/ua_client_worker.c

@@ -0,0 +1,213 @@
+/* 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_util.h"
+#include "ua_client.h"
+#include "ua_client_internal.h"
+
+/**
+ * Worker Threads and Dispatch Queue
+ * ---------------------------------
+ * The worker threads dequeue callbacks from a central Multi-Producer
+ * Multi-Consumer Queue (MPMC). When there are no callbacks, workers go idle.
+ * The condition to wake them up is triggered whenever a callback is
+ * dispatched.
+ *
+ * Future Plans: Use work-stealing to load-balance between cores.
+ * Le, Nhat Minh, et al. "Correct and efficient work-stealing for weak memory
+ * models." ACM SIGPLAN Notices. Vol. 48. No. 8. ACM, 2013. */
+
+/**
+ * Repeated Callbacks
+ * ------------------
+ * Repeated Callbacks are handled by UA_Timer (used in both client and client).
+ * In the multi-threaded case, callbacks are dispatched to workers. Otherwise,
+ * they are executed immediately. */
+
+void UA_Client_workerCallback(UA_Client *client, UA_ClientCallback callback,
+        void *data) {
+    /* Execute immediately */
+    callback(client, data);
+}
+
+/**
+ * Delayed Callbacks
+ * -----------------
+ *
+ * Delayed Callbacks are called only when all callbacks that were dispatched
+ * prior are finished. In the single-threaded case, the callback is added to a
+ * singly-linked list that is processed at the end of the client's main-loop. In
+ * the multi-threaded case, the delay is ensure by a three-step procedure:
+ *
+ * 1. The delayed callback is dispatched to the worker queue. So it is only
+ *    dequeued when all prior callbacks have been dequeued.
+ *
+ * 2. When the callback is first dequeued by a worker, sample the counter of all
+ *    workers. Once all counters have advanced, the callback is ready.
+ *
+ * 3. Check regularly if the callback is ready by adding it back to the dispatch
+ *    queue. */
+
+typedef struct UA_DelayedClientCallback {
+    SLIST_ENTRY(UA_DelayedClientCallback)
+    next;
+    UA_ClientCallback callback;
+    void *data;
+} UA_DelayedClientCallback;
+
+UA_StatusCode UA_Client_delayedCallback(UA_Client *client,
+        UA_ClientCallback callback, void *data) {
+    UA_DelayedClientCallback *dc = (UA_DelayedClientCallback*) UA_malloc(
+            sizeof(UA_DelayedClientCallback));
+    if (!dc)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+
+    dc->callback = callback;
+    dc->data = data;
+    SLIST_INSERT_HEAD(&client->delayedClientCallbacks, dc, next);
+    return UA_STATUSCODE_GOOD;
+}
+
+void
+processDelayedClientCallbacks(UA_Client *client);
+
+void processDelayedClientCallbacks(UA_Client *client) {
+    UA_DelayedClientCallback *dc, *dc_tmp;
+    SLIST_FOREACH_SAFE(dc, &client->delayedClientCallbacks, next, dc_tmp)
+    {
+        SLIST_REMOVE(&client->delayedClientCallbacks, dc,
+                UA_DelayedClientCallback, next);
+        dc->callback(client, dc->data);
+        UA_free(dc);
+    }
+}
+
+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 */
+
+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;
+#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
+    if(timeout){
+        retval = UA_Client_manuallyRenewSecureChannel(client);
+        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_TimerDispatchCallback) UA_Client_workerCallback, 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);
+
+#ifndef UA_ENABLE_MULTITHREADING
+/* Process delayed callbacks when all callbacks and
+ * network events are done */
+    processDelayedClientCallbacks(client);
+#endif
+    return retval;
+}

+ 1 - 1
src/server/ua_services_discovery_multicast.c

@@ -97,7 +97,7 @@ static UA_StatusCode
 multicastListenStop(UA_Server* server) {
     mdnsd_shutdown(server->mdnsDaemon);
     // wake up select
-    write(server->mdnsSocket, "\0", 1);
+    if(write(server->mdnsSocket, "\0", 1)){};
     if(pthread_join(server->mdnsThread, NULL)) {
         UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER,
                      "Multicast error: Can not stop thread.");

+ 23 - 1
src/ua_connection.c

@@ -175,7 +175,7 @@ UA_Connection_processChunks(UA_Connection *connection, void *application,
     return retval;
 }
 
-/* In order to know whether a chunk was processed, we insert an indirection into
+/* In order to know whether a chunk was processed, we insert an redirection into
  * the callback. */
 struct completeChunkTrampolineData {
     UA_Boolean called;
@@ -234,6 +234,28 @@ UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application
     return retval;
 }
 
+UA_StatusCode
+UA_Connection_receiveChunksNonBlocking(UA_Connection *connection, void *application,
+                                    UA_Connection_processChunk processCallback) {
+    struct completeChunkTrampolineData data;
+    data.called = false;
+    data.application = application;
+    data.processCallback = processCallback;
+
+    /* Listen for messages to arrive */
+    UA_ByteString packet = UA_BYTESTRING_NULL;
+    UA_StatusCode retval = connection->recv(connection, &packet, 1);
+
+    if((retval != UA_STATUSCODE_GOOD) && (retval != UA_STATUSCODE_GOODNONCRITICALTIMEOUT))
+        return retval;
+
+    /* Try to process one complete chunk */
+    retval = UA_Connection_processChunks(connection, &data, completeChunkTrampoline, &packet);
+    connection->releaseRecvBuffer(connection, &packet);
+
+    return retval;
+}
+
 void UA_Connection_detachSecureChannel(UA_Connection *connection) {
     UA_SecureChannel *channel = connection->channel;
     if(channel)

+ 19 - 0
src/ua_connection_internal.h

@@ -44,6 +44,21 @@ UA_StatusCode
 UA_Connection_processChunks(UA_Connection *connection, void *application,
                             UA_Connection_processChunk processCallback,
                             const UA_ByteString *packet);
+/*
+ * @param connection The connection
+ * @param message The received message. The content may be overwritten when a
+ *        previsouly received buffer is completed.
+ * @param realloced The Boolean value is set to true if the outgoing message has
+ *        been reallocated from the network layer.
+ * @return Returns UA_STATUSCODE_GOOD or an error code. When an error occurs,
+ *         the ingoing message and the current buffer in the connection are
+ *         freed. */
+UA_StatusCode
+UA_Connection_completeMessages(UA_Connection *connection,
+                               UA_ByteString * UA_RESTRICT message,
+                               UA_Boolean * UA_RESTRICT realloced);
+
+
 
 /* Try to receive at least one complete chunk on the connection. This blocks the
  * current thread up to the given timeout.
@@ -59,6 +74,10 @@ UA_Connection_receiveChunksBlocking(UA_Connection *connection, void *application
                                     UA_Connection_processChunk processCallback,
                                     UA_UInt32 timeout);
 
+UA_StatusCode
+UA_Connection_receiveChunksNonBlocking(UA_Connection *connection, void *application,
+                                    UA_Connection_processChunk processCallback);
+
 /* When a fatal error occurs the Server shall send an Error Message to the
  * Client and close the socket. When a Client encounters one of these errors, it
  * shall also close the socket but does not send an Error Message. After the

+ 4 - 0
tests/CMakeLists.txt

@@ -204,6 +204,10 @@ add_executable(check_client_async client/check_client_async.c $<TARGET_OBJECTS:o
 target_link_libraries(check_client_async ${LIBS})
 add_test_valgrind(client_async ${TESTS_BINARY_DIR}/check_client_async)
 
+add_executable(check_client_async_connect client/check_client_async_connect.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
+target_link_libraries(check_client_async_connect ${LIBS})
+add_test_valgrind(client_async_connect ${TESTS_BINARY_DIR}/check_client_async_connect)
+
 add_executable(check_client_subscriptions client/check_client_subscriptions.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
 target_link_libraries(check_client_subscriptions ${LIBS})
 add_test_valgrind(client_subscriptions ${TESTS_BINARY_DIR}/check_client_subscriptions)

+ 188 - 32
tests/client/check_client_async.c

@@ -12,10 +12,13 @@
 #include "ua_types.h"
 #include "ua_server.h"
 #include "ua_client.h"
+#include "ua_client_highlevel_async.h"
 #include "ua_config_default.h"
 #include "ua_network_tcp.h"
 #include "check.h"
 #include "testing_clock.h"
+#include "testing_networklayers.h"
+#include "client/ua_client_internal.h"
 
 #include "thread_wrapper.h"
 
@@ -26,7 +29,7 @@ UA_ServerNetworkLayer nl;
 THREAD_HANDLE server_thread;
 
 THREAD_CALLBACK(serverloop) {
-    while(running)
+    while (running)
         UA_Server_run_iterate(server, true);
     return 0;
 }
@@ -47,56 +50,209 @@ static void teardown(void) {
     UA_ServerConfig_delete(config);
 }
 
-static void
-asyncReadCallback(UA_Client *client, void *userdata,
-                  UA_UInt32 requestId, const UA_ReadResponse *response) {
-    UA_UInt16 *asyncCounter = (UA_UInt16*)userdata;
+static void asyncReadCallback(UA_Client *client, void *userdata,
+        UA_UInt32 requestId, const UA_ReadResponse *response) {
+    UA_UInt16 *asyncCounter = (UA_UInt16*) userdata;
+    if (response->responseHeader.serviceResult == UA_STATUSCODE_BADTIMEOUT) {
+        (*asyncCounter) = 9999;
+        UA_fakeSleep(10);
+    } else {
+        (*asyncCounter)++;
+        UA_fakeSleep(10);
+    }
+}
+
+static void asyncReadValueAtttributeCallback(UA_Client *client, void *userdata,
+        UA_UInt32 requestId, UA_Variant *var) {
+    UA_UInt16 *asyncCounter = (UA_UInt16*) userdata;
     (*asyncCounter)++;
     UA_fakeSleep(10);
 }
 
-START_TEST(Client_read_async) {
-    UA_Client *client = UA_Client_new(UA_ClientConfig_default);
-    UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
-    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+START_TEST(Client_highlevel_async_readValue)
+    {
+        UA_ClientConfig clientConfig = UA_ClientConfig_default;
+        clientConfig.outStandingPublishRequests = 0;
+
+        UA_Client *client = UA_Client_new(clientConfig);
+
+        UA_StatusCode retval = UA_Client_connect(client,
+                "opc.tcp://localhost:4840");
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        UA_Client_recv = client->connection.recv;
+        client->connection.recv = UA_Client_recvTesting;
 
-    UA_UInt16 asyncCounter = 0;
+        UA_UInt16 asyncCounter = 0;
 
-    UA_ReadRequest rr;
-    UA_ReadRequest_init(&rr);
+        UA_UInt32 reqId = 0;
+        retval = UA_Client_readValueAttribute_async(client,
+                UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME),
+                (UA_ClientAsyncReadValueAttributeCallback) asyncReadValueAtttributeCallback,
+                (void*)&asyncCounter, &reqId);
 
-    UA_ReadValueId rvid;
-    UA_ReadValueId_init(&rvid);
-    rvid.attributeId = UA_ATTRIBUTEID_VALUE;
-    rvid.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
-    rr.nodesToRead = &rvid;
-    rr.nodesToReadSize = 1;
+        /* Process async responses during 1s */
+        UA_Client_run_iterate(client, 999 + 1);
 
-    /* Send 100 requests */
-    for(size_t i = 0; i < 100; i++) {
-        retval = __UA_Client_AsyncService(client, &rr, &UA_TYPES[UA_TYPES_READREQUEST],
-                                          (UA_ClientAsyncServiceCallback)asyncReadCallback,
-                                          &UA_TYPES[UA_TYPES_READRESPONSE], &asyncCounter, NULL);
         ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(asyncCounter, 1);
+
+        /* Simulate network cable unplugged (no response from server) */
+        UA_Client_recvTesting_result = UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
+
+        UA_Client_disconnect(client);
+        UA_Client_delete(client);
     }
+}
+
+
+
+START_TEST(Client_read_async)
+    {
+        UA_Client *client = UA_Client_new(UA_ClientConfig_default);
+        UA_StatusCode retval = UA_Client_connect(client,
+                "opc.tcp://localhost:4840");
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        UA_UInt16 asyncCounter = 0;
+
+        UA_ReadRequest rr;
+        UA_ReadRequest_init(&rr);
+
+        UA_ReadValueId rvid;
+        UA_ReadValueId_init(&rvid);
+        rvid.attributeId = UA_ATTRIBUTEID_VALUE;
+        rvid.nodeId = UA_NODEID_NUMERIC(0,
+                UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
+
+        rr.nodesToRead = &rvid;
+        rr.nodesToReadSize = 1;
+
+        /* Send 100 requests */
+        for (size_t i = 0; i < 100; i++) {
+            retval = __UA_Client_AsyncService(client, &rr,
+                    &UA_TYPES[UA_TYPES_READREQUEST],
+                    (UA_ClientAsyncServiceCallback) asyncReadCallback,
+                    &UA_TYPES[UA_TYPES_READRESPONSE], &asyncCounter, NULL);
+            ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        }
+
+        /* Process async responses during 1s */
+        retval = UA_Client_run_iterate(client, 999);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(asyncCounter, 100);
+
+        UA_Client_disconnect(client);
+        UA_Client_delete(client);
+    }END_TEST
+
+START_TEST(Client_read_async_timed)
+    {
+        UA_ClientConfig clientConfig = UA_ClientConfig_default;
+        clientConfig.outStandingPublishRequests = 0;
+
+        UA_Client *client = UA_Client_new(clientConfig);
+
+        UA_StatusCode retval = UA_Client_connect(client,
+                "opc.tcp://localhost:4840");
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
-    /* Process async responses during 1s */
-    retval = UA_Client_runAsync(client, 999);
-    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
-    ck_assert_uint_eq(asyncCounter, 100);
+        UA_Client_recv = client->connection.recv;
+        client->connection.recv = UA_Client_recvTesting;
 
-    UA_Client_disconnect(client);
-    UA_Client_delete(client);
+        UA_UInt16 asyncCounter = 0;
+
+        UA_ReadRequest rr;
+        UA_ReadRequest_init(&rr);
+
+        UA_ReadValueId rvid;
+        UA_ReadValueId_init(&rvid);
+        rvid.attributeId = UA_ATTRIBUTEID_VALUE;
+        rvid.nodeId = UA_NODEID_NUMERIC(0,
+                UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
+
+        rr.nodesToRead = &rvid;
+        rr.nodesToReadSize = 1;
+
+        retval = __UA_Client_AsyncServiceEx(client, &rr,
+                &UA_TYPES[UA_TYPES_READREQUEST],
+                (UA_ClientAsyncServiceCallback) asyncReadCallback,
+                &UA_TYPES[UA_TYPES_READRESPONSE], &asyncCounter, NULL, 999);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        /* Process async responses during 1s */
+        retval = UA_Client_run_iterate(client, 999 + 1);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(asyncCounter, 1);
+
+        /* Simulate network cable unplugged (no response from server) */
+        UA_Client_recvTesting_result = UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
+
+        retval = __UA_Client_AsyncServiceEx(client, &rr,
+                &UA_TYPES[UA_TYPES_READREQUEST],
+                (UA_ClientAsyncServiceCallback) asyncReadCallback,
+                &UA_TYPES[UA_TYPES_READRESPONSE], &asyncCounter, NULL, 100);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        /* Process async responses during 1s */
+        UA_Client_run_iterate(client, 100 + 1);
+        ck_assert_uint_eq(asyncCounter, 9999);
+
+        UA_Client_disconnect(client);
+        UA_Client_delete(client);
+    }END_TEST
+
+static UA_Boolean inactivityCallbackTriggered = false;
+
+static void inactivityCallback(UA_Client *client) {
+    inactivityCallbackTriggered = true;
 }
-END_TEST
+
+START_TEST(Client_connectivity_check)
+    {
+        UA_ClientConfig clientConfig = UA_ClientConfig_default;
+        clientConfig.outStandingPublishRequests = 0;
+        clientConfig.inactivityCallback = inactivityCallback;
+        clientConfig.connectivityCheckInterval = 1000;
+
+        UA_Client *client = UA_Client_new(clientConfig);
+
+        UA_StatusCode retval = UA_Client_connect(client,
+                "opc.tcp://localhost:4840");
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+        UA_Client_recv = client->connection.recv;
+        client->connection.recv = UA_Client_recvTesting;
+
+        inactivityCallbackTriggered = false;
+
+        retval = UA_Client_run_iterate(client, 1000 + 1);
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(inactivityCallbackTriggered, false);
+
+        /* Simulate network cable unplugged (no response from server) */
+        UA_Client_recvTesting_result = UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
+
+        retval = UA_Client_run_iterate(client,
+                (UA_UInt16) (1000 + 1 + clientConfig.timeout));
+        ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+        ck_assert_uint_eq(inactivityCallbackTriggered, true);
+
+        UA_Client_disconnect(client);
+        UA_Client_delete(client);
+    }END_TEST
 
 static Suite* testSuite_Client(void) {
     Suite *s = suite_create("Client");
     TCase *tc_client = tcase_create("Client Basic");
     tcase_add_checked_fixture(tc_client, setup, teardown);
     tcase_add_test(tc_client, Client_read_async);
-    suite_add_tcase(s,tc_client);
+    tcase_add_test(tc_client, Client_read_async_timed);
+    tcase_add_test(tc_client, Client_connectivity_check);
+    tcase_add_test(tc_client, Client_highlevel_async_readValue);
+
+    suite_add_tcase(s, tc_client);
     return s;
 }
 
@@ -104,7 +260,7 @@ int main(void) {
     Suite *s = testSuite_Client();
     SRunner *sr = srunner_create(s);
     srunner_set_fork_status(sr, CK_NOFORK);
-    srunner_run_all(sr,CK_NORMAL);
+    srunner_run_all(sr, CK_NORMAL);
     int number_failed = srunner_ntests_failed(sr);
     srunner_free(sr);
     return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;

+ 158 - 0
tests/client/check_client_async_connect.c

@@ -0,0 +1,158 @@
+/* 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 <stdio.h>
+#include <stdlib.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
+#include "ua_types.h"
+#include "ua_server.h"
+#include "ua_client.h"
+#include "client/ua_client_internal.h"
+#include "ua_client_highlevel_async.h"
+#include "ua_config_default.h"
+#include "ua_network_tcp.h"
+#include "check.h"
+#include "testing_clock.h"
+#include "testing_networklayers.h"
+#include "thread_wrapper.h"
+
+UA_Server *server;
+UA_ServerConfig *config;
+UA_Boolean running;
+UA_ServerNetworkLayer nl;
+THREAD_HANDLE server_thread;
+
+THREAD_CALLBACK(serverloop) {
+    while(running)
+        UA_Server_run_iterate(server, true);
+    return 0;
+}
+
+static void
+onConnect (UA_Client *Client, void *connected, UA_UInt32 requestId,
+           void *response) {
+    if (UA_Client_getState (Client) == UA_CLIENTSTATE_SESSION)
+        *(UA_Boolean *)connected = true;
+}
+
+static void setup(void) {
+    running = true;
+    config = UA_ServerConfig_new_default();
+    server = UA_Server_new(config);
+    UA_Server_run_startup(server);
+    THREAD_CREATE(server_thread, serverloop);
+    /* Waiting server is up */
+    UA_comboSleep(1000);
+}
+
+static void teardown(void) {
+    running = false;
+    THREAD_JOIN(server_thread);
+    UA_Server_run_shutdown(server);
+    UA_Server_delete(server);
+    UA_ServerConfig_delete(config);
+}
+
+static void
+asyncBrowseCallback(UA_Client *Client, void *userdata,
+                  UA_UInt32 requestId, UA_BrowseResponse *response) {
+    UA_UInt16 *asyncCounter = (UA_UInt16*)userdata;
+    (*asyncCounter)++;
+}
+
+START_TEST(Client_connect_async){
+    UA_StatusCode retval;
+    UA_Client *client = UA_Client_new(UA_ClientConfig_default);
+    UA_Boolean connected = false;
+    UA_Client_connect_async(client, "opc.tcp://localhost:4840", onConnect,
+                            &connected);
+    /*Windows needs time to response*/
+    UA_sleep_ms(100);
+    UA_UInt32 reqId = 0;
+    UA_UInt16 asyncCounter = 0;
+    UA_BrowseRequest bReq;
+    UA_BrowseRequest_init (&bReq);
+    bReq.requestedMaxReferencesPerNode = 0;
+    bReq.nodesToBrowse = UA_BrowseDescription_new ();
+    bReq.nodesToBrowseSize = 1;
+    bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC (0, UA_NS0ID_OBJECTSFOLDER);
+    bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
+    /* Connected gets updated when client is connected */
+
+    do{
+        if(connected) {
+            /* If not connected requests are not sent */
+            UA_Client_sendAsyncBrowseRequest (client, &bReq, asyncBrowseCallback,
+                                              &asyncCounter, &reqId);
+        }
+        /* Manual clock for unit tests */
+        UA_comboSleep(20);
+        retval = UA_Client_run_iterate(client, 0);
+        /*fix infinite loop, but why is server occasionally shut down in Appveyor?!*/
+        if(retval == UA_STATUSCODE_BADCONNECTIONCLOSED)
+            break;
+    } while(reqId < 10);
+
+    UA_BrowseRequest_deleteMembers(&bReq);
+    ck_assert_uint_eq(connected, true);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+    /* With default setting the client uses 4 requests to connect */
+    ck_assert_uint_eq(asyncCounter, 10-4);
+    UA_Client_disconnect(client);
+    UA_Client_delete (client);
+}
+END_TEST
+
+START_TEST(Client_no_connection) {
+    UA_Client *client = UA_Client_new(UA_ClientConfig_default);
+
+    UA_Boolean connected = false;
+    UA_StatusCode retval = UA_Client_connect_async(client, "opc.tcp://localhost:4840", onConnect,
+            &connected);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
+
+    UA_Client_recv = client->connection.recv;
+    client->connection.recv = UA_Client_recvTesting;
+    //simulating unconnected server
+    UA_Client_recvTesting_result = UA_STATUSCODE_BADCONNECTIONCLOSED;
+    retval = UA_Client_run_iterate(client, 0);
+    ck_assert_uint_eq(retval, UA_STATUSCODE_BADCONNECTIONCLOSED);
+    UA_Client_disconnect(client);
+    UA_Client_delete(client);
+}
+END_TEST
+
+START_TEST(Client_without_run_iterate) {
+    UA_Client *client = UA_Client_new(UA_ClientConfig_default);
+    UA_Boolean connected = false;
+    UA_Client_connect_async(client, "opc.tcp://localhost:4840", onConnect,
+                            &connected);
+    UA_Client_delete(client);
+}
+END_TEST
+
+static Suite* testSuite_Client(void) {
+    Suite *s = suite_create("Client");
+    TCase *tc_client_connect = tcase_create("Client Connect Async");
+    tcase_add_checked_fixture(tc_client_connect, setup, teardown);
+    tcase_add_test(tc_client_connect, Client_connect_async);
+    tcase_add_test(tc_client_connect, Client_no_connection);
+    tcase_add_test(tc_client_connect, Client_without_run_iterate);
+    suite_add_tcase(s,tc_client_connect);
+    return s;
+}
+
+int main(void) {
+    Suite *s = testSuite_Client();
+    SRunner *sr = srunner_create(s);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    srunner_run_all(sr, CK_NORMAL);
+    int number_failed = srunner_ntests_failed(sr);
+    srunner_free(sr);
+    return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 14 - 14
tests/client/check_client_subscriptions.c

@@ -90,7 +90,7 @@ START_TEST(Client_subscription) {
 
     notificationReceived = false;
 
-    retval = UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    retval = UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(notificationReceived, true);
 
@@ -169,13 +169,13 @@ START_TEST(Client_subscription_createDataChanges) {
 
     notificationReceived = false;
     countNotificationReceived = 0;
-    retval = UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    retval = UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(notificationReceived, true);
     ck_assert_uint_eq(countNotificationReceived, 2);
 
     notificationReceived = false;
-    retval = UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    retval = UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(notificationReceived, true);
     ck_assert_uint_eq(countNotificationReceived, 3);
@@ -293,13 +293,13 @@ START_TEST(Client_subscription_connectionClose) {
 
     UA_fakeSleep((UA_UInt32)publishingInterval + 1);
 
-    retval = UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 60));
+    retval = UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 60));
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
 
     /* Simulate BADCONNECTIONCLOSE */
     UA_Client_recvTesting_result = UA_STATUSCODE_BADCONNECTIONCLOSED;
 
-    retval = UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 60));
+    retval = UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 60));
     ck_assert_uint_eq(retval, UA_STATUSCODE_BADCONNECTIONCLOSED);
 
     UA_Client_disconnect(client);
@@ -337,12 +337,12 @@ START_TEST(Client_subscription_without_notification) {
     UA_fakeSleep((UA_UInt32)publishingInterval + 1);
 
     notificationReceived = false;
-    retval = UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    retval = UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(notificationReceived, true);
 
     notificationReceived = false;
-    retval = UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    retval = UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_uint_eq(notificationReceived, false);
 
@@ -420,27 +420,27 @@ START_TEST(Client_subscription_async_sub) {
     countNotificationReceived = 0;
 
     notificationReceived = false;
-    UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(notificationReceived, true);
     ck_assert_uint_eq(countNotificationReceived, 1);
 
     notificationReceived = false;
-    UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(notificationReceived, true);
     ck_assert_uint_eq(countNotificationReceived, 2);
 
     notificationReceived = false;
-    UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(notificationReceived, true);
     ck_assert_uint_eq(countNotificationReceived, 3);
 
     notificationReceived = false;
-    UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(notificationReceived, true);
     ck_assert_uint_eq(countNotificationReceived, 4);
 
     notificationReceived = false;
-    UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(notificationReceived, true);
     ck_assert_uint_eq(countNotificationReceived, 5);
 
@@ -449,14 +449,14 @@ START_TEST(Client_subscription_async_sub) {
     notificationReceived = false;
     /* Simulate network cable unplugged (no response from server) */
     UA_Client_recvTesting_result = UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
-    UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 1));
+    UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 1));
     ck_assert_uint_eq(notificationReceived, false);
     ck_assert_uint_eq(callbackClientState, UA_CLIENTSTATE_SESSION);
 
     /* Simulate network cable unplugged (no response from server) */
     ck_assert_uint_eq(inactivityCallbackCalled, false);
     UA_Client_recvTesting_result = UA_STATUSCODE_GOODNONCRITICALTIMEOUT;
-    UA_Client_runAsync(client, (UA_UInt16)clientConfig.timeout);
+    UA_Client_run_iterate(client, (UA_UInt16)clientConfig.timeout);
     ck_assert_uint_eq(inactivityCallbackCalled, true);
     ck_assert_uint_eq(callbackClientState, UA_CLIENTSTATE_SESSION);
 

+ 1 - 1
tests/server/check_monitoreditem_filter.c

@@ -130,7 +130,7 @@ static UA_StatusCode waitForNotification(UA_UInt32 notifications, UA_UInt32 maxT
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     for (UA_UInt32 i = 0; i < maxTries; ++i) {
         UA_fakeSleep((UA_UInt32)publishingInterval + 100);
-        retval = UA_Client_runAsync(client, (UA_UInt16)(publishingInterval + 100));
+        retval = UA_Client_run_iterate(client, (UA_UInt16)(publishingInterval + 100));
         if (retval != UA_STATUSCODE_GOOD)
             return retval;
         if (countNotificationReceived == notifications)