From: hcj <1224667075@qq.com> Date: Fri, 19 Sep 2025 10:40:33 +0000 (+0800) Subject: ws ln提交 X-Git-Tag: leiniao_debug_1.0.0_20250922_020716~13^2~12 X-Git-Url: http://git.snicker.com.cn/css/static/gitweb.css?a=commitdiff_plain;h=bbab8b67fea02692ea5e1f53d1824b071d0573c3;p=mirrors%2FFD230.git ws ln提交 --- diff --git a/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/brandy-native-js-ln/libwebsocket.a b/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/brandy-native-js-ln/libwebsocket.a index 93b5e6f80..909c556d0 100644 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 diff --git a/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket.h b/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket.h index ef9fdf3e9..4475dcab7 100644 --- a/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket.h +++ b/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket.h @@ -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}. */ diff --git a/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket_opts.h b/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket_opts.h index 70d7d5d35..61ec0835a 100644 --- a/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket_opts.h +++ b/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/netstack/websocket/include/net_websocket_opts.h @@ -11,11 +11,11 @@ #define WS_MAX_CLIENT_NUM 4 -#define WS_MAX_URI_LENGTH 256 +#define WS_MAX_URI_LENGTH 512 #define WS_MAX_DATA_LENGTH (20 * 1024) -#define WS_HANDSHAKE_BUF_LEN 512 +#define WS_HANDSHAKE_BUF_LEN 1024 #define WSS_READ_TIMEOUT_MS 100 diff --git a/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/websocket/websocket_module.cpp b/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/websocket/websocket_module.cpp index 5af2226fd..4b2b30160 100644 --- a/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/websocket/websocket_module.cpp +++ b/code/sdk/open_source/openharmony/foundation/ace/ace_engine_lite/modules/websocket/websocket_module.cpp @@ -155,7 +155,7 @@ JSIValue WebsocketModule::Connect(const JSIValue thisVal, const JSIValue *args, return JSI::CreateUndefined(); } - int ret = WebSocketClientConnect(client, url); + int ret = WebSocketClientConnect(client, url,NULL); if (ret != 0) { HILOG_ERROR(HILOG_MODULE_ACE, "WebSocketClientConnect fail!"); return JSI::CreateUndefined(); diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c index ddf37ce38..32c6e07e3 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c @@ -1,34 +1,99 @@ #include "ln_api.h" #include "net_websocket.h" -#include #include #include #include #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(×tamp); + + // 添加字段 + 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; + } + } +} + diff --git a/code/sdk/zhihui/websocket/test/ws_multi_test.c b/code/sdk/zhihui/websocket/test/ws_multi_test.c index a9830bd83..c1b176c05 100644 --- a/code/sdk/zhihui/websocket/test/ws_multi_test.c +++ b/code/sdk/zhihui/websocket/test/ws_multi_test.c @@ -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) {