ws ln提交
authorhcj <1224667075@qq.com>
Fri, 19 Sep 2025 10:40:33 +0000 (18:40 +0800)
committerhcj <1224667075@qq.com>
Fri, 19 Sep 2025 10:40:33 +0000 (18:40 +0800)
code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/brandy-native-js-ln/libwebsocket.a
code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket.h
code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket_opts.h
code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/websocket/websocket_module.cpp
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c
code/sdk/zhihui/websocket/test/ws_multi_test.c

index 93b5e6f..909c556 100644 (file)
Binary files a/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/brandy-native-js-ln/libwebsocket.a and b/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/brandy-native-js-ln/libwebsocket.a differ
index ef9fdf3..4475dca 100644 (file)
@@ -25,8 +25,6 @@ extern "C" {
 int WebSocketClientConstructor(WebSocketOnOpenCallback onOpen, WebSocketOnMessageCallback onMessage,
     WebSocketOnErrorCallback onError, WebSocketOnCloseCallback onclose);
 
-void WebSocketClientSetProtocol(int client, const char *protocol);
-
 #ifdef WEBSOCKET_SSL_ENABLE
 /**
  * @brief Set server certificate for ssl connection.
@@ -43,9 +41,10 @@ int WebSocketClientSetServerCert(int client, const char *serverCert, int serverC
  *
  * @param client client fd.
  * @param url URL for the client to connect to the server.
+ * @param option string for websocket custom header, must end with '\r\n'.
  * @return 0 if success; non-0 otherwise. For details about error codes, see {@link WebsocketErrCode}.
  */
-int WebSocketClientConnect(int client, const char *url);
+int WebSocketClientConnect(int client, const char *url, const char *option);
 
 /**
  * @brief Sends data from the client to the server.
@@ -88,7 +87,7 @@ int WebSocketClientSendBinEnd(int client, const char *data, size_t length);
 
 /**
  * @brief Closes a WebSocket connection.
- *
+ * @detailed do not use it under onMessage/onOpen/OnError callback
  * @param client Client fd.
  * @return 0 if success; non-0 otherwise. For details about error codes, see {@link WebsocketErrCode}.
  */
index 5af2226..4b2b301 100644 (file)
@@ -155,7 +155,7 @@ JSIValue WebsocketModule::Connect(const JSIValue thisVal, const JSIValue *args,
         return JSI::CreateUndefined();\r
     }\r
 \r
-    int ret = WebSocketClientConnect(client, url);\r
+    int ret = WebSocketClientConnect(client, url,NULL);\r
     if (ret != 0) {\r
         HILOG_ERROR(HILOG_MODULE_ACE, "WebSocketClientConnect fail!");\r
         return JSI::CreateUndefined();\r
index ddf37ce..32c6e07 100644 (file)
@@ -1,34 +1,99 @@
 #include "ln_api.h"
 #include "net_websocket.h"
-#include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include "cJSON.h" // Added for JSON parsing
+#include "custom_utils.h"
+#include "soc_osal.h"
 
 /*
  * - g_ws_client_fd    当前 WebSocket 客户端fd
- * - g_ws_connected    握手成功后置1,关闭/异常置0
+ * - g_ws_connected    连接状态:0=未连接,1=连接中,2=连接成功
  * - g_ws_running      心跳线程运行标志
  */
+
+/* 消息队列相关定义 */
+#define WS_MSG_QUEUE_COUNT 16
+#define WS_MSG_TYPE_SEND_TEXT 0x01
+#define WS_MSG_TYPE_FORCE_RECONNECT 0x02
+#define WS_MSG_TYPE_STOP 0x03
+#define WS_MSG_TYPE_DATA_MESSAGE 0x04
+
+/* 连接状态定义 */
+#define WS_STATE_DISCONNECTED 0  // 未连接
+#define WS_STATE_CONNECTING   1  // 连接中
+#define WS_STATE_CONNECTED    2  // 连接成功
+
+/* WebSocket命令枚举 */
+typedef enum {
+    WS_CMD_UNKNOWN = 0,
+    WS_CMD_CHARACTER_CHANGE,
+    WS_CMD_CHARACTER_CONFIG_CHANGE,
+    WS_CMD_SLEEP_MODE_START,
+    WS_CMD_SLEEP_MODE_END,
+    WS_CMD_DEVICE_BIND,
+    WS_CMD_DEVICE_UNBIND,
+    WS_CMD_SCHEDULE_REMINDER,
+    WS_CMD_SCHEDULE_REMINDER_CFG,
+    WS_CMD_DOWNLOAD_EMOJI,
+    WS_CMD_DELETE_EMOJI,
+    WS_CMD_PING,
+    WS_CMD_PONG,
+    WS_CMD_MAX
+} ws_command_t;
+
+typedef struct {
+    uint8_t msg_type;
+    uint16_t data_len;
+    uint8_t *data;
+} ws_msg_info_t;
+
+/* 统一的消息数据结构 */
+typedef struct {
+    char device_id[64];
+    uint32_t timestamp;
+    char message_id[64];
+    ws_command_t command;
+    char command_str[32];
+    char *raw_json;  // 原始JSON字符串,用于需要完整数据的场景
+} ws_data_msg_t;
+
 static int g_ws_client_fd = -1;
-static volatile int g_ws_connected = 0;
-static pthread_t g_ws_heartbeat_thread = 0;
-static pthread_t g_ws_supervisor_thread = 0;
+static volatile int g_ws_connected = WS_STATE_DISCONNECTED;
+static uint32_t g_ws_main_thread = 0;
 static volatile int g_ws_running = 0;
 static volatile unsigned int g_ws_reconnect_count = 0; /* 断线后递增,用于URL reconnect参数 */
+static unsigned long g_ws_msg_queue = 0xFFFFFFFF;
+
+static void ln_ws_build_url(char *out, size_t out_size);
+int ln_ws_send_ping(void);
+static ws_command_t ln_ws_parse_command_string(const char *command_str);
+static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_msg);
+static int ln_ws_queue_data_message(const ws_data_msg_t *data_msg);
+static void ln_ws_free_data_message(ws_data_msg_t *data_msg);
+
+/* 队列创建函数 */
+void ln_ws_queue_init(void)
+{
+    osal_msg_queue_create("ws_queue", WS_MSG_QUEUE_COUNT, &g_ws_msg_queue, 0, sizeof(ws_msg_info_t));
+}
 
 /* 握手成功回调:标记连接就绪 */
 static void ln_ws_on_open(int client)
 {
     (void)client;
-    g_ws_connected = 1;
+    printf("WebSocketClientConstructor ln_ws_on_open\r\n");
+    g_ws_connected = WS_STATE_CONNECTED;
+    printf("[LN][WS] 连接状态: 连接成功\n");
 }
 
 /*
  * 收到服务器下发的文本消息
  * - 上层可根据业务定义的 JSON 字段进行再分发
  */
+
+//  [LN][WS] on_message: {"deviceId":"860132060562402","timestamp":1758275259,"messageId":"0fb3719adca2472c8f0909d38df87666","command":"Pong"}
 static void ln_ws_on_message(int client, const char *data, size_t length, bool isFinished)
 {
     (void)client;
@@ -39,37 +104,17 @@ static void ln_ws_on_message(int client, const char *data, size_t length, bool i
     memcpy(buf, data, length);
     buf[length] = '\0';
 
-    int dispatch_cmd = LN_WS_RECV_UNKNOWN;
-
-    /* 简单判断 */
+    /* 统一的消息解析处理 */
+    printf("WebSocketClientConstructor data\r\n");
     if (length >= 2 && buf[0] == '{' && buf[length - 1] == '}') {
-        /* 使用 cJSON 解析 command 字段 */
-        cJSON *root = cJSON_Parse(buf);
-        if (root) {
-            cJSON *cmd = cJSON_GetObjectItem(root, "command");
-            if (cmd && cJSON_IsString(cmd) && cmd->valuestring) {
-                const char *c = cmd->valuestring;
-                if (strcmp(c, "CharacterChange") == 0) {
-                    dispatch_cmd = LN_WS_RECV_CHARACTER_CHANGE;
-                } else if (strcmp(c, "CharacterConfigChange") == 0) {
-                    dispatch_cmd = LN_WS_RECV_CHARACTER_CONFIG_CHANGE;
-                } else if (strcmp(c, "SleepModeStart") == 0) {
-                    dispatch_cmd = LN_WS_RECV_SLEEP_MODE_START;
-                } else if (strcmp(c, "SleepModeEnd") == 0) {
-                    dispatch_cmd = LN_WS_RECV_SLEEP_MODE_END;
-                } else if (strcmp(c, "DeviceBind") == 0) {
-                    dispatch_cmd = LN_WS_RECV_DEVICE_BIND;
-                } else if (strcmp(c, "DeviceUnbind") == 0) {
-                    dispatch_cmd = LN_WS_RECV_DEVICE_UNBIND;
-                } else if (strcmp(c, "ScheduleReminder") == 0) {
-                    dispatch_cmd = LN_WS_RECV_SCHEDULE_REMINDER;
-                } else if (strcmp(c, "ScheduleReminderConfig") == 0) {
-                    dispatch_cmd = LN_WS_RECV_SCHEDULE_REMINDER_CFG;
-                } else {
-                    dispatch_cmd = LN_WS_RECV_UNKNOWN;
-                }
-            }
-            cJSON_Delete(root);
+        // 解析消息并写入队列
+        ws_data_msg_t data_msg;
+        if (ln_ws_parse_data_message(buf, &data_msg) == 0) {
+            ln_ws_queue_data_message(&data_msg);
+            printf("[LN][WS] Message parsed and queued successfully: command=%d (%s)\n", 
+                   data_msg.command, data_msg.command_str);
+        } else {
+            printf("[LN][WS] Failed to parse message\n");
         }
     }
     printf("[LN][WS] on_message: %s\n", buf);
@@ -77,7 +122,7 @@ static void ln_ws_on_message(int client, const char *data, size_t length, bool i
     /* 透传给接收队列:上层可基于 dispatch_cmd 做进一步处理 */
     //do_ln_http_server_recv_data(dispatch_cmd, buf, (uint32_t)length);
     /* 立即在同线程内调上层处理(可选:如担心阻塞可删除此行,仅由接收任务处理) */
-    ln_ws_process_message(dispatch_cmd, buf);
+    // ln_ws_process_message(dispatch_cmd, buf);
     free(buf);
 }
 
@@ -85,14 +130,22 @@ static void ln_ws_on_message(int client, const char *data, size_t length, bool i
 static void ln_ws_on_error(int client, struct WebSocketErrorResult error)
 {
     (void)client;
-    printf("[LN][WS] error: code=%d msg=%s\n", error.errorCode, error.errorMessage);
+    printf("WebSocketClientConstructor [LN][WS] error: code=%d msg=%s\n", error.errorCode, error.errorMessage);
 }
 
 /* 断开回调:标记为未连接。是否自动重连交由上层决定 */
 static void ln_ws_on_close(int client)
 {
     (void)client;
-    g_ws_connected = 0;
+    printf("WebSocketClientConstructor [LN][WS] ln_ws_on_close");
+    g_ws_connected = WS_STATE_DISCONNECTED;
+    printf("[LN][WS] 连接状态: 未连接\n");
+    if(g_ws_client_fd>=0)
+    {
+        WebSocketClientClose(g_ws_client_fd);
+        g_ws_client_fd=-1;
+    }
+
     /* 断开后增加重连计数,由监督线程在下一次连接时携带 */
     if (g_ws_running) {
         if (g_ws_reconnect_count < 0xFFFFFFFFu) {
@@ -102,58 +155,115 @@ static void ln_ws_on_close(int client)
 }
 
 /*
- * 心跳线程:每30秒发送一次 "ping"
- * - 使用文本帧维持链路活性
- * - 收到的 "pong" 可在 onMessage 中由上层识别(当前透传)
+ * 主线程:负责连接管理、心跳维护和队列消息处理
+ * - 监督连接状态,处理重连逻辑
+ * - 每30秒发送一次 "ping" 维持链路活性
+ * - 处理队列中的消息
  */
-static void *ln_ws_heartbeat(void *arg)
+static void ln_ws_main_thread(void *arg)
 {
     (void)arg;
+    int heartbeat_counter = 0; // 心跳计数器,每30秒发送一次ping
+    ws_msg_info_t msg;
+    uint32_t msgsize =0;
     while (g_ws_running) {
-        if (g_ws_connected && g_ws_client_fd >= 0) {
-            const char *ping = "ping";
-            (void)WebSocketClientSendText(g_ws_client_fd, ping, strlen(ping));
+        // 处理队列消息(非阻塞)
+        if (osal_msg_queue_read_copy(g_ws_msg_queue, &msg,&msgsize,1000) == ERRCODE_SUCC) {
+            printf("[LN][WS] osal_msg_queue_read_copy: g_ws_msg_queue\n");
+            switch (msg.msg_type) {
+                case WS_MSG_TYPE_SEND_TEXT:
+                    if (g_ws_connected == WS_STATE_CONNECTED && g_ws_client_fd >= 0 && msg.data) {
+                        WebSocketClientSendText(g_ws_client_fd, (char*)msg.data, msg.data_len);
+                        printf("[LN][WS] Sent text from queue: %.*s\n", msg.data_len, msg.data);
+                    } else {
+                        printf("[LN][WS] Cannot send text, connection state: %d\n", g_ws_connected);
+                    }
+                    break;
+                    
+                case WS_MSG_TYPE_FORCE_RECONNECT:
+                    printf("[LN][WS] Force reconnect requested\n");
+                    if (g_ws_client_fd >= 0) {
+                        WebSocketClientClose(g_ws_client_fd);
+                        g_ws_client_fd = -1;
+                    }
+                    g_ws_connected = WS_STATE_DISCONNECTED;
+                    printf("[LN][WS] 连接状态: 未连接(强制重连)\n");
+                    break;
+                    
+                case WS_MSG_TYPE_STOP:
+                    printf("[LN][WS] Stop requested from queue\n");
+                    g_ws_running = 0;
+                    break;
+                    
+                case WS_MSG_TYPE_DATA_MESSAGE:
+                    if (msg.data) {
+                        ws_data_msg_t *data_msg = (ws_data_msg_t*)msg.data;
+                        printf("[LN][WS] Received message: deviceId=%s, timestamp=%u, messageId=%s, command=%d (%s)\n",
+                               data_msg->device_id, data_msg->timestamp, data_msg->message_id, 
+                               data_msg->command, data_msg->command_str);
+                        
+                        // 根据命令类型进行具体处理
+                        switch (data_msg->command) {
+                            case WS_CMD_PONG:
+                                printf("[LN][WS] Processing Pong message\n");
+                                // 更新心跳状态、记录响应时间等
+                                break;
+                            case WS_CMD_CHARACTER_CHANGE:
+                                printf("[LN][WS] Processing CharacterChange message\n");
+                                break;
+                            case WS_CMD_SLEEP_MODE_START:
+                                printf("[LN][WS] Processing SleepModeStart message\n");
+                                break;
+                            case WS_CMD_SLEEP_MODE_END:
+                                printf("[LN][WS] Processing SleepModeEnd message\n");
+                                break;
+                            case WS_CMD_DEVICE_BIND:
+                                printf("[LN][WS] Processing DeviceBind message\n");
+                                break;
+                            case WS_CMD_DEVICE_UNBIND:
+                                printf("[LN][WS] Processing DeviceUnbind message\n");
+                                break;
+                            case WS_CMD_SCHEDULE_REMINDER:
+                                printf("[LN][WS] Processing ScheduleReminder message\n");
+                                break;
+                            case WS_CMD_SCHEDULE_REMINDER_CFG:
+                                printf("[LN][WS] Processing ScheduleReminderConfig message\n");
+                                break;
+                            default:
+                                printf("[LN][WS] Processing unknown command: %d\n", data_msg->command);
+                                break;
+                        }
+                        
+                        // 释放消息内存
+                        ln_ws_free_data_message(data_msg);
+                    }
+                    break;
+                    
+                default:
+                    printf("[LN][WS] Unknown message type: %d\n", msg.msg_type);
+                    break;
+            }
+            
+            // 释放动态分配的内存
+            if (msg.data) {
+                free(msg.data);
+                msg.data = NULL;
+            }
         }
-        /* 30s 间隔 */
-        for (int i = 0; i < 30 && g_ws_running; i++) {
-            usleep(1000 * 1000);
+        else
+        {
+            printf("osal_msg_queue_read_copy fail\r\n");
         }
-    }
-    return NULL;
-}
-
-/*
- * 拼接连接URL:环境域名 + 业务必须的查询参数
- * - 认证方式采用“查询参数方式”:至少包含 deviceToken(规范允许)
- * - 兼容性增强:同时附带 deviceId 与 reconnect 计数,方便服务端校验与审计
- * - 如需切换环境,仅修改 base 即可
- */
-static void ln_ws_build_url(char *out, size_t out_size)
-{
-    const char *base = "wss://ambybox-api.test.leiniao.com/ambybox-device-api/v1/websocket"; 
-    const char *device_id = uapi_ln_get_device_id();
-    const char *token = uapi_ln_get_device_token();
-    if (!device_id) device_id = "";
-    if (!token) token = "";
-    /* 仅使用 deviceToken 也可通过;额外带上 deviceId 与 reconnect 便于后端管理 */
-    snprintf(out, out_size, "%s?deviceToken=%s&deviceId=%s&reconnect=%u",
-             base, token, device_id, g_ws_reconnect_count);
-}
-
-/*
- * 监督线程:负责初次连接与断线后的自动重连
- * - 重连次数随断线递增并写入URL的 reconnect 参数
- * - 使用简单指数退避,最大60s
- */
-static void *ln_ws_supervisor(void *arg)
-{
-    (void)arg;
-    while (g_ws_running) {
-        if (!g_ws_connected) {
+        
+        // 连接管理部分
+        if (g_ws_connected == WS_STATE_DISCONNECTED) {
             if (g_ws_client_fd < 0) {
+                printf("[LN][WS] 开始创建WebSocket客户端...\n");
                 g_ws_client_fd = WebSocketClientConstructor(ln_ws_on_open, ln_ws_on_message, ln_ws_on_error, ln_ws_on_close);
                 if (g_ws_client_fd < 0) {
                     printf("[LN][WS] constructor failed: %d\n", g_ws_client_fd);
+                    g_ws_connected = WS_STATE_DISCONNECTED;
+                    printf("[LN][WS] 连接状态: 未连接(创建失败)\n");
                     /* 失败则短暂休眠后重试创建 */
                     usleep(2000 * 1000);
                     continue;
@@ -162,9 +272,25 @@ static void *ln_ws_supervisor(void *arg)
 
             char url[512] = {0};
             ln_ws_build_url(url, sizeof(url));
-            int ret = WebSocketClientConnect(g_ws_client_fd, url);
+
+            const char *device_id = uapi_ln_get_device_id();
+                    // f"deviceId: {self.device_id}",
+                    //     f"deviceIccid: {self.device_iccid}",
+                    //     f"deviceModel: {Constants.DEVICE_MODEL.value}",
+                    //     f"deviceVersion: {Constants.DEVICE_VERSION.value}"
+
+            printf("[LN][WS] 尝试连接WebSocket: %s\n", url);
+            char header_buf[512]={0};
+            int hdr = snprintf(header_buf, sizeof(header_buf),
+                        "deviceId: %s\r\n",
+                       device_id);
+            int ret = WebSocketClientConnect(g_ws_client_fd, url,header_buf);
+            // int ret = WebSocketClientConnect(g_ws_client_fd,"ws://118.145.158.207:22080/chat/ws?platform=huoshan");
+           
             if (ret != 0) {
                 printf("[LN][WS] connect failed: %d\n", ret);
+                g_ws_connected = WS_STATE_DISCONNECTED;
+                printf("[LN][WS] 连接状态: 未连接(连接失败)\n");
                 (void)WebSocketClientClose(g_ws_client_fd);
                 g_ws_client_fd = -1;
                 /* 指数退避*/
@@ -175,19 +301,91 @@ static void *ln_ws_supervisor(void *arg)
                 }
                 continue;
             }
-
-            /* 首次连接成功,启动心跳线程*/
-            if (!g_ws_heartbeat_thread) {
-                (void)pthread_create(&g_ws_heartbeat_thread, NULL, ln_ws_heartbeat, NULL);
-                printf("[LN][WS] heartbeat thread created\n");
+            else
+            {
+                g_ws_connected = WS_STATE_CONNECTING;
+            }
+            /* 连接请求已发送,等待握手完成 */
+            printf("[LN][WS] 连接请求已发送,等待握手完成...\n");
+        }
+        
+        // 心跳部分:每30秒发送一次ping
+        if (g_ws_connected == WS_STATE_CONNECTED && g_ws_client_fd >= 0) {
+            heartbeat_counter++;
+            if (heartbeat_counter >= 15) { // 30秒 = 30次 * 1秒
+                (void)ln_ws_send_ping();
+                heartbeat_counter = 0; // 重置计数器
             }
         }
-        /* 已连接/或刚触发连接,短暂休眠 */
-        usleep(500 * 1000);
+        
+        /* 1秒间隔 */
+        usleep(1000 * 1000);
     }
-    return NULL;
 }
 
+/*
+ * 拼接连接URL:环境域名 + 业务必须的查询参数
+ * - 认证方式采用“查询参数方式”:至少包含 deviceToken(规范允许)
+ * - 兼容性增强:同时附带 deviceId 与 reconnect 计数,方便服务端校验与审计
+ * - 如需切换环境,仅修改 base 即可
+ */
+static void ln_ws_build_url(char *out, size_t out_size)
+{
+    const char *base = "ws://ambybox-api.test.leiniao.com/ambybox-device-api/v1/websocket"; 
+    const char *device_id = uapi_ln_get_device_id();
+    const char *token = uapi_ln_get_device_token();
+    if (!device_id) device_id = "";
+    if (!token) token = "";
+    /* 仅使用 deviceToken 也可通过;额外带上 deviceId 与 reconnect 便于后端管理 */
+    snprintf(out, out_size, "%s?deviceToken=%s&deviceId=%s&reconnect=%u",
+             base, token, device_id, g_ws_reconnect_count);
+}
+int ln_ws_send_ping(void)
+{
+    if (g_ws_connected != WS_STATE_CONNECTED || g_ws_client_fd < 0) {
+        printf("[LN][WS] Cannot send ping, connection state: %d\n", g_ws_connected);
+        return -1;
+    }
+    
+    // 创建 JSON 对象
+    cJSON *json = cJSON_CreateObject();
+    if (!json) {
+        printf("[LN][WS] Failed to create JSON object\n");
+        return -1;
+    }
+    
+    // 获取当前时间戳
+    uint32_t timestamp;
+    uapi_get_timestampe_ss(&timestamp);
+    
+    // 添加字段
+    cJSON_AddStringToObject(json, "deviceId", uapi_ln_get_device_id());
+    cJSON_AddNumberToObject(json, "timestamp", (double)timestamp);
+    cJSON_AddStringToObject(json, "messageId", "ping_001");
+    cJSON_AddStringToObject(json, "command", "Ping");
+    cJSON_AddNullToObject(json, "data");
+    
+    // 转换为字符串
+    char *json_string = cJSON_Print(json);
+    if (!json_string) {
+        printf("[LN][WS] Failed to print JSON\n");
+        cJSON_Delete(json);
+        return -1;
+    }
+    
+    // 发送 JSON 字符串
+    int ret = WebSocketClientSendText(g_ws_client_fd, json_string, strlen(json_string));
+    
+    printf("[LN][WS] Sent ping: %s %d\n", json_string,g_ws_client_fd);
+    
+    // 清理内存
+    free(json_string);
+    cJSON_Delete(json);
+    
+    return ret;
+}
+
+
 /*
  * 启动:开启监督线程与心跳机制(心跳线程首次连接成功后会自然启动)
  * - 若已在运行则直接返回成功
@@ -199,9 +397,25 @@ int ln_ws_start(void)
     }
     g_ws_running = 1;
     g_ws_reconnect_count = 0; /* 启动时从0开始 */
-    if (!g_ws_supervisor_thread) {
-        (void)pthread_create(&g_ws_supervisor_thread, NULL, ln_ws_supervisor, NULL);
+    g_ws_connected = WS_STATE_DISCONNECTED; /* 启动时重置连接状态 */
+    if (!g_ws_main_thread) {
+        app_task_definition_ext_t main_task = {
+            .attr = {
+                .name = "ln_ws_main",
+                .stack_size = 4096,
+                .priority = osPriorityNormal
+            },
+            .func = ln_ws_main_thread,
+            .task_handle = &g_ws_main_thread,
+            .create_queue_handle = ln_ws_queue_init,
+            .use_mem = USE_EXTEND_MEM,
+            .inst_args = NULL
+        };
+        (void)uapi_create_osThread_custom_with_args(&main_task);
     }
+    cat1_api_keep_wakeup(1);
+    cat1_api_wakeup_cat1(1);
+
     return 0;
 }
 
@@ -209,29 +423,282 @@ int ln_ws_start(void)
 int ln_ws_send_text(const char *text)
 {
     if (!text) return -1;
-    if (!g_ws_connected || g_ws_client_fd < 0) return -1;
+    if (g_ws_connected != WS_STATE_CONNECTED || g_ws_client_fd < 0) {
+        printf("[LN][WS] Cannot send text, connection state: %d\n", g_ws_connected);
+        return -1;
+    }
     return WebSocketClientSendText(g_ws_client_fd, text, strlen(text));
 }
 
+/* 通过队列发送文本消息(异步) */
+int ln_ws_send_text_async(const char *text)
+{
+    if (!text || !g_ws_running) return -1;
+    
+    ws_msg_info_t msg;
+    msg.msg_type = WS_MSG_TYPE_SEND_TEXT;
+    msg.data_len = strlen(text);
+    msg.data = NULL;
+    
+    // 分配内存
+    msg.data = (uint8_t*)malloc(msg.data_len + 1);
+    if (!msg.data) {
+        printf("[LN][WS] Failed to allocate memory for text message\n");
+        return -1;
+    }
+    
+    memcpy(msg.data, text, msg.data_len);
+    msg.data[msg.data_len] = '\0';
+    
+    int ret = osal_msg_queue_write_copy(g_ws_msg_queue, &msg, sizeof(ws_msg_info_t),0);
+    if (ret != ERRCODE_SUCC) {
+        // 发送失败,释放内存
+        free(msg.data);
+        msg.data = NULL;
+    }
+    
+    return ret;
+}
+
+/* 强制重连 */
+int ln_ws_force_reconnect(void)
+{
+    if (!g_ws_running) return -1;
+    
+    ws_msg_info_t msg;
+    msg.msg_type = WS_MSG_TYPE_FORCE_RECONNECT;
+    msg.data_len = 0;
+    msg.data = NULL;
+    
+    return osal_msg_queue_write_copy(g_ws_msg_queue, &msg,sizeof(ws_msg_info_t), 0);
+}
+
+/* 通过队列停止WebSocket服务 */
+int ln_ws_stop_async(void)
+{
+    if (!g_ws_running) return -1;
+    
+    ws_msg_info_t msg;
+    msg.msg_type = WS_MSG_TYPE_STOP;
+    msg.data_len = 0;
+    msg.data = NULL;
+    
+    return osal_msg_queue_write_copy(g_ws_msg_queue, &msg,sizeof(ws_msg_info_t), 0);
+}
+
 /* 停止:关闭心跳并关闭连接 */
 int ln_ws_stop(void)
 {
     g_ws_running = 0;
-    if (g_ws_supervisor_thread) {
-        pthread_join(g_ws_supervisor_thread, NULL);
-        g_ws_supervisor_thread = 0;
-    }
-    if (g_ws_heartbeat_thread) {
-        pthread_join(g_ws_heartbeat_thread, NULL);
-        g_ws_heartbeat_thread = 0;
+    if (g_ws_main_thread) {
+        osThreadJoin(g_ws_main_thread);
+        g_ws_main_thread = 0;
     }
     int ret = 0;
     if (g_ws_client_fd >= 0) {
         ret = WebSocketClientClose(g_ws_client_fd);
         g_ws_client_fd = -1;
     }
-    g_ws_connected = 0;
+    g_ws_connected = WS_STATE_DISCONNECTED;
     return ret;
 }
 
+/* 获取当前连接状态 */
+int ln_ws_get_connection_state(void)
+{
+    return g_ws_connected;
+}
+
+/* 获取连接状态字符串 */
+const char* ln_ws_get_connection_state_string(void)
+{
+    switch (g_ws_connected) {
+        case WS_STATE_DISCONNECTED:
+            return "未连接";
+        case WS_STATE_CONNECTING:
+            return "连接中";
+        case WS_STATE_CONNECTED:
+            return "连接成功";
+        default:
+            return "未知状态";
+    }
+}
+
+/* ============================ 统一消息解析和队列处理 ============================ */
+
+/**
+ * @brief 将命令字符串转换为枚举值
+ * @param command_str 命令字符串
+ * @return 对应的命令枚举值
+ */
+static ws_command_t ln_ws_parse_command_string(const char *command_str)
+{
+    if (!command_str) {
+        return WS_CMD_UNKNOWN;
+    }
+    
+    if (strcmp(command_str, "CharacterChange") == 0) {
+        return WS_CMD_CHARACTER_CHANGE;
+    } else if (strcmp(command_str, "CharacterConfigChange") == 0) {
+        return WS_CMD_CHARACTER_CONFIG_CHANGE;
+    } else if (strcmp(command_str, "SleepModeStart") == 0) {
+        return WS_CMD_SLEEP_MODE_START;
+    } else if (strcmp(command_str, "SleepModeEnd") == 0) {
+        return WS_CMD_SLEEP_MODE_END;
+    } else if (strcmp(command_str, "DeviceBind") == 0) {
+        return WS_CMD_DEVICE_BIND;
+    } else if (strcmp(command_str, "DeviceUnbind") == 0) {
+        return WS_CMD_DEVICE_UNBIND;
+    } else if (strcmp(command_str, "ScheduleReminder") == 0) {
+        return WS_CMD_SCHEDULE_REMINDER;
+    } else if (strcmp(command_str, "ScheduleReminderConfig") == 0) {
+        return WS_CMD_SCHEDULE_REMINDER_CFG;
+    } else if (strcmp(command_str, "DownloadEmoji") == 0) {
+        return WS_CMD_DOWNLOAD_EMOJI;
+    } else if (strcmp(command_str, "DeleteEmoji") == 0) {
+        return WS_CMD_DELETE_EMOJI;
+    } else if (strcmp(command_str, "Ping") == 0) {
+        return WS_CMD_PING;
+    } else if (strcmp(command_str, "Pong") == 0) {
+        return WS_CMD_PONG;
+    } else {
+        return WS_CMD_UNKNOWN;
+    }
+}
+
+/**
+ * @brief 解析WebSocket消息的JSON数据
+ * @param json_str JSON字符串
+ * @param data_msg 输出的消息结构体
+ * @return 0成功,-1失败
+ */
+static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_msg)
+{
+    if (!json_str || !data_msg) {
+        printf("[LN][WS] Invalid parameters for message parsing\n");
+        return -1;
+    }
+    
+    // 初始化结构体
+    memset(data_msg, 0, sizeof(ws_data_msg_t));
+    
+    // 解析JSON
+    cJSON *root = cJSON_Parse(json_str);
+    if (!root) {
+        printf("[LN][WS] Failed to parse JSON for message\n");
+        return -1;
+    }
+    
+    // 解析deviceId
+    cJSON *device_id = cJSON_GetObjectItem(root, "deviceId");
+    if (device_id && cJSON_IsString(device_id) && device_id->valuestring) {
+        strncpy(data_msg->device_id, device_id->valuestring, sizeof(data_msg->device_id) - 1);
+        data_msg->device_id[sizeof(data_msg->device_id) - 1] = '\0';
+    } else {
+        printf("[LN][WS] Missing or invalid deviceId in message\n");
+        cJSON_Delete(root);
+        return -1;
+    }
+    
+    // 解析timestamp
+    cJSON *timestamp = cJSON_GetObjectItem(root, "timestamp");
+    if (timestamp && cJSON_IsNumber(timestamp)) {
+        data_msg->timestamp = (uint32_t)timestamp->valuedouble;
+    } else {
+        printf("[LN][WS] Missing or invalid timestamp in message\n");
+        cJSON_Delete(root);
+        return -1;
+    }
+    
+    // 解析messageId
+    cJSON *message_id = cJSON_GetObjectItem(root, "messageId");
+    if (message_id && cJSON_IsString(message_id) && message_id->valuestring) {
+        strncpy(data_msg->message_id, message_id->valuestring, sizeof(data_msg->message_id) - 1);
+        data_msg->message_id[sizeof(data_msg->message_id) - 1] = '\0';
+    } else {
+        printf("[LN][WS] Missing or invalid messageId in message\n");
+        cJSON_Delete(root);
+        return -1;
+    }
+    
+    // 解析command
+    cJSON *command = cJSON_GetObjectItem(root, "command");
+    if (command && cJSON_IsString(command) && command->valuestring) {
+        strncpy(data_msg->command_str, command->valuestring, sizeof(data_msg->command_str) - 1);
+        data_msg->command_str[sizeof(data_msg->command_str) - 1] = '\0';
+        data_msg->command = ln_ws_parse_command_string(command->valuestring);
+    } else {
+        printf("[LN][WS] Missing or invalid command in message\n");
+        cJSON_Delete(root);
+        return -1;
+    }
+    
+    // 保存原始JSON字符串
+    data_msg->raw_json = (char*)malloc(strlen(json_str) + 1);
+    if (data_msg->raw_json) {
+        strcpy(data_msg->raw_json, json_str);
+    }
+    
+    cJSON_Delete(root);
+    
+    printf("[LN][WS] Message parsed successfully: deviceId=%s, timestamp=%u, messageId=%s, command=%d (%s)\n",
+           data_msg->device_id, data_msg->timestamp, data_msg->message_id, 
+           data_msg->command, data_msg->command_str);
+    
+    return 0;
+}
+
+/**
+ * @brief 将消息写入队列
+ * @param data_msg 消息结构体指针
+ * @return 0成功,-1失败
+ */
+static int ln_ws_queue_data_message(const ws_data_msg_t *data_msg)
+{
+    if (!data_msg || !g_ws_running) {
+        printf("[LN][WS] Invalid parameters or WebSocket not running\n");
+        return -1;
+    }
+    
+    ws_msg_info_t msg;
+    msg.msg_type = WS_MSG_TYPE_DATA_MESSAGE;
+    msg.data_len = sizeof(ws_data_msg_t);
+    msg.data = NULL;
+    
+    // 分配内存并复制数据
+    msg.data = (uint8_t*)malloc(sizeof(ws_data_msg_t));
+    if (!msg.data) {
+        printf("[LN][WS] Failed to allocate memory for data message\n");
+        return -1;
+    }
+    
+    memcpy(msg.data, data_msg, sizeof(ws_data_msg_t));
+    
+    // 写入队列
+    int ret = osal_msg_queue_write_copy(g_ws_msg_queue, &msg, sizeof(ws_msg_info_t), 0);
+    if (ret != ERRCODE_SUCC) {
+        printf("[LN][WS] Failed to write data message to queue: %d\n", ret);
+        free(msg.data);
+        msg.data = NULL;
+        return -1;
+    }
+    
+    printf("[LN][WS] Data message queued successfully\n");
+    return 0;
+}
+
+/**
+ * @brief 释放消息结构体内存
+ * @param data_msg 消息结构体指针
+ */
+static void ln_ws_free_data_message(ws_data_msg_t *data_msg)
+{
+    if (data_msg) {
+        if (data_msg->raw_json) {
+            free(data_msg->raw_json);
+            data_msg->raw_json = NULL;
+        }
+    }
+}
+
 
index a9830bd..c1b176c 100644 (file)
@@ -730,7 +730,7 @@ static int ws_test_create_client(uint8_t client_id)
     
     // 连接到服务器 (异步连接,不等待连接完成)
     WS_TEST_LOGI("Initiating WebSocket connection for client %d to %s...", client_id, client->url);
-    int connect_result = WS_ClientConnect(client->client_fd, client->url);
+    int connect_result = WS_ClientConnect(client->client_fd, client->url,NULL);
     WS_TEST_LOGI("WS_ClientConnect returned: %d (WS_OK=%d)", connect_result, WS_OK);
     
     if (connect_result != WS_OK) {