#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;
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);
/* 透传给接收队列:上层可基于 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);
}
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) {
}
/*
- * 心跳线程:每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;
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;
/* 指数退避*/
}
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;
+}
+
+
/*
* 启动:开启监督线程与心跳机制(心跳线程首次连接成功后会自然启动)
* - 若已在运行则直接返回成功
}
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;
}
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;
+ }
+ }
+}
+