#include "ln_api.h"
#include "net_websocket.h"
+#include "haisi_websocket.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "custom_utils.h"
#include "soc_osal.h"
#include "cmsis_os2.h"
+#include "mbedtls/base64.h"
/*
* - g_ws_client_fd 当前 WebSocket 客户端fd
* - 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 // 连接中
WS_CLOSE_SUCCESS = 2 // 执行成功
} ws_close_status_t;
-/* WebSocket命令枚举 */
-
-
-typedef struct {
- uint8_t msg_type;
- uint16_t data_len;
- ws_data_msg_t data; // 直接存储数据而不是指针
-} ws_msg_info_t;
-
-
-static int g_ws_client_fd = -1;
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 osMessageQueueId_t g_ws_msg_queue = NULL;
static ws_message_callback_t g_ws_message_callback = NULL; /* 消息处理回调函数 */
static volatile ws_close_status_t g_ws_close_status = WS_CLOSE_NOT_EXECUTED; /* WebSocket关闭状态标志位 */
+/* Base64数据缓存相关变量 */
+static char *g_base64_cache = NULL; /* 缓存的Base64数据 */
+static size_t g_base64_cache_size = 0; /* 缓存大小 */
+static size_t g_base64_cache_capacity = 0; /* 缓存容量 */
+static bool g_base64_in_progress = false; /* 是否正在进行Base64数据接收 */
+
+/* 回调函数结构体 */
+static ws_call_backs_t g_ws_callbacks = {0};
+
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);
static void ln_ws_default_message_handler(const ws_data_msg_t *data_msg, ws_command_t type);
static void ln_ws_process_message_with_callback(const ws_data_msg_t *data_msg);
static ws_close_status_t ln_ws_get_close_status(void);
static void ln_ws_reset_close_status(void);
const char* ln_ws_get_connection_state_string(void);
-/* 队列创建函数 */
-void ln_ws_queue_init(void)
-{
- printf("[LN][WS] ln_ws_queue_init\n");
- g_ws_msg_queue = osMessageQueueNew(WS_MSG_QUEUE_COUNT, sizeof(ws_msg_info_t), NULL);
- if (g_ws_msg_queue == NULL) {
- printf("[LN][WS] ln_ws_queue_init failed: failed to create message queue\n");
- } else {
- printf("[LN][WS] ln_ws_queue_init success: queue created\n");
- }
-}
+
+/* Base64缓存管理函数 */
+static void ln_ws_base64_cache_init(void);
+static void ln_ws_base64_cache_cleanup(void);
+static int ln_ws_base64_cache_append(const char *data, size_t length);
+static void ln_ws_base64_cache_reset(void);
+static void ln_ws_process_base64_data(const char *base64_data, size_t length);
+static void ln_ws_process_incremental_base64_data(const char *new_data, size_t new_length);
+static void ln_ws_incomplete_callback(const char *raw_data, size_t length, bool isBinary);
/* 握手成功回调:标记连接就绪 */
static void ln_ws_on_open(int client)
(void)client;
printf("WebSocketClientConstructor ln_ws_on_open\r\n");
g_ws_connected = WS_STATE_CONNECTED;
- ln_ws_reset_close_status(); // 连接成功时重置关闭状态
- printf("[LN][WS] 连接状态: 连接成功, 关闭状态已重置为 NOT_EXECUTED (%d)\n", g_ws_close_status);
+ 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,bool isBinary)
+/* 消息接收回调函数 */
+static void ln_ws_message_callback(const char *raw_data, uint32_t timestamp)
{
- (void)client;
- (void)isFinished;
- if (data == NULL || length == 0) return;
+ (void)timestamp;
+ if (raw_data == NULL) return;
+
+ size_t length = strlen(raw_data);
char *buf = (char *)malloc(length + 1);
if (!buf) return;
- memcpy(buf, data, length);
+ memcpy(buf, raw_data, length);
buf[length] = '\0';
/* 统一的消息解析处理 */
printf("WebSocketClientConstructor data\r\n");
if (length >= 2 && buf[0] == '{' && buf[length - 1] == '}') {
- // 解析消息并写入队列
+ // 解析消息并直接处理
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",
+ printf("[LN][WS] Message parsed successfully: command=%d (%s)\n",
data_msg.command, data_msg.command_str);
+
+ // 直接使用回调函数处理消息
+ ln_ws_process_message_with_callback(&data_msg);
+
+ // 释放消息内存
+ ln_ws_free_data_message(&data_msg);
} else {
printf("[LN][WS] Failed to parse message\n");
}
}
+ else
+ {
+ ln_ws_incomplete_callback(raw_data,length,false);
+ printf("[LN][WS] Invalid 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);
free(buf);
}
-/* 出错回调:仅打印,实际重连策略可在上层统一管理 */
-static void ln_ws_on_error(int client, struct WebSocketErrorResult error)
-{
- (void)client;
- printf("WebSocketClientConstructor [LN][WS] error: code=%d msg=%s\n", error.errorCode, error.errorMessage);
-}
-
-/* 断开回调:标记为未连接。是否自动重连交由上层决定 */
-static void ln_ws_on_close(int client)
+/* 连接事件回调函数 */
+static void ln_ws_event_callback(ws_event_type_t event_type)
{
- printf("WebSocketClientConstructor [LN][WS] ln_ws_on_close %d\r\n",client);
-
- // 在断开回调触发时,将关闭状态设置为成功
-
- printf("[LN][WS] WebSocket close callback triggered, status set to SUCCESS (%d)\n", g_ws_close_status);
-
+ switch (event_type) {
+ case WS_EVENT_CONNECTING:
+ printf("[LN][WS] Event: Connecting\n");
+ g_ws_connected = WS_STATE_CONNECTING;
+ break;
+ case WS_EVENT_CONNECTED:
+ printf("[LN][WS] Event: Connected\n");
+ g_ws_connected = WS_STATE_CONNECTED;
+ break;
+ case WS_EVENT_DISCONNECTED:
+ printf("[LN][WS] Event: Disconnected\n");
g_ws_connected = WS_STATE_DISCONNECTED;
- printf("[LN][WS] 连接状态: 未连接\n");
- if(g_ws_client_fd>=0)
- {
- // 这里不再调用 ln_ws_close_with_status,因为回调已经触发说明关闭成功
- if(g_ws_close_status==WS_CLOSE_NOT_EXECUTED)
- {
- // int ret = WebSocketClientClose(g_ws_client_fd);
-
- }
- else
- {
- g_ws_client_fd=-1;
- }
- }
/* 断开后增加重连计数,由监督线程在下一次连接时携带 */
if (g_ws_running) {
if (g_ws_reconnect_count < 0xFFFFFFFFu) {
g_ws_reconnect_count++;
}
}
-}
-
-/*
- * 主线程:负责连接管理、心跳维护和队列消息处理
- * - 监督连接状态,处理重连逻辑
- * - 每30秒发送一次 "ping" 维持链路活性
- * - 处理队列中的消息
- */
-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 (osMessageQueueGet(g_ws_msg_queue, &msg, NULL, 1000) == osOK) {
- printf("[LN][WS] osMessageQueueGet: 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) {
- // 对于文本消息,我们需要特殊处理,因为现在data是ws_data_msg_t类型
- // 这里需要重新设计文本消息的存储方式
- printf("[LN][WS] Text message type not supported in new structure\n");
- } 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) {
- printf("[LN][WS] WebSocketClientClose\n");
- ln_ws_close_with_status(g_ws_client_fd);
- printf("[LN][WS] WebSocketClientClose end\n");
- }
- 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:
- {
- ws_data_msg_t *data_msg = &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);
-
- // 使用回调函数处理消息
- ln_ws_process_message_with_callback(data_msg);
-
- // 释放消息内存
- ln_ws_free_data_message(data_msg);
- }
- break;
-
- default:
- printf("[LN][WS] Unknown message type: %d\n", msg.msg_type);
- break;
- }
- }
- else
- {
- // printf("osMessageQueueGet timeout or failed\r\n");
- }
-
- // 连接管理部分
- if (g_ws_connected == WS_STATE_SLEEP) {
- // 休眠状态:暂停所有操作,只打印状态信息
- printf("[LN][WS] Sleep mode: pausing all operations\n");
- usleep(1000 * 1000); // 休眠1秒
- continue;
- }
-
- if (g_ws_connected == WS_STATE_DISCONNECTED) {
- if (g_ws_client_fd >= 0) {
- {
- if(g_ws_close_status== WS_CLOSE_NOT_EXECUTED)
- {
- printf("[LN][WS] WebSocketClientClose WS_CLOSE_NOT_EXECUTED\n");
- ln_ws_close_with_status(g_ws_client_fd);
- g_ws_client_fd=-1;
- printf("[LN][WS] WebSocketClientClose WS_CLOSE_NOT_EXECUTED end\n");
-
- }
- }
- }
-
- 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));
-
- 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)ln_ws_close_with_status(g_ws_client_fd);
- g_ws_client_fd = -1;
- /* 指数退避*/
- unsigned int delay = 1u << (g_ws_reconnect_count > 5 ? 5 : g_ws_reconnect_count);
- if (delay > 60) delay = 60;
- for (unsigned int i = 0; i < delay && g_ws_running; i++) {
- usleep(1000 * 1000);
- }
- continue;
- }
- 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; // 重置计数器
- }
- }
- else
- {
- printf("ln ws_state %s\r\n",ln_ws_get_connection_state_string());
- }
-
- /* 1秒间隔 */
- usleep(1000 * 1000);
- }
-
-
- if (g_ws_client_fd >= 0) {
- ln_ws_close_with_status(g_ws_client_fd);
- }
- g_ws_connected = WS_STATE_DISCONNECTED;
-
- // 删除消息队列
- if (g_ws_msg_queue != NULL) {
-
- ws_msg_info_t msg;
- while (osMessageQueueGet(g_ws_msg_queue, &msg, NULL, 0) == osOK) {
- if(msg.data.raw_json)
- {
- free(msg.data.raw_json);
- }
-
- }
- osMessageQueueDelete(g_ws_msg_queue);
- g_ws_msg_queue = NULL;
+ break;
+ default:
+ printf("[LN][WS] Event: Unknown event type %d\n", event_type);
+ break;
}
}
-/*
- * 拼接连接URL:环境域名 + 业务必须的查询参数
- * - 认证方式采用“查询参数方式”:至少包含 deviceToken(规范允许)
- * - 兼容性增强:同时附带 deviceId 与 reconnect 计数,方便服务端校验与审计
- * - 如需切换环境,仅修改 base 即可
- */
-static void ln_ws_build_url(char *out, size_t out_size)
+/* URL构建回调函数 */
+static void ln_ws_url_callback(char *url, size_t url_size, char *header_buf, size_t header_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",
+ snprintf(url, url_size, "%s?deviceToken=%s&deviceId=%s&reconnect=%u",
base, token, device_id, g_ws_reconnect_count);
+
+ /* 构建头部信息 */
+ snprintf(header_buf, header_size, "deviceId: %s\r\n", device_id);
}
+
+/* 心跳处理回调函数 */
+static int ln_ws_heartbeat_callback(void)
+{
+ // 每30秒发送一次心跳
+ static int heartbeat_counter = 0;
+ heartbeat_counter++;
+ if (heartbeat_counter >= 15) { // 30秒 = 30次 * 1秒
+ heartbeat_counter = 0;
+ ln_ws_send_ping();
+ return 1; // 需要发送心跳
+ }
+ return 0; // 不需要发送心跳
+}
+
+
int ln_ws_send_ping(void)
{
- if (g_ws_connected != WS_STATE_CONNECTED || g_ws_client_fd < 0) {
+ if (g_ws_connected != WS_STATE_CONNECTED) {
printf("[LN][WS] Cannot send ping, connection state: %d\n", g_ws_connected);
return -1;
}
}
// 发送 JSON 字符串
- int ret = WebSocketClientSendText(g_ws_client_fd, json_string, strlen(json_string));
+ int ret = uapi_ws_send_text(json_string);
- printf("[LN][WS] Sent ping: %s %d\n", json_string,g_ws_client_fd);
+ printf("[LN][WS] Sent ping: %s\n", json_string);
// 清理内存
free(json_string);
}
+
+uint16_t send_audio=0;
+
+// 音频聊天相关结构体
+typedef struct {
+ char chat_id[64]; // 聊天ID
+ uint32_t message_counter; // 消息计数器
+ bool is_active; // 是否活跃
+} audio_chat_context_t;
+
+// 全局音频聊天上下文
+static audio_chat_context_t g_audio_chat_ctx = {0};
+
+// 音频聊天结束标志
+static bool g_audio_chat_ending = false;
+
+// 函数声明
+static void generate_chat_id(char *chat_id, size_t size);
+static void generate_message_id(char *message_id, size_t size, const char *chat_id, uint32_t counter);
+
+// 生成聊天ID
+static void generate_chat_id(char *chat_id, size_t size)
+{
+ if (!chat_id || size < 16) return;
+
+ uint32_t timestamp;
+ uapi_get_timestampe_ss(×tamp);
+
+ snprintf(chat_id, size, "chat_%u_%u", timestamp, (uint32_t)rand());
+}
+
+// 生成消息ID
+static void generate_message_id(char *message_id, size_t size, const char *chat_id, uint32_t counter)
+{
+ if (!message_id || !chat_id || size < 32) return;
+
+ snprintf(message_id, size, "%s_msg_%u", chat_id, counter);
+}
+
+// 发送音频数据到WebSocket
+int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool finished)
+{
+ // 检查结束标志位,如果设置了结束标志,则finished必须为true
+ if (g_audio_chat_ending && !finished) {
+ printf("[LN][WS] Audio chat is ending, finished must be true\n");
+ return -1;
+ }
+
+ // 对于正常音频数据,必须有有效的数据
+ if (!finished && (!audio_data || audio_size == 0)) {
+ printf("[LN][WS] Invalid audio data parameters\n");
+ return -1;
+ }
+
+ if (g_ws_connected != WS_STATE_CONNECTED) {
+ printf("[LN][WS] Cannot send audio data, connection state: %d\n", g_ws_connected);
+ return -1;
+ }
+
+ // 如果聊天未开始,生成新的聊天ID
+ if (!g_audio_chat_ctx.is_active) {
+ generate_chat_id(g_audio_chat_ctx.chat_id, sizeof(g_audio_chat_ctx.chat_id));
+ g_audio_chat_ctx.message_counter = 0;
+ g_audio_chat_ctx.is_active = true;
+ printf("[LN][WS] New chat started: %s\n", g_audio_chat_ctx.chat_id);
+ }
+
+ // 增加消息计数器
+ g_audio_chat_ctx.message_counter++;
+
+ // 生成消息ID
+ char message_id[128];
+ generate_message_id(message_id, sizeof(message_id), g_audio_chat_ctx.chat_id, g_audio_chat_ctx.message_counter);
+
+ // Base64编码音频数据(如果是结束消息,则使用空字符串)
+ char *audio_base64 = NULL;
+ if (audio_size == 0) {
+ // 结束消息,使用空字符串
+ audio_base64 = (char*)malloc(1);
+ if (!audio_base64) {
+ printf("[LN][WS] Failed to allocate memory for base64 encoding\n");
+ return -1;
+ }
+ audio_base64[0] = '\0';
+ } else {
+ // 正常音频数据,进行Base64编码
+ size_t base64_size =audio_size*2; //((audio_size + 2) / 3) * 4 + 1;
+ audio_base64 = (char*)malloc(base64_size);
+ if (!audio_base64) {
+ printf("[LN][WS] Failed to allocate memory for base64 encoding\n");
+ return -1;
+ }
+
+ size_t output_len = 0;
+ int ret_encode = mbedtls_base64_encode((unsigned char*)audio_base64, base64_size - 1, &output_len,
+ audio_data, audio_size);
+ if (ret_encode != 0) {
+ printf("[LN][WS] Base64 encoding failed: %d\n", ret_encode);
+ free(audio_base64);
+ return -1;
+ }
+ audio_base64[output_len] = '\0';
+ }
+
+ // 获取当前时间戳
+ uint32_t timestamp;
+ uapi_get_timestampe_ss(×tamp);
+
+ // 创建JSON消息
+ cJSON *json = cJSON_CreateObject();
+ if (!json) {
+ printf("[LN][WS] Failed to create JSON object\n");
+ free(audio_base64);
+ return -1;
+ }
+
+ // 添加基本字段
+ cJSON_AddStringToObject(json, "deviceId", uapi_ln_get_device_id());
+ cJSON_AddNumberToObject(json, "timestamp", (double)timestamp);
+ cJSON_AddStringToObject(json, "messageId", message_id);
+ cJSON_AddStringToObject(json, "command", "VoiceChatRequest");
+
+ // 创建data对象
+ cJSON *data_obj = cJSON_CreateObject();
+ if (!data_obj) {
+ printf("[LN][WS] Failed to create data object\n");
+ cJSON_Delete(json);
+ free(audio_base64);
+ return -1;
+ }
+
+ cJSON_AddStringToObject(data_obj, "audioBase64", audio_base64);
+ cJSON_AddStringToObject(data_obj, "chatId", g_audio_chat_ctx.chat_id);
+ cJSON_AddBoolToObject(data_obj, "finished", finished);
+
+ cJSON_AddItemToObject(json, "data", data_obj);
+
+ // 转换为字符串
+ char *json_string = cJSON_Print(json);
+ if (!json_string) {
+ printf("[LN][WS] Failed to print JSON\n");
+ cJSON_Delete(json);
+ free(audio_base64);
+ return -1;
+ }
+
+ // 发送JSON字符串
+ int ret = uapi_ws_send_text(json_string);
+
+ printf("[LN][WS] Sent audio data: chatId=%s, messageId=%s, size=%u, finished=%s\n",
+ g_audio_chat_ctx.chat_id, message_id, audio_size, finished ? "true" : "false");
+
+ // 清理内存
+ free(json_string);
+ free(audio_base64);
+ cJSON_Delete(json);
+
+ // 如果音频结束,重置聊天上下文
+ if (finished) {
+ g_audio_chat_ctx.is_active = false;
+ printf("[LN][WS] Chat ended: %s\n", g_audio_chat_ctx.chat_id);
+ }
+
+ return ret;
+}
+
+// 开始新的音频聊天
+int ln_ws_start_audio_chat(void)
+{
+ if (g_audio_chat_ctx.is_active) {
+ printf("[LN][WS] Audio chat already active: %s\n", g_audio_chat_ctx.chat_id);
+ return 0;
+ }
+
+ generate_chat_id(g_audio_chat_ctx.chat_id, sizeof(g_audio_chat_ctx.chat_id));
+ g_audio_chat_ctx.message_counter = 0;
+ g_audio_chat_ctx.is_active = true;
+
+ printf("[LN][WS] Audio chat started: %s\n", g_audio_chat_ctx.chat_id);
+ return 0;
+}
+
+// 结束当前音频聊天
+int ln_ws_end_audio_chat(void)
+{
+ if (!g_audio_chat_ctx.is_active) {
+ printf("[LN][WS] No active audio chat to end\n");
+ return -1;
+ }
+ printf("[LN][WS] Ending audio chat: %s, total messages: %u\n",
+ g_audio_chat_ctx.chat_id, g_audio_chat_ctx.message_counter);
+ // 设置结束标志,等待下次AIVoiceDataCallback_ln调用时发送结束消息
+ g_audio_chat_ending = true;
+
+ return 0;
+}
+
+// 获取当前聊天ID
+const char* ln_ws_get_current_chat_id(void)
+{
+ return g_audio_chat_ctx.is_active ? g_audio_chat_ctx.chat_id : NULL;
+}
+
+static void AIVoiceDataCallback_ln(const uint8_t* data, uint32_t size, void* userData)
+{
+ // MEDIA_INFO_LOG("Received audio data: size=%u bytes", size);
+ if( g_audio_chat_ctx.is_active ==true)
+ {
+ if (g_audio_chat_ending) {
+ printf("[LN][WS] Sending audio chat end message\n");
+ // 发送结束消息
+ ln_ws_send_audio_data(data, size, true);
+ // 重置聊天上下文和结束标志
+ g_audio_chat_ctx.is_active = false;
+ g_audio_chat_ending = false;
+ }
+ else
+
+ {
+ // 使用新的音频发送接口
+ ln_ws_send_audio_data(data, size, false);
+ }
+ }
+
+ // 检查是否设置了结束标志
+
+}
+
+// "audioBase64":
+
+static void ai_binary_message_handler(const uint8_t *raw_data, uint32_t timestamp, uint16_t data_len)
+{
+ printf("[AI] Received binary message at %u, length: %d bytes\n", timestamp, data_len);
+
+ if (!raw_data || data_len == 0) {
+ printf("[AI] Invalid binary data parameters\n");
+ return;
+ }
+
+ const size_t CHUNK_SIZE = 1024; // 1K 分块大小
+
+ if (data_len <= CHUNK_SIZE) {
+ // 数据小于等于1K,直接发送
+ printf("[AI] Sending data directly (size: %d bytes)\n", data_len);
+ AI_SendAudioDataToQueue(raw_data, data_len);
+ } else {
+ // 数据大于1K,分块发送
+ printf("[AI] Data size %d bytes > %zu bytes, splitting into chunks\n", data_len, CHUNK_SIZE);
+
+ size_t remaining = data_len;
+ const uint8_t *current_pos = raw_data;
+ int chunk_count = 0;
+
+ while (remaining > 0) {
+ size_t current_chunk_size = (remaining > CHUNK_SIZE) ? CHUNK_SIZE : remaining;
+
+ printf("[AI] Sending chunk %d: %zu bytes\n", ++chunk_count, current_chunk_size);
+ AI_SendAudioDataToQueue(current_pos, current_chunk_size);
+
+ current_pos += current_chunk_size;
+ remaining -= current_chunk_size;
+
+ // 添加小延迟避免过快发送
+ usleep(1000); // 1ms延迟
+ }
+
+ printf("[AI] All %d chunks sent successfully\n", chunk_count);
+ }
+}
+
+
+static void ln_ws_incomplete_callback(const char *raw_data, size_t length, bool isBinary)
+{
+ if (!raw_data || length == 0) {
+ return;
+ }
+
+ printf("[LN][WS] Incomplete message received: length=%zu, isBinary=%s\n",
+ length, isBinary ? "true" : "false");
+
+ // 检查是否包含 "audioBase64":" 字符串
+ const char *audioBase64_key = "\"audioBase64\":\"";
+ const char *found = strstr(raw_data, audioBase64_key);
+
+ if (found) {
+ printf("[LN][WS] Found audioBase64 key in incomplete message\n");
+
+ // 找到 "audioBase64":" 后面的内容
+ const char *value_start = found + strlen(audioBase64_key);
+
+ // 找到结束引号
+ const char *value_end = strchr(value_start, '"');
+
+ if (value_end) {
+ // 找到结束引号,数据完整
+ size_t base64_length = value_end - value_start;
+ printf("[LN][WS] Found complete Base64 data: length=%zu\n", base64_length);
+
+ // 处理完整的Base64数据
+ ln_ws_process_base64_data(value_start, base64_length);
+
+ // 重置缓存状态
+ ln_ws_base64_cache_reset();
+
+ } else {
+ // 没有找到结束引号,数据不完整
+ printf("[LN][WS] No closing quote found, data incomplete\n");
+
+ // 处理不完整的Base64数据
+ size_t remaining_length = length - (value_start - raw_data);
+ if (remaining_length > 0) {
+ if (!g_base64_in_progress) {
+ g_base64_in_progress = true;
+ }
+ ln_ws_process_incremental_base64_data(value_start, remaining_length);
+ }
+ }
+ } else {
+ // 没有找到audioBase64关键字
+ if (g_base64_in_progress) {
+ // 如果正在进行Base64数据接收,检查是否包含结束引号
+ const char *end_quote = strchr(raw_data, '"');
+ if (end_quote) {
+ // 找到结束引号,处理到引号位置的数据
+ size_t data_before_quote = end_quote - raw_data;
+ if (data_before_quote > 0) {
+ printf("[LN][WS] Found end quote, processing data before quote: %zu bytes\n", data_before_quote);
+ ln_ws_process_incremental_base64_data(raw_data, data_before_quote);
+ }
+
+ // 处理缓存中剩余的数据(如果有的话)
+ if (g_base64_cache_size > 0) {
+ printf("[LN][WS] Processing remaining cached data: %zu bytes\n", g_base64_cache_size);
+ ln_ws_process_base64_data(g_base64_cache, g_base64_cache_size);
+ ln_ws_base64_cache_reset();
+ }
+
+ // 结束Base64数据接收
+ g_base64_in_progress = false;
+ printf("[LN][WS] Base64 data reception completed\n");
+ } else {
+ // 没有结束引号,继续处理增量数据
+ printf("[LN][WS] Continuing Base64 data reception, processing incrementally\n");
+ ln_ws_process_incremental_base64_data(raw_data, length);
+ }
+ } else {
+ printf("[LN][WS] No audioBase64 key found in message\n");
+ }
+ }
+}
/*
* 启动:开启监督线程与心跳机制(心跳线程首次连接成功后会自然启动)
* - 若已在运行则直接返回成功
* - callback: 消息处理回调函数,可以为NULL使用默认处理
*/
-int ln_ws_start(ws_message_callback_t callback)
+int ln_ws_start(ws_message_callback_t_ln callback)
{
if (g_ws_running) {
return 0;
} else {
printf("[LN][WS] Using default message handler\n");
}
- if (!g_ws_main_thread) {
- app_task_definition_ext_t main_task = {
- .attr = {
- .name = "ln_ws_main",
- .stack_size = 0x2000,
- .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);
+
+ // 设置回调函数结构体
+ g_ws_callbacks.message_callback = ln_ws_message_callback;
+ g_ws_callbacks.event_callback = ln_ws_event_callback;
+ g_ws_callbacks.url_callback = ln_ws_url_callback;
+ g_ws_callbacks.heartbeat_callback = ln_ws_heartbeat_callback;
+ g_ws_callbacks.binary_callback = ai_binary_message_handler;
+ g_ws_callbacks.incomplete_callback= ln_ws_incomplete_callback;
+ // 启动WebSocket服务
+ int ret = uapi_ws_start(&g_ws_callbacks);
+ if (ret != 0) {
+ printf("[LN][WS] uapi_ws_start failed: %d\n", ret);
+ g_ws_running = 0;
+ return ret;
}
+
+ old_Start_AIVoice(AIVoiceDataCallback_ln,NULL,NULL,NULL,false);
+
cat1_api_keep_wakeup(1);
cat1_api_wakeup_cat1(1);
int ln_ws_send_text(const char *text)
{
if (!text) return -1;
- if (g_ws_connected != WS_STATE_CONNECTED || g_ws_client_fd < 0) {
+ if (g_ws_connected != WS_STATE_CONNECTED) {
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;
-
- // 由于新的数据结构不支持直接文本消息,这里暂时返回错误
- // 如果需要支持文本消息,需要重新设计消息类型
- printf("[LN][WS] Text message not supported in new queue structure\n");
- return -1;
+ return uapi_ws_send_text(text);
}
-/* 强制重连 */
-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.raw_json = NULL;
-
- osStatus_t ret = osMessageQueuePut(g_ws_msg_queue, &msg, 0, 0);
- return (ret == osOK) ? 0 : -1;
-}
-
-/* 通过队列停止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.raw_json = NULL;
- // msg.data = NULL;
-
- osStatus_t ret = osMessageQueuePut(g_ws_msg_queue, &msg, 0, 0);
- return (ret == osOK) ? 0 : -1;
-}
/* 停止:关闭心跳并关闭连接 */
int ln_ws_stop(void)
{
g_ws_running = 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 = ln_ws_close_with_status(g_ws_client_fd);
- g_ws_client_fd = -1;
- }
+ int ret = uapi_ws_stop();
g_ws_connected = WS_STATE_DISCONNECTED;
- // 删除消息队列
- if (g_ws_msg_queue != NULL) {
- osMessageQueueDelete(g_ws_msg_queue);
- g_ws_msg_queue = NULL;
- }
+ // 清理Base64缓存
+ ln_ws_base64_cache_cleanup();
return ret;
}
* @brief 注册消息处理回调函数(公共接口)
* @param callback 回调函数
*/
-void ln_ws_register_message_callback(ws_message_callback_t callback)
+void ln_ws_register_message_callback(ws_message_callback_t_ln callback)
{
g_ws_message_callback = callback;
if (callback != NULL) {
return WS_CMD_PING;
} else if (strcmp(command_str, "Pong") == 0) {
return WS_CMD_PONG;
+ } else if (strcmp(command_str, "VoiceChatResponse") == 0) {
+ return WS_CMD_VOICE_CHAT_RESPONSE;
} else {
return WS_CMD_UNKNOWN;
}
* @param data_msg 输出的消息结构体
* @return 0成功,-1失败
*/
+
+// {"deviceId":"DEVICE_PC_TEST_001","timestamp":1758880220,
+// "messageId":"d63a0670a3854d709cb95720e1e89f83","command":"VoiceChatResponse","data":
+// {"finished":false,"chatId":"chat_1758880210_2087035594",
+// "data":{"audioUrl":"http://ambybox-api.test.leiniao.com/ambybox-device-api/file/774317549446_20250926175018_1758880218961.mp3",
+// "textContent":"(颔首,夹着烟的手指轻敲桌面)来者何人?"}}}
+
static int ln_ws_parse_data_message(const char *json_str, ws_data_msg_t *data_msg)
{
if (!json_str || !data_msg) {
return -1;
}
+ // 解析VoiceChatResponse特定字段
+ if (data_msg->command == WS_CMD_VOICE_CHAT_RESPONSE) {
+ cJSON *data_obj = cJSON_GetObjectItem(root, "data");
+ if (data_obj && cJSON_IsObject(data_obj)) {
+ // 解析finished字段
+ cJSON *finished = cJSON_GetObjectItem(data_obj, "finished");
+ if (finished && cJSON_IsBool(finished)) {
+ data_msg->finished = cJSON_IsTrue(finished);
+ }
+
+ // 解析chatId字段
+ cJSON *chat_id = cJSON_GetObjectItem(data_obj, "chatId");
+ if (chat_id && cJSON_IsString(chat_id) && chat_id->valuestring) {
+ strncpy(data_msg->chat_id, chat_id->valuestring, sizeof(data_msg->chat_id) - 1);
+ data_msg->chat_id[sizeof(data_msg->chat_id) - 1] = '\0';
+ }
+
+ // 解析data.data字段
+ cJSON *data_data = cJSON_GetObjectItem(data_obj, "data");
+ if (data_data && cJSON_IsObject(data_data)) {
+ // 解析audioUrl字段
+ cJSON *audio_url = cJSON_GetObjectItem(data_data, "audioUrl");
+ if (audio_url && cJSON_IsString(audio_url) && audio_url->valuestring) {
+ strncpy(data_msg->audio_url, audio_url->valuestring, sizeof(data_msg->audio_url) - 1);
+ data_msg->audio_url[sizeof(data_msg->audio_url) - 1] = '\0';
+ }
+
+ // 解析textContent字段
+ cJSON *text_content = cJSON_GetObjectItem(data_data, "textContent");
+ if (text_content && cJSON_IsString(text_content) && text_content->valuestring) {
+ strncpy(data_msg->text_content, text_content->valuestring, sizeof(data_msg->text_content) - 1);
+ data_msg->text_content[sizeof(data_msg->text_content) - 1] = '\0';
+ }
+ }
+ }
+
+ printf("[LN][WS] VoiceChatResponse parsed: finished=%s, chatId=%s, audioUrl=%s, textContent=%s\n",
+ data_msg->finished ? "true" : "false", data_msg->chat_id, data_msg->audio_url, data_msg->text_content);
+ }
+
// 保存原始JSON字符串
data_msg->raw_json = (char*)malloc(strlen(json_str) + 1);
if (data_msg->raw_json) {
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);
-
- // 直接复制数据到结构体中
- memcpy(&msg.data, data_msg, sizeof(ws_data_msg_t));
-
- // 写入队列
- osStatus_t ret = osMessageQueuePut(g_ws_msg_queue, &msg, 0, 0);
- if (ret != osOK) {
- printf("[LN][WS] Failed to put data message to queue: %d\n", ret);
- return -1;
- }
-
- printf("[LN][WS] Data message queued successfully count %d\n", osMessageQueueGetCount(g_ws_msg_queue));
- return 0;
-}
/**
* @brief 释放消息结构体内存
case WS_CMD_SCHEDULE_REMINDER_CFG:
printf("[LN][WS] Default: Processing ScheduleReminderConfig message\n");
break;
+ case WS_CMD_VOICE_CHAT_RESPONSE:
+ printf("[LN][WS] Default: Processing VoiceChatResponse message\n");
+ printf("[LN][WS] VoiceChatResponse details: finished=%s, chatId=%s\n",
+ data_msg->finished ? "true" : "false", data_msg->chat_id);
+ printf("[LN][WS] Audio URL: %s\n", data_msg->audio_url);
+ printf("[LN][WS] Text Content: %s\n", data_msg->text_content);
+ break;
default:
printf("[LN][WS] Default: Processing unknown command: %d\n", type);
break;
}
}
-/* ============================ WebSocket关闭状态管理 ============================ */
+
+/* ============================ 公共接口函数 ============================ */
+
+
/**
- * @brief 带状态跟踪的WebSocket关闭函数
- * @param client_fd WebSocket客户端文件描述符
- * @return WebSocketClientClose的返回值
+ * @brief 进入休眠状态(公共接口)
+ * @return 0成功,-1失败
*/
-static int ln_ws_close_with_status(int client_fd)
+int ln_ws_enter_sleep(void)
{
- if (client_fd < 0) {
- printf("[LN][WS] Invalid client_fd for close operation\n");
- return -1;
- }
-
- // 设置状态为开始执行
- g_ws_close_status = WS_CLOSE_STARTING;
- printf("[LN][WS] WebSocketClientClose starting, fd=%d, status=%d\n", client_fd, g_ws_close_status);
+ printf("[LN][WS] Entering sleep mode\n");
- // 执行关闭操作
- int ret = WebSocketClientClose(client_fd);
+ if (!g_ws_running) {
+ printf("[LN][WS] WebSocket service not running, cannot enter sleep\n");
+ return -1;
+ }
- // 注意:成功状态由 ln_ws_on_close 回调函数设置,这里只处理失败情况
- if (ret != 0) {
- g_ws_close_status = WS_CLOSE_NOT_EXECUTED;
- printf("[LN][WS] WebSocketClientClose failed, fd=%d, ret=%d, status=%d\n", client_fd, ret, g_ws_close_status);
- } else {
- printf("[LN][WS] WebSocketClientClose call returned success, waiting for close callback\n");
+ // 使用新的接口进入休眠
+ int ret = uapi_ws_enter_sleep();
+ if (ret == 0) {
+ g_ws_connected = WS_STATE_SLEEP;
+ printf("[LN][WS] Sleep mode activated, all operations paused\n");
}
return ret;
}
/**
- * @brief 获取当前WebSocket关闭状态
- * @return 当前关闭状态
+ * @brief 退出休眠状态(公共接口)
+ * @return 0成功,-1失败
*/
-static ws_close_status_t ln_ws_get_close_status(void)
+int ln_ws_exit_sleep(void)
{
- return g_ws_close_status;
+ printf("[LN][WS] Exiting sleep mode\n");
+
+ if (g_ws_connected != WS_STATE_SLEEP) {
+ printf("[LN][WS] Not in sleep mode, current state: %s\n", ln_ws_get_connection_state_string());
+ return -1;
+ }
+
+ // 使用新的接口退出休眠
+ int ret = uapi_ws_exit_sleep();
+ if (ret == 0) {
+ g_ws_connected = WS_STATE_DISCONNECTED;
+ printf("[LN][WS] Sleep mode deactivated, resuming normal operations\n");
+ }
+
+ return ret;
}
+/* ============================ Base64缓存管理函数 ============================ */
+
/**
- * @brief 重置WebSocket关闭状态
+ * @brief 初始化Base64缓存
*/
-static void ln_ws_reset_close_status(void)
+static void ln_ws_base64_cache_init(void)
{
- g_ws_close_status = WS_CLOSE_NOT_EXECUTED;
- printf("[LN][WS] WebSocket close status reset to NOT_EXECUTED (%d)\n", g_ws_close_status);
+ if (g_base64_cache == NULL) {
+ g_base64_cache_capacity = 4096; // 初始容量4KB
+ g_base64_cache = (char*)malloc(g_base64_cache_capacity);
+ if (g_base64_cache) {
+ g_base64_cache_size = 0;
+ g_base64_in_progress = false;
+ printf("[LN][WS] Base64 cache initialized with capacity: %zu\n", g_base64_cache_capacity);
+ } else {
+ printf("[LN][WS] Failed to initialize Base64 cache\n");
+ g_base64_cache_capacity = 0;
+ }
+ }
}
-/* ============================ 公共接口函数 ============================ */
-
/**
- * @brief 获取WebSocket关闭状态(公共接口)
- * @return 当前关闭状态
+ * @brief 清理Base64缓存
*/
-int ln_ws_get_close_status_public(void)
+static void ln_ws_base64_cache_cleanup(void)
{
- return (int)ln_ws_get_close_status();
+ if (g_base64_cache) {
+ free(g_base64_cache);
+ g_base64_cache = NULL;
+ g_base64_cache_size = 0;
+ g_base64_cache_capacity = 0;
+ g_base64_in_progress = false;
+ printf("[LN][WS] Base64 cache cleaned up\n");
+ }
}
/**
- * @brief 打印WebSocket状态信息(调试用)
+ * @brief 重置Base64缓存
*/
-void ln_ws_print_status_info(void)
+static void ln_ws_base64_cache_reset(void)
{
- printf("[LN][WS] === WebSocket Status Info ===\n");
- printf("[LN][WS] Connection State: %s (%d)\n", ln_ws_get_connection_state_string(), g_ws_connected);
- printf("[LN][WS] Close Status: %d (0=NOT_EXECUTED, 1=STARTING, 2=SUCCESS)\n", g_ws_close_status);
- printf("[LN][WS] Client FD: %d\n", g_ws_client_fd);
- printf("[LN][WS] Running: %d\n", g_ws_running);
- printf("[LN][WS] Reconnect Count: %u\n", g_ws_reconnect_count);
- printf("[LN][WS] ===============================\n");
+ if (g_base64_cache) {
+ g_base64_cache_size = 0;
+ g_base64_in_progress = false;
+ printf("[LN][WS] Base64 cache reset\n");
+ }
}
/**
- * @brief 进入休眠状态(公共接口)
+ * @brief 向Base64缓存追加数据
+ * @param data 要追加的数据
+ * @param length 数据长度
* @return 0成功,-1失败
*/
-int ln_ws_enter_sleep(void)
+static int ln_ws_base64_cache_append(const char *data, size_t length)
{
- printf("[LN][WS] Entering sleep mode\n");
+ if (!data || length == 0) {
+ return -1;
+ }
- if (!g_ws_running) {
- printf("[LN][WS] WebSocket service not running, cannot enter sleep\n");
- return -1;
+ // 确保缓存已初始化
+ if (g_base64_cache == NULL) {
+ ln_ws_base64_cache_init();
+ if (g_base64_cache == NULL) {
+ return -1;
+ }
}
- // 如果当前有连接,先断开
- if (g_ws_client_fd >= 0) {
- printf("[LN][WS] Disconnecting before entering sleep mode\n");
- ln_ws_close_with_status(g_ws_client_fd);
- }
+ // 检查是否需要扩容
+ if (g_base64_cache_size + length > g_base64_cache_capacity) {
+ size_t new_capacity = g_base64_cache_capacity * 2;
+ while (new_capacity < g_base64_cache_size + length) {
+ new_capacity *= 2;
+ }
+
+ char *new_cache = (char*)realloc(g_base64_cache, new_capacity);
+ if (new_cache) {
+ g_base64_cache = new_cache;
+ g_base64_cache_capacity = new_capacity;
+ printf("[LN][WS] Base64 cache expanded to capacity: %zu\n", g_base64_cache_capacity);
+ } else {
+ printf("[LN][WS] Failed to expand Base64 cache\n");
+ return -1;
+ }
+ }
- // 设置状态为休眠
- g_ws_connected = WS_STATE_SLEEP;
- printf("[LN][WS] Sleep mode activated, all operations paused\n");
+ // 追加数据
+ memcpy(g_base64_cache + g_base64_cache_size, data, length);
+ g_base64_cache_size += length;
+ g_base64_cache[g_base64_cache_size] = '\0'; // 确保字符串结束
+ printf("[LN][WS] Base64 cache appended %zu bytes, total size: %zu\n", length, g_base64_cache_size);
return 0;
}
/**
- * @brief 退出休眠状态(公共接口)
- * @return 0成功,-1失败
+ * @brief 处理Base64数据(验证、解码)
+ * @param base64_data Base64数据
+ * @param length 数据长度
*/
-int ln_ws_exit_sleep(void)
+static void ln_ws_process_base64_data(const char *base64_data, size_t length)
{
- printf("[LN][WS] Exiting sleep mode\n");
+ if (!base64_data || length == 0) {
+ printf("[LN][WS] Invalid Base64 data parameters\n");
+ return;
+ }
- if (g_ws_connected != WS_STATE_SLEEP) {
- printf("[LN][WS] Not in sleep mode, current state: %s\n", ln_ws_get_connection_state_string());
- return -1;
+ printf("[LN][WS] Processing Base64 data: length=%zu\n", length);
+
+ // 检查Base64长度是否有效(必须是4的倍数)
+ if (length % 4 != 0) {
+ printf("[LN][WS] Invalid Base64 length: %zu (not multiple of 4)\n", length);
+ return;
}
- // 设置状态为断开,让主线程重新开始连接
- g_ws_connected = WS_STATE_DISCONNECTED;
- printf("[LN][WS] Sleep mode deactivated, resuming normal operations\n");
+ // 检查是否包含有效的Base64字符
+ bool valid_base64 = true;
+ for (size_t i = 0; i < length; i++) {
+ char c = base64_data[i];
+ if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') || c == '+' || c == '/' || c == '=')) {
+ valid_base64 = false;
+ break;
+ }
+ }
- return 0;
+ if (!valid_base64) {
+ printf("[LN][WS] Invalid Base64 characters detected\n");
+ return;
+ }
+
+ printf("[LN][WS] Valid Base64 format detected, attempting decode\n");
+
+ // 计算解码后的数据长度
+ size_t decoded_length = (length * 3) / 4;
+
+ // 如果Base64字符串以'='结尾,需要调整长度
+ if (length >= 2 && base64_data[length - 1] == '=') {
+ if (base64_data[length - 2] == '=') {
+ decoded_length -= 2; // 两个填充字符
+ } else {
+ decoded_length -= 1; // 一个填充字符
+ }
+ }
+
+ printf("[LN][WS] Expected decoded length: %zu\n", decoded_length);
+
+ // 分配内存进行解码
+ uint8_t *decoded_data = (uint8_t*)malloc(decoded_length);
+ if (decoded_data) {
+ size_t actual_decoded_length = 0;
+
+ // 创建临时字符串进行解码
+ char *temp_base64 = (char*)malloc(length + 1);
+ if (temp_base64) {
+ memcpy(temp_base64, base64_data, length);
+ temp_base64[length] = '\0';
+
+ // 执行Base64解码
+ int ret = mbedtls_base64_decode(decoded_data, decoded_length,
+ &actual_decoded_length,
+ (const unsigned char*)temp_base64,
+ length);
+
+ if (ret == 0) {
+ printf("[LN][WS] Base64 decode successful: %zu bytes decoded\n", actual_decoded_length);
+
+ // 这里可以处理解码后的音频数据
+ // 例如:调用音频处理回调函数
+ printf("[LN][WS] Decoded audio data: first 16 bytes: ");
+ for (size_t i = 0; i < (actual_decoded_length < 16 ? actual_decoded_length : 16); i++) {
+ printf("%02X ", decoded_data[i]);
+ }
+ printf("\n");
+
+ // TODO: 在这里可以调用音频数据处理函数
+ AI_SendAudioDataToQueue(decoded_data, actual_decoded_length);
+
+ } else {
+ printf("[LN][WS] Base64 decode failed: %d\n", ret);
+ }
+
+ free(temp_base64);
+ }
+
+ free(decoded_data);
+ } else {
+ printf("[LN][WS] Failed to allocate memory for decoded data\n");
+ }
+}
+
+/**
+ * @brief 增量处理Base64数据(只缓存多余字符)
+ * @param new_data 新的Base64数据
+ * @param new_length 新数据长度
+ */
+static void ln_ws_process_incremental_base64_data(const char *new_data, size_t new_length)
+{
+ if (!new_data || new_length == 0) {
+ return;
+ }
+
+ printf("[LN][WS] Processing incremental Base64 data: length=%zu\n", new_length);
+
+ // 确保缓存已初始化
+ if (g_base64_cache == NULL) {
+ ln_ws_base64_cache_init();
+ if (g_base64_cache == NULL) {
+ return;
+ }
+ }
+
+ // 将新数据追加到缓存
+ if (ln_ws_base64_cache_append(new_data, new_length) != 0) {
+ printf("[LN][WS] Failed to append data to cache\n");
+ return;
+ }
+
+ // 计算可以完整解码的数据长度(必须是4的倍数)
+ size_t decodable_length = (g_base64_cache_size / 4) * 4;
+
+ if (decodable_length > 0) {
+ printf("[LN][WS] Decodable length: %zu, total cache: %zu\n", decodable_length, g_base64_cache_size);
+
+ // 解码可用的完整数据
+ ln_ws_process_base64_data(g_base64_cache, decodable_length);
+
+ // 保留多余的字符(不足4个字符的部分)
+ size_t remaining_length = g_base64_cache_size - decodable_length;
+ if (remaining_length > 0) {
+ printf("[LN][WS] Keeping %zu remaining characters in cache\n", remaining_length);
+
+ // 将剩余字符移到缓存开头
+ memmove(g_base64_cache, g_base64_cache + decodable_length, remaining_length);
+ g_base64_cache_size = remaining_length;
+ g_base64_cache[g_base64_cache_size] = '\0';
+ } else {
+ // 没有剩余字符,重置缓存
+ ln_ws_base64_cache_reset();
+ }
+ } else {
+ printf("[LN][WS] Not enough data for Base64 decode yet: %zu\n", g_base64_cache_size);
+ }
}
+////////////////////////////////////////////////////////////////////////////
+
+// int uapi_ln_download_mp3_file(const char *url)
+// {
+// if (!url)
+// {
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] url or save_path is NULL");
+// return ERRCODE_FAIL;
+// }
+
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 开始下载: %s -> %s", url);
+
+// HttpClient client = {0};
+// HttpClientData client_data = {0};
+// int ret = -1;
+// size_t total_written = 0;
+
+
+// HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, LN_CLIENT_PREPARE_SIZE * 2);
+// if (prep != HTTP_SUCCESS)
+// {
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] prepare failed: %d", prep);
+
+// return ERRCODE_FAIL;
+// }
+
+// // 建立连接
+// ret = HttpClientConn(&client, url);
+// if (ret != 0)
+// {
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] connection failed, ret=0x%x", ret);
+
+// HttpClientUnprepare(&client_data);
+// return ERRCODE_FAIL;
+// }
+
+// // 发送GET请求
+// ret = HttpClientSend(&client, url, HTTP_GET, &client_data);
+// if (ret != 0)
+// {
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] send request failed, ret=0x%x", ret);
+
+// HttpClientClse(&client);
+// HttpClientUnprepare(&client_data);
+// return ERRCODE_FAIL;
+// }
+
+// // 接收响应数据
+// do
+// {
+// HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data);
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] recv_res = %d", recv_res);
+// if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
+// {
+// if (client_data.contentBlockLen > 0)
+// {
+// // size_t written = fwrite(client_data.responseBuf, 1, client_data.contentBlockLen, fd);
+
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] written = %zu", written);
+// if (written != client_data.contentBlockLen)
+// {
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] write failed, written=%zu, expected=%d",
+// written, client_data.contentBlockLen);
+// HttpClientClse(&client);
+// HttpClientUnprepare(&client_data);
+// return ERRCODE_FAIL;
+// }
+// total_written += written;
+// }
+// }
+// } while (client_data.isMore);
+// if (client_data.responseContentLen == 0)
+// {
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] receive length = 0");
+// }
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] responsecode = %d", client.responseCode);
+// if (total_written == client_data.responseContentLen && client.responseCode == 200 && total_written > 0)
+// {
+// ret = ERRCODE_SUCC;
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] The file is downloaded successfully and the data is complete!");
+// }
+// else
+// {
+// ret = ERRCODE_FAIL;
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] The file data is incomplete!");
+// }
+// HttpClientUnprepare(&client_data);
+// HttpClientClse(&client);
+// WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] download process finished,ret=%d", ret);
+// return ret;
+// }
\ No newline at end of file