From d5ee27df861e64cee7aba97618faa98f920097c3 Mon Sep 17 00:00:00 2001 From: hcj <1224667075@qq.com> Date: Fri, 5 Dec 2025 17:55:24 +0800 Subject: [PATCH] =?utf8?q?=E4=BF=AE=E6=94=B9=E4=BD=8E=E5=8A=9F=E8=80=97?= =?utf8?q?=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../lwip/lwip_adapter/liteos/src/arch/sio.c | 14 +- .../lwip/lwip_v2.1.3/src/api/tcpip.c | 4 + .../zhihui/leiniao/imsl_ln/ln_api_websocket.c | 5 +- .../zhihui/websocket/os_ws/haisi_websocket.c | 286 +++++++++++++++++- .../zhihui/websocket/os_ws/haisi_websocket.h | 6 + 5 files changed, 296 insertions(+), 19 deletions(-) diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c index b94f2a28b..1d63274d7 100644 --- a/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c +++ b/code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c @@ -63,7 +63,7 @@ #define SIO_IRQ_PROCESS_STACK_SIZE 4096 // 处理线程栈大小 /* CAT1 vote 管理配置(与PPP保持一致) */ -#ifdef SUPPORT_PPP_SERVICE_INTERRUPT +#ifdef SUPPORT_CAT1_SERVICE #define SIO_CAT1_VOTE_IDLE_MS 3000 // 3秒空闲后移除CAT1 vote #endif @@ -165,7 +165,7 @@ static sio_stats_t g_sio_stats = {0}; /* 数据处理回调函数指针 - 用于将数据传递给SLIP */ static void (*g_sio_data_callback)(const uint8_t *data, uint32_t len) = NULL; -#ifdef SUPPORT_PPP_SERVICE_INTERRUPT +#ifdef SUPPORT_CAT1_SERVICE /* CAT1 vote 状态管理(与PPP保持一致) */ static volatile bool g_sio_cat1_vote_added = false; static volatile uint32_t g_sio_last_rx_time_ms = 0; // 最后接收数据时间 @@ -326,7 +326,7 @@ static void sio_timeout_timer_callback(unsigned long arg) // ⚠️ 更新全局时间戳(供ISR使用,ISR中不能调用时间函数) g_sio_timer_timestamp_ms = current_time; -#ifdef SUPPORT_PPP_SERVICE_INTERRUPT +#ifdef SUPPORT_CAT1_SERVICE // ✅ CAT1 vote 管理:检测 idle 状态,超过阈值时移除 vote if (g_sio_cat1_vote_added) { if (g_sio_last_rx_time_ms > 0) { @@ -434,7 +434,7 @@ static void sio_uart_rx_interrupt_handler(const void *buffer, uint16_t length, b g_sio_stats.total_interrupts++; g_sio_stats.total_bytes_received += length; -#ifdef SUPPORT_PPP_SERVICE_INTERRUPT +#ifdef SUPPORT_CAT1_SERVICE // ✅ 更新最后接收时间(用于空闲检测) g_sio_last_rx_time_ms = g_sio_timer_timestamp_ms; @@ -637,7 +637,7 @@ errcode_t sio_interrupt_enable(void (*data_callback)(const uint8_t *data, uint32 } g_sio_current_buffer_index = 0; -#ifdef SUPPORT_PPP_SERVICE_INTERRUPT +#ifdef SUPPORT_CAT1_SERVICE // 初始化 CAT1 vote 状态 g_sio_cat1_vote_added = false; g_sio_last_rx_time_ms = 0; @@ -842,7 +842,7 @@ errcode_t sio_interrupt_disable(void) // 6. 清零统计 memset(&g_sio_stats, 0, sizeof(sio_stats_t)); -#ifdef SUPPORT_PPP_SERVICE_INTERRUPT +#ifdef SUPPORT_CAT1_SERVICE // 7. 清理 CAT1 vote(如果之前添加过) if (g_sio_cat1_vote_added) { cat1_api_remove_cat1_vote(); @@ -868,7 +868,7 @@ void sio_print_stats(void) { printf("=== SIO Interrupt Statistics (Ping-Pong Mode) ===\n"); printf("Interrupt Status: %s\n", g_sio_interrupt_enabled ? "ENABLED" : "DISABLED"); -#ifdef SUPPORT_PPP_SERVICE_INTERRUPT +#ifdef SUPPORT_CAT1_SERVICE printf("CAT1 Vote Status: %s\n", g_sio_cat1_vote_added ? "ADDED (awake)" : "REMOVED (sleep)"); if (g_sio_last_rx_time_ms > 0) { diff --git a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/api/tcpip.c b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/api/tcpip.c index f4b5da2a3..c7824297f 100644 --- a/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/api/tcpip.c +++ b/code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/api/tcpip.c @@ -292,6 +292,10 @@ tcpip_send_msg_na(enum lowpower_msg_type type) return; } + /* 唤醒websocket(当tcpip_thread被唤醒时,同时唤醒websocket) */ + extern int uapi_ws_resume(void); + uapi_ws_resume(); + if (type == LOW_BLOCK) { LOWPOWER_SEM_WAIT(msg->msg.lowpower.wait_up); LOWPOWER_SEM_FREE(msg->msg.lowpower.wait_up); 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 6f15826e5..49978cafd 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c @@ -27,7 +27,7 @@ #define LN_WS_MAIN_THREAD_INTERVAL_MS 500 // 心跳间隔(秒) -#define LN_WS_HEARTBEAT_INTERVAL_SEC 30 //5分钟发送一次心跳,正常最大7分钟 +#define LN_WS_HEARTBEAT_INTERVAL_SEC 60*5 //5分钟发送一次心跳,正常最大7分钟 // 心跳间隔计数器阈值(基于主线程间隔计算) #define LN_WS_HEARTBEAT_COUNTER_THRESHOLD ((LN_WS_HEARTBEAT_INTERVAL_SEC * 1000) / LN_WS_MAIN_THREAD_INTERVAL_MS) @@ -1321,6 +1321,8 @@ void ln_ws_audio_init(void) // 设置初始化标志位 g_audio_initialized = true; + + uapi_ws_wake_keep(1); LN_WS_INFO("Audio initialization completed successfully\n"); #if ENABLE_VAD_SERVER @@ -1378,6 +1380,7 @@ void ln_ws_audio_deinit(void) g_audio_playback_active = false; g_audio_chat_ctx.finish_state = LN_CHAT_NOT_ENDED; + uapi_ws_wake_keep(0); // 清除初始化标志位 g_audio_initialized = false; diff --git a/code/sdk/zhihui/websocket/os_ws/haisi_websocket.c b/code/sdk/zhihui/websocket/os_ws/haisi_websocket.c index 6237df6dc..fdc2d3e62 100644 --- a/code/sdk/zhihui/websocket/os_ws/haisi_websocket.c +++ b/code/sdk/zhihui/websocket/os_ws/haisi_websocket.c @@ -97,6 +97,16 @@ static uint32_t g_main_thread_interval_ms = 1000; /* 主线程循环间隔时长 static time_t g_ws_connect_start_time = 0; /* 连接开始时间 */ static volatile int g_ws_connect_timeout_occurred = 0; /* 超时标志 */ +/* 低功耗机制相关变量 */ +#ifndef WS_IDLE_TIMEOUT_SECONDS +#define WS_IDLE_TIMEOUT_SECONDS 5 /* idle超时时间(秒),默认5秒 */ +#endif +static time_t g_ws_last_tx_time = 0; /* 最后发送时间 */ +static time_t g_ws_last_rx_time = 0; /* 最后接收时间 */ +static volatile int g_ws_is_sending = 0; /* 发送状态标志:0=空闲,1=发送中 */ +static volatile int g_ws_is_suspended = 0; /* suspend状态标志:0=未suspend,1=已suspend */ +static volatile int g_ws_wake_keep = 0; /* 保持唤醒状态标志:0=允许低功耗,1=保持唤醒 */ + static void uapi_ws_build_url(char *out, size_t out_size); static void uapi_ws_default_url_handler(char *url, size_t url_size, char *header_buf, size_t header_size); int uapi_ws_send_ping(void); @@ -110,6 +120,13 @@ static void uapi_ws_start_connect_timer(void); static int uapi_ws_is_connect_timeout(void); static void uapi_ws_reset_connect_timer(void); +/* 低功耗机制相关函数 */ +static void uapi_ws_update_tx_time(void); +static void uapi_ws_update_rx_time(void); +int uapi_ws_is_idle(void); +int uapi_ws_suspend(void); +int uapi_ws_resume(void); + /* 消息内存管理函数 */ static ws_msg_info_t* ws_msg_create(uint16_t data_len) { @@ -162,6 +179,13 @@ static void uapi_ws_on_open(int client) uapi_ws_reset_close_status(); // 连接成功时重置关闭状态 uapi_ws_reset_connect_timer(); // 连接成功时重置超时计时器 + // 连接成功时初始化活动时间和重置suspend状态 + time_t current_time = time(NULL); + g_ws_last_tx_time = current_time; + g_ws_last_rx_time = current_time; + g_ws_is_suspended = 0; + g_ws_is_sending = 0; + HAISI_WS_INFO("连接状态: 连接成功, 关闭状态已重置为 NOT_EXECUTED (%d)\n", g_ws_close_status); // 检查是否是从重连状态恢复的连接 @@ -188,6 +212,12 @@ static void uapi_ws_on_message(int client, const char *data, size_t length, bool (void)isFinished; if (data == NULL || length == 0) return; + // 接收消息时检查并resume + uapi_ws_resume(); + + // 更新最后接收时间 + uapi_ws_update_rx_time(); + /* 数据完整性判断 */ HAISI_WS_DEBUG("WebSocketClientConstructor data %d\n",length); @@ -263,6 +293,12 @@ static void uapi_ws_on_close(int client) g_ws_connected = WS_STATE_DISCONNECTED; HAISI_WS_DEBUG("连接状态: 未连接\n"); + // 连接断开时重置suspend状态和活动时间 + g_ws_is_suspended = 0; + g_ws_last_tx_time = 0; + g_ws_last_rx_time = 0; + g_ws_is_sending = 0; + // 触发连接断开事件(注意:这里不能直接调用回调,因为不在主线程中) // 事件回调将在主线程中处理 if(g_ws_client_fd>=0) @@ -518,6 +554,21 @@ static void uapi_ws_main_thread(void *arg) HAISI_WS_DEBUG("Heartbeat callback triggered\n"); } } + + // 低功耗机制:idle检测和suspend/resume管理 + if (uapi_ws_is_idle()) { + // 检测到idle且未suspend,执行suspend + if (g_ws_is_suspended == 0) { + HAISI_WS_DEBUG("Idle detected, suspending WebSocket\n"); + uapi_ws_suspend(); + } + } else { + // 检测到busy且已suspend,执行resume + if (g_ws_is_suspended != 0) { + HAISI_WS_DEBUG("Busy detected, resuming WebSocket\n"); + uapi_ws_resume(); + } + } } else { @@ -637,7 +688,25 @@ int uapi_ws_send_text(const char *text) HAISI_WS_WARN("Cannot send text, connection state: %d\n", g_ws_connected); return -1; } - return WebSocketClientSendText(g_ws_client_fd, text, strlen(text)); + + // 发送前检查并resume + uapi_ws_resume(); + + // 设置发送状态标志 + g_ws_is_sending = 1; + + // 发送数据 + int ret = WebSocketClientSendText(g_ws_client_fd, text, strlen(text)); + + // 更新最后发送时间 + if (ret == 0) { + uapi_ws_update_tx_time(); + } + + // 清除发送状态标志 + g_ws_is_sending = 0; + + return ret; } uint32_t size_uapi_ws_send_BIN=0; @@ -648,30 +717,50 @@ int uapi_ws_send_BIN(uint8_t *data,int len) HAISI_WS_WARN("Cannot send text, connection state: %d\n", g_ws_connected); return -1; } + + // 发送前检查并resume + uapi_ws_resume(); + + // 设置发送状态标志 + g_ws_is_sending = 1; + + int ret = 0; if(len%3) { uint16_t data_mod_len=len%3; - WebSocketClientSendBinStart(g_ws_client_fd, data, (len/3)); + ret = WebSocketClientSendBinStart(g_ws_client_fd, data, (len/3)); + if (ret != 0) goto send_end; - WebSocketClientSendBinContinue(g_ws_client_fd, data+ (len/3), (len/3)); + ret = WebSocketClientSendBinContinue(g_ws_client_fd, data+ (len/3), (len/3)); + if (ret != 0) goto send_end; - WebSocketClientSendBinEnd(g_ws_client_fd,data+(len/3)+(len/3),(len/3)+data_mod_len); + ret = WebSocketClientSendBinEnd(g_ws_client_fd,data+(len/3)+(len/3),(len/3)+data_mod_len); } else { - WebSocketClientSendBinStart(g_ws_client_fd, data, len/3); + ret = WebSocketClientSendBinStart(g_ws_client_fd, data, len/3); + if (ret != 0) goto send_end; - WebSocketClientSendBinContinue(g_ws_client_fd,data+len/3,len/3); + ret = WebSocketClientSendBinContinue(g_ws_client_fd,data+len/3,len/3); + if (ret != 0) goto send_end; - WebSocketClientSendBinEnd(g_ws_client_fd,data+len/3+len/3,len/3); - + ret = WebSocketClientSendBinEnd(g_ws_client_fd,data+len/3+len/3,len/3); } - size_uapi_ws_send_BIN+=len; - HAISI_WS_DEBUG("uapi_ws_send_BIN,size: %d %d\n", len,size_uapi_ws_send_BIN); + +send_end: + // 更新最后发送时间(仅成功时) + if (ret == 0) { + uapi_ws_update_tx_time(); + size_uapi_ws_send_BIN+=len; + HAISI_WS_DEBUG("uapi_ws_send_BIN,size: %d %d\n", len,size_uapi_ws_send_BIN); + } + + // 清除发送状态标志 + g_ws_is_sending = 0; - return 0; + return ret; } @@ -1069,3 +1158,178 @@ static void uapi_ws_reset_connect_timer(void) HAISI_WS_DEBUG("Connect timer reset\n"); } +/* ============================ 低功耗机制实现 ============================ */ + +/** + * @brief 更新最后发送时间 + */ +static void uapi_ws_update_tx_time(void) +{ + g_ws_last_tx_time = time(NULL); + HAISI_WS_DEBUG("TX time updated: %ld\n", (long)g_ws_last_tx_time); +} + +/** + * @brief 更新最后接收时间 + */ +static void uapi_ws_update_rx_time(void) +{ + g_ws_last_rx_time = time(NULL); + HAISI_WS_DEBUG("RX time updated: %ld\n", (long)g_ws_last_rx_time); +} + +/** + * @brief 判断WebSocket是否处于idle状态 + * @return 1表示idle,0表示busy + * + * Idle条件: + * 1. 距离最后收发时间超过5秒 + * 2. 消息队列为空 + * 3. 无正在发送的数据 + * 4. 连接状态为已连接 + */ +int uapi_ws_is_idle(void) +{ + // 检查连接状态 + if (g_ws_connected != WS_STATE_CONNECTED || g_ws_client_fd < 0) { + return 0; // 未连接,不算idle + } + + // 检查是否设置了保持唤醒标志 + if (g_ws_wake_keep != 0) { + HAISI_WS_DEBUG("Not idle: wake keep enabled\n"); + return 0; // 保持唤醒状态,不算idle + } + + // 检查是否有正在发送的数据 + if (g_ws_is_sending != 0) { + HAISI_WS_DEBUG("Not idle: sending in progress\n"); + return 0; // 正在发送,不算idle + } + + // 检查消息队列是否为空 + if (g_ws_msg_queue != NULL) { + uint32_t queue_count = osMessageQueueGetCount(g_ws_msg_queue); + if (queue_count > 0) { + HAISI_WS_DEBUG("Not idle: queue has %u messages\n", queue_count); + return 0; // 队列非空,不算idle + } + } else { + // 队列未初始化,返回非idle状态 + return 0; + } + + // 检查时间超时 + time_t current_time = time(NULL); + time_t last_activity_time = (g_ws_last_tx_time > g_ws_last_rx_time) ? + g_ws_last_tx_time : g_ws_last_rx_time; + + // 如果从未有活动,不算idle(等待首次活动) + if (last_activity_time == 0) { + return 0; + } + + time_t elapsed = current_time - last_activity_time; + if (elapsed >= WS_IDLE_TIMEOUT_SECONDS) { + HAISI_WS_DEBUG("Idle detected: %ld seconds since last activity\n", (long)elapsed); + return 1; // idle + } + + return 0; // busy +} + +/** + * @brief 统一suspend接口 + * @return 0成功,-1失败 + */ +int uapi_ws_suspend(void) +{ + // 检查是否已经suspend + if (g_ws_is_suspended != 0) { + HAISI_WS_DEBUG("WebSocket already suspended\n"); + return 0; // 已经suspend,直接返回成功 + } + + // 检查连接状态 + if (g_ws_connected != WS_STATE_CONNECTED || g_ws_client_fd < 0) { + HAISI_WS_DEBUG("Cannot suspend: not connected (state=%d, fd=%d)\n", + g_ws_connected, g_ws_client_fd); + return -1; + } + + // 调用底层suspend + int ret = WebsocketSuspend(g_ws_client_fd); + if (ret == 0) { + g_ws_is_suspended = 1; + HAISI_WS_INFO("WebSocket suspended successfully (fd=%d)\n", g_ws_client_fd); + } else { + HAISI_WS_ERROR("WebSocket suspend failed (fd=%d, ret=%d)\n", g_ws_client_fd, ret); + } + + return ret; +} + +/** + * @brief 统一resume接口 + * @return 0成功,-1失败 + */ +int uapi_ws_resume(void) +{ + // 检查是否已经resume + if (g_ws_is_suspended == 0) { + HAISI_WS_DEBUG("WebSocket already resumed\n"); + return 0; // 已经resume,直接返回成功 + } + + // 检查连接状态 + if (g_ws_client_fd < 0) { + HAISI_WS_DEBUG("Cannot resume: invalid client fd\n"); + return -1; + } + + // 调用底层resume + int ret = WebsocketResume(g_ws_client_fd); + if (ret == 0) { + g_ws_is_suspended = 0; + // 更新活动时间,标记为活跃 + uapi_ws_update_tx_time(); + HAISI_WS_INFO("WebSocket resumed successfully (fd=%d)\n", g_ws_client_fd); + } else { + HAISI_WS_ERROR("WebSocket resume failed (fd=%d, ret=%d)\n", g_ws_client_fd, ret); + } + + return ret; +} + +/** + * @brief 设置/清除保持唤醒状态(防止进入低功耗模式) + * @param enable 1=保持唤醒(禁止低功耗),0=允许低功耗 + * @return 0成功,-1失败 + * + * 当设置为保持唤醒时,WebSocket将不会进入suspend状态,即使检测到idle + * 如果当前已处于suspend状态,设置保持唤醒后会立即resume + */ +int uapi_ws_wake_keep(int enable) +{ + if (enable != 0 && enable != 1) { + HAISI_WS_ERROR("Invalid parameter: enable must be 0 or 1\n"); + return -1; + } + + g_ws_wake_keep = enable; + + if (enable != 0) { + HAISI_WS_INFO("Wake keep enabled: WebSocket will not enter low power mode\n"); + + // 如果当前已suspend,立即resume + if (g_ws_is_suspended != 0) { + HAISI_WS_INFO("Resuming WebSocket due to wake keep enabled\n"); + uapi_ws_resume(); + } + } else { + HAISI_WS_INFO("Wake keep disabled: WebSocket can enter low power mode when idle\n"); + } + + return 0; +} + diff --git a/code/sdk/zhihui/websocket/os_ws/haisi_websocket.h b/code/sdk/zhihui/websocket/os_ws/haisi_websocket.h index 76e24cb2c..a9b28e598 100644 --- a/code/sdk/zhihui/websocket/os_ws/haisi_websocket.h +++ b/code/sdk/zhihui/websocket/os_ws/haisi_websocket.h @@ -78,6 +78,12 @@ int uapi_ws_get_close_status_public(void); void uapi_ws_print_status_info(void); uint32_t uapi_ws_get_main_thread_interval(void); +/* WebSocket低功耗管理接口 */ +int uapi_ws_suspend(void); +int uapi_ws_resume(void); +int uapi_ws_is_idle(void); +int uapi_ws_wake_keep(int enable); /* 保持唤醒状态:1=保持唤醒(禁止低功耗),0=允许低功耗 */ + /* 回调函数注册接口 */ void uapi_ws_register_message_callback(ws_message_callback_t callback); void uapi_ws_register_binary_callback(ws_binary_message_callback_t callback); -- 2.17.1