1 /**************************************************************************/
2 /* */
3 /* Copyright (c) Microsoft Corporation. All rights reserved. */
4 /* */
5 /* This software is licensed under the Microsoft Software License */
6 /* Terms for Microsoft Azure RTOS. Full text of the license can be */
7 /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
8 /* and in the root directory of this software. */
9 /* */
10 /**************************************************************************/
11
12 #include <stdio.h>
13
14 #include "nx_azure_iot_hub_client.h"
15
16 #ifndef SAMPLE_MAX_EXPONENTIAL_BACKOFF_IN_SEC
17 #define SAMPLE_MAX_EXPONENTIAL_BACKOFF_IN_SEC (10 * 60)
18 #endif /* SAMPLE_MAX_EXPONENTIAL_BACKOFF_IN_SEC */
19
20 #ifndef SAMPLE_INITIAL_EXPONENTIAL_BACKOFF_IN_SEC
21 #define SAMPLE_INITIAL_EXPONENTIAL_BACKOFF_IN_SEC (3)
22 #endif /* SAMPLE_INITIAL_EXPONENTIAL_BACKOFF_IN_SEC */
23
24 #ifndef SAMPLE_MAX_EXPONENTIAL_BACKOFF_JITTER_PERCENT
25 #define SAMPLE_MAX_EXPONENTIAL_BACKOFF_JITTER_PERCENT (60)
26 #endif /* SAMPLE_MAX_EXPONENTIAL_BACKOFF_JITTER_PERCENT */
27
28 VOID sample_connection_monitor(NX_IP *ip_ptr, NX_AZURE_IOT_HUB_CLIENT *iothub_client_ptr, UINT sample_status,
29 UINT (*iothub_init)(NX_AZURE_IOT_HUB_CLIENT *hub_client_ptr));
30
31 static UINT exponential_retry_count;
32
33 static UINT iothub_init_count = 0;
34
exponential_backoff_with_jitter()35 static UINT exponential_backoff_with_jitter()
36 {
37 double jitter_percent = (SAMPLE_MAX_EXPONENTIAL_BACKOFF_JITTER_PERCENT / 100.0) * (rand() / ((double)RAND_MAX));
38 UINT base_delay = SAMPLE_MAX_EXPONENTIAL_BACKOFF_IN_SEC;
39 uint64_t delay;
40
41 if (exponential_retry_count < (sizeof(UINT) * 8))
42 {
43 delay = (uint64_t)((1 << exponential_retry_count) * SAMPLE_INITIAL_EXPONENTIAL_BACKOFF_IN_SEC);
44 if (delay <= (UINT)(-1))
45 {
46 base_delay = (UINT)delay;
47 }
48 }
49
50 if (base_delay > SAMPLE_MAX_EXPONENTIAL_BACKOFF_IN_SEC)
51 {
52 base_delay = SAMPLE_MAX_EXPONENTIAL_BACKOFF_IN_SEC;
53 }
54 else
55 {
56 exponential_retry_count++;
57 }
58
59 return((UINT)(base_delay * (1 + jitter_percent)) * NX_IP_PERIODIC_RATE) ;
60 }
61
exponential_backoff_reset()62 static VOID exponential_backoff_reset()
63 {
64 exponential_retry_count = 0;
65 }
66
67 /**
68 *
69 * Connection state
70 *
71 *
72 * +--------------+ +--------------+ +--------------+
73 * | | INIT | | NETWORK | |
74 * | | SUCCESS | | GOOD | |
75 * | INIT | | NETWORK | | CONNECT |
76 * | +-------------> CHECK +---------------------> +---------+
77 * | | | | | | |
78 * | | | | | | |
79 * +------^-------+ +------^-------+ +------+-------+ |
80 * | | | |
81 * | | CONNECT FAIL | |
82 * | | +--------------------------+ |
83 * | | | CONNECTED |
84 * | RECONNECT | | |
85 * | | | |
86 * | | +------v-------+ +--------------+ |
87 * | REINITIALIZE | | | | | |
88 * | +---+ | DISCONNECT | | |
89 * | | DISCONNECTED | | CONNECTED | |
90 * | | <--------------+ <------+
91 * +--------------------------------+ | | |
92 * | | | |
93 * +--------------+ +---^------+---+
94 * | |(TELEMETRY |
95 * | | C2D |
96 * | | COMMAND | METHOD |
97 * +------+ PROPERTIES | DEVICE TWIN)
98 *
99 *
100 *
101 */
sample_connection_monitor(NX_IP * ip_ptr,NX_AZURE_IOT_HUB_CLIENT * hub_client_ptr,UINT connection_status,UINT (* iothub_init)(NX_AZURE_IOT_HUB_CLIENT * hub_client_ptr))102 VOID sample_connection_monitor(NX_IP *ip_ptr, NX_AZURE_IOT_HUB_CLIENT *hub_client_ptr, UINT connection_status,
103 UINT (*iothub_init)(NX_AZURE_IOT_HUB_CLIENT *hub_client_ptr))
104 {
105 UINT loop = NX_TRUE;
106 ULONG gateway_address;
107
108 /* Check parameters. */
109 if ((ip_ptr == NX_NULL) || (hub_client_ptr == NX_NULL) || (iothub_init == NX_NULL))
110 {
111 return;
112 }
113
114 /* Check if connected. */
115 if (connection_status == NX_SUCCESS)
116 {
117
118 /* Reset the exponential. */
119 exponential_backoff_reset();
120 }
121 else
122 {
123
124 /* Disconnect. */
125 if (connection_status != NX_AZURE_IOT_NOT_INITIALIZED)
126 {
127 nx_azure_iot_hub_client_disconnect(hub_client_ptr);
128 }
129
130 /* Recover. */
131 while (loop)
132 {
133 switch (connection_status)
134 {
135
136 /* Something bad has happened with client state, we need to re-initialize it. */
137 case NX_DNS_QUERY_FAILED :
138 case NXD_MQTT_ERROR_BAD_USERNAME_PASSWORD :
139 case NXD_MQTT_ERROR_NOT_AUTHORIZED :
140 {
141
142 /* Deinitialize iot hub client. */
143 nx_azure_iot_hub_client_deinitialize(hub_client_ptr);
144 }
145 /* fallthrough */
146
147 case NX_AZURE_IOT_NOT_INITIALIZED:
148 {
149 if (iothub_init_count++)
150 {
151 printf("Re-initializing iothub connection, after backoff\r\n");
152 tx_thread_sleep(exponential_backoff_with_jitter());
153 }
154
155 /* Initialize iot hub. */
156 if (iothub_init(hub_client_ptr))
157 {
158 connection_status = NX_AZURE_IOT_NOT_INITIALIZED;
159 }
160 else
161 {
162
163 /* Wait for network. */
164 while (nx_ip_gateway_address_get(ip_ptr, &gateway_address))
165 {
166 tx_thread_sleep(NX_IP_PERIODIC_RATE);
167 }
168
169 /* Connect to iot hub. */
170 connection_status = nx_azure_iot_hub_client_connect(hub_client_ptr, NX_FALSE, NX_WAIT_FOREVER);
171 }
172 }
173 break;
174
175 case NX_AZURE_IOT_SAS_TOKEN_EXPIRED:
176 {
177 printf("SAS token expired\r\n");
178 }
179 /* fallthrough */
180
181 default :
182 {
183 printf("Reconnecting iothub, after backoff\r\n");
184
185 tx_thread_sleep(exponential_backoff_with_jitter());
186
187 /* Wait for network. */
188 while (nx_ip_gateway_address_get(ip_ptr, &gateway_address))
189 {
190 tx_thread_sleep(NX_IP_PERIODIC_RATE);
191 }
192
193 connection_status = nx_azure_iot_hub_client_connect(hub_client_ptr, NX_FALSE, NX_WAIT_FOREVER);
194 }
195 break;
196 }
197
198 /* Check status. */
199 if (connection_status == NX_SUCCESS)
200 {
201 return;
202 }
203 }
204 }
205 }
206