// // Created by misaki on 2025/9/2. // #pragma once #include #include #include #include #include #include "esp_websocket_client.h" #include "cJSON.h" #include "ThreadManager.h" #include "WifiConnectors.h" // WebSocket事件类型 enum class WebSocketEvent { CONNECTED, DISCONNECTED, DATA_RECEIVED, ERROR }; // WebSocket配置 struct WebSocketConfig { std::string uri; // WebSocket服务器URI int reconnect_interval = 5000; // 重连间隔(ms) int heartbeat_interval = 30000; // 心跳间隔(ms) int max_reconnect_attempts = 10; // 最大重连次数 bool auto_reconnect = true; // 是否自动重连 }; // JSON数据回调 using JsonDataCallback = std::function; // 事件回调 using EventCallback = std::function; // 在此说明一下上面的两个回调函数的作用,主要目的还是为了解耦,在handleReceivedData有对这两个回调的调用 // 对于JSON数据回调,如果来自客户端的数据被解析为了数据,那么就会调用JSON数据回调,并将解析后的json数据传给JSON数据回调,回调函数内部只需要访问并释放json数据 // 对于WebSocket事件回调,会有所不同,重点需要关注DATA_RECEIVED事件,这是非json数据的情况,其他的见下面的示例 // 实际上对于本项目,几乎99%的情况都是只使用JSON数据回调 /* 回调函数示例: // JSON数据回调示例 void onJsonData(cJSON* json) { // 处理接收到的JSON数据 cJSON* type = cJSON_GetObjectItem(json, "type"); if (type && cJSON_IsString(type)) { ESP_LOGI("App", "Received message type: %s", type->valuestring); if (strcmp(type->valuestring, "sensor_data") == 0) { cJSON* value = cJSON_GetObjectItem(json, "value"); if (value && cJSON_IsNumber(value)) { ESP_LOGI("App", "Sensor value: %.2f", value->valuedouble); } } } cJSON_Delete(json); } // 事件回调示例 void onWebSocketEvent(WebSocketEvent event, const std::string& message) { switch (event) { case WebSocketEvent::CONNECTED: ESP_LOGI("App", "WebSocket connected: %s", message.c_str()); break; case WebSocketEvent::DISCONNECTED: ESP_LOGI("App", "WebSocket disconnected: %s", message.c_str()); break; case WebSocketEvent::DATA_RECEIVED: ESP_LOGI("App", "Received raw data: %s", message.c_str()); break; case WebSocketEvent::ERROR: ESP_LOGE("App", "WebSocket error: %s", message.c_str()); break; } } */ class WebSocketManager { public: // 获取单例实例 static WebSocketManager* getInstance(); // 删除拷贝构造函数和赋值运算符 WebSocketManager(const WebSocketManager&) = delete; WebSocketManager& operator=(const WebSocketManager&) = delete; // 初始化WebSocket管理器 bool initialize(const WebSocketConfig& ws_config); // 连接到WebSocket服务器 bool connect(); // 断开连接 void disconnect(); // 发送JSON数据,注意此成员函数会释放json数据,调用之后请不要再次释放json数据 bool sendJson(cJSON* json); // 发送原始字符串数据 bool sendRaw(const std::string& data); // 设置JSON数据回调 void setJsonCallback(const JsonDataCallback &callback); // 设置事件回调 void setEventCallback(const EventCallback &callback); // 获取连接状态 [[nodiscard]] bool isConnected() const; // 获取配置信息 [[nodiscard]] WebSocketConfig getConfig() const; // 更新配置 void updateConfig(const WebSocketConfig& ws_config); // 获取统计信息 struct Stats { uint32_t messages_sent; /// 发送消息数 uint32_t messages_received; /// 接收到的消息数 uint32_t connection_attempts; /// 连接尝试次数 uint32_t successful_connections; /// 成功连接次数 }; [[nodiscard]] Stats getStats() const; private: WebSocketManager(); // 私有构造函数 ~WebSocketManager(); // 私有析构函数 /** * WebSocket事件处理函数 * @param handler_args 处理参数 * @param base 事件基础 * @param event_id 事件ID * @param event_data 事件数据 */ static void websocketEventHandler(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data); /** * 处理接收到的数据 * @param data 数据 * @param len 数据长度 */ void handleReceivedData(const char* data, int len) const; // 重连线程函数 void reconnectThread(); // 心跳线程函数 void heartbeatThread(); // 发送线程函数 void sendThread(); // 创建WebSocket客户端 bool createWebSocketClient(); // 销毁WebSocket客户端 void destroyWebSocketClient(); // 处理WiFi事件 static void wifiEventHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); private: static WebSocketManager* instance; static std::mutex instance_mutex; esp_websocket_client_handle_t client; WebSocketConfig config; // 线程相关 std::thread reconnect_thread; /// 重连线程 std::thread heartbeat_thread; /// 心跳线程 std::thread send_thread; /// 发送线程 bool threads_running; /// 线程运行标志 // 回调函数 JsonDataCallback json_callback; /// JSON数据回调 EventCallback event_callback; /// 事件回调 // 状态变量 bool connected; bool connecting; uint32_t reconnect_attempts; // 统计信息 Stats stats; // 发送队列 std::queue send_queue; std::mutex queue_mutex; std::condition_variable queue_cv; // WiFi实例 WifiConnectors* wifi; // sn码和设备信息 static std::string sn; /// sn码会在设备首次连网后连接服务器后经过服务器计算后返回唯一的sn码 /// 在首次连接服务器后会提交设备的芯片序列号和mac地址,以用于计算唯一sn码 /// 在计算后,服务器首先会把计算好的sn码返回给设备,并保存起来 /// 客户端会将服务器返回的sn码存在设备flash当中 /// 这样客户端和服务端之间就都有sn码了 /// 当客户端再次连接服务器的时候,会先从flash中读取sn码,如果sn码存在,则将sn码发送给服务器以建立连接 /// 如此下来服务器也方便找到设备保存的信息 std::string device_info = "esp32s3"; /// 可以根据不同的设备进行修改,在本项目中使用的是esp32s3 };