#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
/* 数据处理回调函数指针 - 用于将数据传递给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; // 最后接收数据时间
// ⚠️ 更新全局时间戳(供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) {
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;
}
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;
// 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();
{
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) {
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);
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)
{
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);
// 检查是否是从重连状态恢复的连接
(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);
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)
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
{
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;
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;
}
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;
+}
+