-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathauto_reconnect_example.cpp
More file actions
267 lines (228 loc) · 8.48 KB
/
auto_reconnect_example.cpp
File metadata and controls
267 lines (228 loc) · 8.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/**
* ESP32 SignalR Auto-Reconnect Example
*
* This example demonstrates how to use the auto-reconnect feature
* with skip negotiation for WebSocket-only connections.
*/
#include "hub_connection_builder.h"
#include "signalr_client_config.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
static const char* TAG = "SIGNALR_EXAMPLE";
// Global connection object
static signalr::hub_connection* g_connection = nullptr;
/**
* Configure and create a SignalR connection with auto-reconnect
*/
void setup_signalr_connection()
{
ESP_LOGI(TAG, "Setting up SignalR connection with auto-reconnect...");
// Create connection with automatic reconnect enabled
// This uses the default reconnect delays: 0s, 2s, 10s, 30s (matching C# and JS clients)
auto connection = signalr::hub_connection_builder()
.with_url("wss://your-signalr-server.com/signalrhub")
.skip_negotiation() // Skip negotiation - directly use WebSocket
.with_automatic_reconnect() // Enable auto-reconnect with default delays
.build();
// Alternatively, use custom reconnect delays:
// std::vector<std::chrono::milliseconds> custom_delays = {
// std::chrono::seconds(0),
// std::chrono::seconds(2),
// std::chrono::seconds(10),
// std::chrono::seconds(30)
// };
// auto connection = signalr::hub_connection_builder()
// .with_url("wss://your-server.com/hub")
// .skip_negotiation()
// .with_automatic_reconnect(custom_delays)
// .build();
// Set up disconnected callback
connection.set_disconnected([](std::exception_ptr ex) {
try {
if (ex) {
std::rethrow_exception(ex);
}
ESP_LOGW(TAG, "Connection closed gracefully");
} catch (const std::exception& e) {
ESP_LOGE(TAG, "Connection lost: %s", e.what());
}
ESP_LOGI(TAG, "Auto-reconnect will attempt to restore the connection...");
});
// Register server method handlers
connection.on("ReceiveMessage", [](const std::vector<signalr::value>& args) {
if (args.size() >= 2 && args[0].is_string() && args[1].is_string()) {
std::string user = args[0].as_string();
std::string message = args[1].as_string();
ESP_LOGI(TAG, "Message from %s: %s", user.c_str(), message.c_str());
}
});
connection.on("UpdateStatus", [](const std::vector<signalr::value>& args) {
if (!args.empty() && args[0].is_string()) {
ESP_LOGI(TAG, "Status update: %s", args[0].as_string().c_str());
}
});
connection.on("DeviceCommand", [](const std::vector<signalr::value>& args) {
if (!args.empty() && args[0].is_string()) {
std::string command = args[0].as_string();
ESP_LOGI(TAG, "Received device command: %s", command.c_str());
// Handle the command
if (command == "reboot") {
ESP_LOGW(TAG, "Reboot command received!");
// Implement reboot logic
} else if (command == "status") {
ESP_LOGI(TAG, "Status command received, sending status...");
// Send status back to server
}
}
});
// Store connection for later use
g_connection = &connection;
// Start the connection
ESP_LOGI(TAG, "Starting SignalR connection...");
connection.start([](std::exception_ptr ex) {
if (ex) {
try {
std::rethrow_exception(ex);
} catch (const std::exception& e) {
ESP_LOGE(TAG, "Failed to start connection: %s", e.what());
}
} else {
ESP_LOGI(TAG, "SignalR connection started successfully!");
ESP_LOGI(TAG, "Connection ID: %s", g_connection->get_connection_id().c_str());
// Send initial message to server
send_device_online_notification();
}
});
}
/**
* Send a message to the server
*/
void send_message_to_server(const std::string& method_name, const std::string& message)
{
if (g_connection == nullptr) {
ESP_LOGE(TAG, "Connection not initialized");
return;
}
if (g_connection->get_connection_state() != signalr::connection_state::connected) {
ESP_LOGW(TAG, "Connection not in connected state, message will be dropped");
return;
}
std::vector<signalr::value> args { message };
g_connection->send(method_name, args, [method_name](std::exception_ptr ex) {
if (ex) {
try {
std::rethrow_exception(ex);
} catch (const std::exception& e) {
ESP_LOGE(TAG, "Failed to send %s: %s", method_name.c_str(), e.what());
}
} else {
ESP_LOGI(TAG, "Successfully sent %s", method_name.c_str());
}
});
}
/**
* Invoke a server method and wait for response
*/
void invoke_server_method(const std::string& method_name, int param)
{
if (g_connection == nullptr) {
ESP_LOGE(TAG, "Connection not initialized");
return;
}
if (g_connection->get_connection_state() != signalr::connection_state::connected) {
ESP_LOGW(TAG, "Connection not in connected state");
return;
}
std::vector<signalr::value> args { param };
g_connection->invoke(method_name, args,
[method_name](const signalr::value& result, std::exception_ptr ex) {
if (ex) {
try {
std::rethrow_exception(ex);
} catch (const std::exception& e) {
ESP_LOGE(TAG, "Failed to invoke %s: %s", method_name.c_str(), e.what());
}
} else {
if (result.is_double()) {
ESP_LOGI(TAG, "%s returned: %f", method_name.c_str(), result.as_double());
} else if (result.is_string()) {
ESP_LOGI(TAG, "%s returned: %s", method_name.c_str(), result.as_string().c_str());
} else {
ESP_LOGI(TAG, "%s completed successfully", method_name.c_str());
}
}
});
}
/**
* Send device online notification
*/
void send_device_online_notification()
{
ESP_LOGI(TAG, "Sending device online notification...");
send_message_to_server("DeviceOnline", "ESP32 device is now online");
}
/**
* Stop the connection gracefully
*/
void stop_signalr_connection()
{
if (g_connection == nullptr) {
return;
}
ESP_LOGI(TAG, "Stopping SignalR connection...");
g_connection->stop([](std::exception_ptr ex) {
if (ex) {
try {
std::rethrow_exception(ex);
} catch (const std::exception& e) {
ESP_LOGE(TAG, "Error while stopping: %s", e.what());
}
} else {
ESP_LOGI(TAG, "Connection stopped successfully");
}
});
}
/**
* Main application task
*/
void app_main(void)
{
// Initialize WiFi first (not shown here)
// wifi_init_sta();
// Wait for WiFi connection
ESP_LOGI(TAG, "Waiting for WiFi connection...");
// wait_for_wifi();
// Setup SignalR connection with auto-reconnect
setup_signalr_connection();
// Main loop - send periodic updates
while (true) {
vTaskDelay(pdMS_TO_TICKS(30000)); // Wait 30 seconds
if (g_connection &&
g_connection->get_connection_state() == signalr::connection_state::connected) {
// Send periodic heartbeat or status update
send_message_to_server("Heartbeat", "Device is alive");
// Or invoke a method with parameters
int sensor_value = 42; // Read from actual sensor
invoke_server_method("UpdateSensorData", sensor_value);
} else {
ESP_LOGW(TAG, "Connection not ready, skipping update (auto-reconnect is active)");
}
}
}
/**
* Simulation: Test reconnection by temporarily disabling WiFi
*/
void test_reconnection()
{
ESP_LOGI(TAG, "Testing reconnection...");
// Simulate network loss
ESP_LOGW(TAG, "Simulating network loss...");
// wifi_disconnect();
vTaskDelay(pdMS_TO_TICKS(10000)); // Wait 10 seconds
// Restore network
ESP_LOGI(TAG, "Restoring network...");
// wifi_reconnect();
// Auto-reconnect should kick in automatically
ESP_LOGI(TAG, "Auto-reconnect should now attempt to restore the connection");
}