修改低功耗控制
authorhcj <1224667075@qq.com>
Fri, 5 Dec 2025 09:55:24 +0000 (17:55 +0800)
committerhcj <1224667075@qq.com>
Fri, 5 Dec 2025 09:55:24 +0000 (17:55 +0800)
code/sdk/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/arch/sio.c
code/sdk/open_source/openharmony/third_party/lwip/lwip_v2.1.3/src/api/tcpip.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c
code/sdk/zhihui/websocket/os_ws/haisi_websocket.c
code/sdk/zhihui/websocket/os_ws/haisi_websocket.h

index b94f2a2..1d63274 100644 (file)
@@ -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) {
index f4b5da2..c782429 100644 (file)
@@ -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);
index 6f15826..49978ca 100644 (file)
@@ -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;
index 6237df6..fdc2d3e 100644 (file)
@@ -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;
+}
+
index 76e24cb..a9b28e5 100644 (file)
@@ -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);