From 52dfd7803f9471d631c8d16ca9bcb15bdb146736 Mon Sep 17 00:00:00 2001 From: luozheng <1732036298@qq.com> Date: Sat, 27 Sep 2025 17:16:13 +0800 Subject: [PATCH] =?utf8?q?=E9=9B=B7=E9=B8=9F=EF=BC=9A=E5=88=A0=E9=99=A4?= =?utf8?q?=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81=EF=BC=8C=E5=BC=83=E7=94=A8?= =?utf8?q?=E9=9B=B7=E9=B8=9F=E7=9A=84=E6=8E=A5=E6=94=B6=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../zhihui/leiniao/imsl_ln/leiniao_api_at.c | 4 - code/sdk/zhihui/leiniao/imsl_ln/ln_api.c | 513 +----------------- code/sdk/zhihui/leiniao/imsl_ln/ln_api.h | 27 - .../leiniao/imsl_ln/ln_api_device_info.c | 4 - code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c | 254 ++++++++- .../leiniao/imsl_ln/ln_api_story_exit.c | 2 - .../leiniao/imsl_ln/ln_api_story_finish.c | 2 - 7 files changed, 243 insertions(+), 563 deletions(-) diff --git a/code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c b/code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c index bcc7c7139..647eca5ce 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/leiniao_api_at.c @@ -823,8 +823,6 @@ static uint32_t ln_audio_play_at(uint8_t *para, uint32_t paraLen, char *strCommo { VAD_UNUSED(para); VAD_UNUSED(paraLen); VAD_UNUSED(strCommonCmd); printf("[LN][AUDIO_PLAY]\n"); - // do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_PLAY, NULL, 0); - ln_ai_record_start(); return ERRCODE_SUCC; } @@ -833,8 +831,6 @@ static uint32_t ln_audio_stop_at(uint8_t *para, uint32_t paraLen, char *strCommo { VAD_UNUSED(para); VAD_UNUSED(paraLen); VAD_UNUSED(strCommonCmd); printf("[LN][AUDIO_STOP]\n"); - // do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_STOP, NULL, 0); - ln_ai_record_stop(); return ERRCODE_SUCC; } diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c index 010d26fe0..561b0099d 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.c @@ -11,19 +11,14 @@ #include #include #include "stdio.h" -#include "audio_stream_player.h" #include "custom_utils.h" #include "ai_media.h" // 声明播放器消息处理函数 -extern void ln_story_player_simple_handle_message(int cmd, char *data, uint32_t len); static app_task_definition_t ln_send_taskcfg = {0}; -static app_task_definition_t ln_recv_taskcfg = {0}; -#define LN_HTTP_BOARD_TASK_SIZE 0x2500 - static unsigned long g_ln_send_queue = 0xFFFFFFFF; -static unsigned long g_ln_recv_queue = 0xFFFFFFFF; +#define LN_HTTP_BOARD_TASK_SIZE 0x2500 static char *s_cached_device_id = NULL; char *g_device_token = NULL;//设备token,服务器获取 @@ -389,285 +384,6 @@ HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, i return ret; } -/* 计算文件MD5(小写hex)*/ -static int ln_calc_file_md5_lower_hex(const char *file_path, char out_hex[33]) -{ - if (file_path == NULL || out_hex == NULL) - { - return -1; - } - unsigned char md5_ctx_buf[16] = {0}; - mbedtls_md5_context ctx; - mbedtls_md5_init(&ctx); - if (mbedtls_md5_starts(&ctx) != 0) - { - mbedtls_md5_free(&ctx); - return -1; - } - FILE *fp = fopen(file_path, "rb"); - if (!fp) - { - mbedtls_md5_free(&ctx); - return -1; - } - /* 使用堆缓冲,避免在调用栈上占用4KB,降低线程峰值栈使用 */ - const size_t kBufSize = 4096; - unsigned char *buf = (unsigned char *)malloc(kBufSize); - if (!buf) - { - fclose(fp); - mbedtls_md5_free(&ctx); - return -1; - } - size_t n; - while ((n = fread(buf, 1, kBufSize, fp)) > 0) - { - if (mbedtls_md5_update(&ctx, buf, n) != 0) - { - fclose(fp); - free(buf); - mbedtls_md5_free(&ctx); - return -1; - } - } - fclose(fp); - free(buf); - if (mbedtls_md5_finish(&ctx, md5_ctx_buf) != 0) - { - mbedtls_md5_free(&ctx); - return -1; - } - mbedtls_md5_free(&ctx); - static const char hex[] = "0123456789abcdef"; - for (int i = 0; i < 16; i++) - { - out_hex[i * 2] = hex[(md5_ctx_buf[i] >> 4) & 0x0F]; - out_hex[i * 2 + 1] = hex[md5_ctx_buf[i] & 0x0F]; - } - out_hex[32] = '\0'; - return 0; -} - -/* 在接收线程内使用:解析 OTA 任务 JSON,决定是否触发下载 */ -static void ln_handle_ota_task_response(const char *json_buf, int json_len) -{ - if (!json_buf || json_len <= 0) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] handle ota task: invalid json"); - return; - } - char *text = (char *)malloc((size_t)json_len + 1); - if (!text) return; - memcpy(text, json_buf, (size_t)json_len); - text[json_len] = '\0'; - - cJSON *root = cJSON_Parse(text); - if (!root) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] handle ota task: json parse failed"); - free(text); - return; - } - - cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode"); - if (errorCode && cJSON_IsString(errorCode)) - { - /* 无升级或服务端错误 */ - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task errorCode=%s", errorCode->valuestring); - cJSON_Delete(root); - free(text); - return; - } - - cJSON *data = cJSON_GetObjectItem(root, "data"); - if (!data || !cJSON_IsObject(data)) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task data missing"); - cJSON_Delete(root); - free(text); - return; - } - - cJSON *targetVersion = cJSON_GetObjectItem(data, "targetVersion"); - cJSON *updateType = cJSON_GetObjectItem(data, "updateType"); - cJSON *packageUrl = cJSON_GetObjectItem(data, "packageUrl"); - cJSON *packageMd5 = cJSON_GetObjectItem(data, "packageMd5"); - cJSON *packageSize = cJSON_GetObjectItem(data, "packageSize"); - if (!(targetVersion && cJSON_IsString(targetVersion) && updateType && (cJSON_IsNumber(updateType) || cJSON_IsString(updateType)))) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task fields invalid"); - cJSON_Delete(root); - free(text); - return; - } - - /* 兼容 updateType 为数字或字符串的两种情况 */ - int ut = -1; - if (cJSON_IsNumber(updateType)) ut = (int)updateType->valuedouble; - else if (cJSON_IsString(updateType)) ut = atoi(updateType->valuestring); - g_ln_last_update_type = ut; - - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task parsed: targetVersion=%s updateType=%d url=%s md5=%s", - targetVersion->valuestring, ut, - (packageUrl && cJSON_IsString(packageUrl)) ? packageUrl->valuestring : "", - (packageMd5 && cJSON_IsString(packageMd5)) ? packageMd5->valuestring : ""); - /* 记录必要字段供后续 AT^LN_OTA_DOWNLOAD 使用 */ - if (g_ln_last_target_version) { free(g_ln_last_target_version); g_ln_last_target_version = NULL; } - if (g_ln_last_package_url) { free(g_ln_last_package_url); g_ln_last_package_url = NULL; } - if (g_ln_last_package_md5) { free(g_ln_last_package_md5); g_ln_last_package_md5 = NULL; } - g_ln_last_package_size = 0; - if (targetVersion && cJSON_IsString(targetVersion)) { - size_t len = strlen(targetVersion->valuestring); - g_ln_last_target_version = (char *)malloc(len + 1); - if (g_ln_last_target_version) memcpy(g_ln_last_target_version, targetVersion->valuestring, len + 1); - } - if (packageUrl && cJSON_IsString(packageUrl)) { - size_t len = strlen(packageUrl->valuestring); - g_ln_last_package_url = (char *)malloc(len + 1); - if (g_ln_last_package_url) memcpy(g_ln_last_package_url, packageUrl->valuestring, len + 1); - } - if (packageMd5 && cJSON_IsString(packageMd5)) { - size_t len = strlen(packageMd5->valuestring); - g_ln_last_package_md5 = (char *)malloc(len + 1); - if (g_ln_last_package_md5) memcpy(g_ln_last_package_md5, packageMd5->valuestring, len + 1); - } - if (packageSize && cJSON_IsNumber(packageSize)) { - g_ln_last_package_size = (int)packageSize->valuedouble; - if (g_ln_last_package_size < 0) g_ln_last_package_size = 0; - } - - if (ut == 1) - { - /* 强制升级:直接触发下载 */ - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] force upgrade: trigger download(强制升级:直接触发下载)"); - push_ota_status(OTA_UI_STATUS_FORCE_START); - } - else if (ut == 0) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] optional upgrade: waiting for user confirmation(可选升级:等待用户确认)"); - push_ota_status(OTA_UI_STATUS_OPTIONAL_START); - } - else - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] unknown updateType, skip"); - } - - cJSON_Delete(root); - free(text); -} - -/* 解析OTA下载消息JSON(简化实现)*/ -static int parse_ota_download_msg(const char *json_str, OtaDownloadMsg *msg) -{ - if (!json_str || !msg) return -1; - cJSON *root = cJSON_Parse(json_str); - if (!root) return -1; - - memset(msg, 0, sizeof(*msg)); - - cJSON *status = cJSON_GetObjectItem(root, "status"); - if (status && cJSON_IsNumber(status)) { - msg->status = (OtaDownloadStatus)status->valueint; - } - cJSON *progress = cJSON_GetObjectItem(root, "progress"); - if (progress && cJSON_IsNumber(progress)) { - msg->progress = progress->valueint; - if (msg->progress < 0) msg->progress = 0; - if (msg->progress > 100) msg->progress = 100; - } - cJSON *filePath = cJSON_GetObjectItem(root, "filePath"); - if (filePath && cJSON_IsString(filePath)) { - snprintf(msg->file_path, sizeof(msg->file_path), "%s", filePath->valuestring); - } - cJSON *error = cJSON_GetObjectItem(root, "error"); - if (error && cJSON_IsString(error)) { - snprintf(msg->error_msg, sizeof(msg->error_msg), "%s", error->valuestring); - } - cJSON *md5 = cJSON_GetObjectItem(root, "md5"); - if (md5 && cJSON_IsString(md5)) { - snprintf(msg->md5, sizeof(msg->md5), "%s", md5->valuestring); - } - - cJSON_Delete(root); - return 0; -} - -/* 校验文件MD5是否匹配(不区分大小写),返回1=通过,0=不通过,-1=出错 */ -static int ln_ota_is_md5_ok(const char *file_path, const char *expected_md5) -{ - if (file_path == NULL || expected_md5 == NULL || expected_md5[0] == '\0') - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] md5 verify: invalid args"); - return -1; - } - char calc[33] = {0}; - if (ln_calc_file_md5_lower_hex(file_path, calc) != 0) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] md5 verify: calc failed for %s", file_path); - return -1; - } - /* 规范化期望md5为小写并截断为32字符 */ - char expect_lower[33] = {0}; - size_t elen = strlen(expected_md5); - size_t copy_len = elen > 32 ? 32 : elen; - for (size_t i = 0; i < copy_len; i++) - { - char ch = expected_md5[i]; - if (ch >= 'A' && ch <= 'Z') ch = (char)(ch - 'A' + 'a'); - expect_lower[i] = ch; - } - expect_lower[32] = '\0'; - - int ok = (strcmp(calc, expect_lower) == 0) ? 1 : 0; - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] md5 verify result=%s, calc=%s, expect=%s", ok ? "OK" : "FAIL", calc, expect_lower); - return ok; -} - -/* OTA下载消息处理(对接时序图中的后续流程)*/ -static int reset_cnt = 0; -static void handle_ota_download_msg(const OtaDownloadMsg *msg) -{ - if (!msg) return; - switch (msg->status) { - case OTA_DOWNLOAD_STATUS_PROGRESS: - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载进度: %d%%", msg->progress); - push_ota_progress(msg->progress); - break; - case OTA_DOWNLOAD_STATUS_COMPLETED: - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载完成,文件路径: %s", msg->file_path); - if (msg->file_path[0] && msg->md5[0]) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 开始MD5校验: %s", msg->md5); - int md5_ok = ln_ota_is_md5_ok(msg->file_path, msg->md5); - if (md5_ok == 1) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] MD5校验成功,准备重启升级"); - push_ota_status(OTA_UI_STATUS_SUCCESS); - /* 外部可在此触发升级动作 */ - uaip_cmd_update_request(); - } else if (md5_ok == 0) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] MD5校验失败,建议重新下载"); - push_ota_status(OTA_UI_STATUS_FAILED); - } else { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] MD5校验出错"); - push_ota_status(OTA_UI_STATUS_FAILED); - } - } - break; - case OTA_DOWNLOAD_STATUS_FAILED: - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载失败: %s", msg->error_msg); - reset_cnt++; - if(reset_cnt < 3){ - do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD, NULL, 0); - } - break; - default: - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 未知状态: %d", msg->status); - break; - } -} - -/* ============================ OTA 包下载实现 End ============================ */ - - /* ============================ WebSocket 业务消息 上层处理 ============================ */ void ln_ws_process_message(const ws_data_msg_t *data_msg, ws_command_t type) { @@ -1062,7 +778,6 @@ static uint32_t ln_parse_and_process_json_data(const char *json_data, int interf if (text_content_err && strlen(text_content_err) > 0) { // 推送文本到UI - do_ln_http_server_recv_data(interface_type, (char *)text_content_err, (uint32_t)strlen(text_content_err)); } // 下载音频,并播放 @@ -1106,7 +821,6 @@ static uint32_t ln_parse_and_process_json_data(const char *json_data, int interf { // 推送文本到UI WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[JSON_PARSE] textContent: %s\n", textContent->valuestring); - do_ln_http_server_recv_data(interface_type, textContent->valuestring, (uint32_t)strlen(textContent->valuestring)); } else { @@ -1367,30 +1081,6 @@ static void ln_http_server_send_task(void const *arg) printf("LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD:下载OTA升级包\n"); ln_ota_download_flow_http(uapi_ln_get_device_id(), uapi_ln_get_device_token(), uapi_ln_get_device_version()); } - else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY) - { - printf("LN_HTTP_SERVICE_CMD_AUDIO_PLAY:播放音频\n"); - } - else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_STOP) - { - printf("LN_HTTP_SERVICE_CMD_AUDIO_STOP:停止播放音频\n"); - } - else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT) - { - printf("LN_HTTP_SERVICE_CMD_AUDIO_PLAY_NEXT:播放下一首\n"); - } - else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PLAY_FILE) - { - printf("LN_HTTP_SERVICE_CMD_AUDIO_PLAY_FILE:播放指定文件\n"); - } - else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_PAUSE) - { - printf("LN_HTTP_SERVICE_CMD_AUDIO_PAUSE:暂停播放\n"); - } - else if (mail.cmd == LN_HTTP_SERVICE_CMD_AUDIO_RESUME) - { - printf("LN_HTTP_SERVICE_CMD_AUDIO_RESUME:恢复播放\n"); - } else if (mail.cmd == LN_HTTP_SERVICE_CMD_EMOJI_REPORT) { printf("LN_HTTP_SERVICE_CMD_EMOJI_REPORT:上报角色表情组下载状态\n"); @@ -1413,98 +1103,6 @@ static void ln_http_server_send_task(void const *arg) } } -// 接收队列 -static void ln_http_server_recv_task(void const *arg) -{ - errcode_t ret; - - ln_http_data_mail_t mail = {0}; - uint32_t msgsize = sizeof(ln_http_data_mail_t); - - while (1) - { - /* 某些OSAL实现会在read后改写msgsize,循环开始时重置,避免下次读取异常 */ - msgsize = sizeof(ln_http_data_mail_t); - ret = osal_msg_queue_read_copy(g_ln_recv_queue, (void *)&mail, &msgsize, - OSAL_MSGQ_WAIT_FOREVER); - if (ret != OSAL_SUCCESS) - { - printf("[LN][RECV] queue read error=0x%x\n", ret); - LOS_Msleep(100); - continue; - } - - // 分类处理消息 - const char *tag = "UNKNOWN"; - if (mail.cmd == LN_HTTP_SERVICE_CMD_SSE_RECV) { - tag = "SPEECH_CHAT"; - //推送给ui - if(mail.data) - { - } - } else if (mail.cmd == LN_HTTP_RECV_STORY_CHOOSE) { - tag = "STORY_CHOOSE"; - //推送给ui - if(mail.data) - { - } - } else if (mail.cmd == LN_HTTP_RECV_STORY_RANDOM) { - tag = "STORY_RANDOM"; - //推送给ui - if(mail.data) - { - } - } else if (mail.cmd == LN_HTTP_RECV_STORY_EXIT) { - tag = "STORY_EXIT"; - } else if (mail.cmd == LN_HTTP_RECV_STORY_FINISH) { - tag = "STORY_FINISH"; - } else if (mail.cmd == LN_HTTP_RECV_DEVICE_CHARACTER) { - tag = "DEVICE_CHARACTER"; - //解析角色配置信息 - if(mail.data) - { - ln_parse_character_config(mail.data, (int)mail.len); - } - } else if (mail.cmd == LN_HTTP_RECV_DEVICE_USER_ID) { - tag = "DEVICE_USER_ID"; - } else if (mail.cmd == LN_HTTP_RECV_OTA_TASK) { - tag = "OTA_TASK"; - /* 在收到 OTA_TASK 后,根据升级策略决定是否触发下载 */ - if (mail.data) { - ln_handle_ota_task_response(mail.data, (int)mail.len); - } - } else if (mail.cmd == LN_HTTP_RECV_OTA_DOWNLOAD_MSG) { - tag = "OTA_DOWNLOAD_MSG"; - /* 在收到 OTA_DOWNLOAD_MSG 后,解析状态并回调处理(进度/完成/失败) */ - if (mail.data) - { - /* 改为堆分配,进一步降低接收线程的峰值栈占用 */ - OtaDownloadMsg *msg_ptr = (OtaDownloadMsg *)malloc(sizeof(OtaDownloadMsg)); - if (msg_ptr) - { - memset(msg_ptr, 0, sizeof(OtaDownloadMsg)); - if (parse_ota_download_msg(mail.data, msg_ptr) == 0) { - handle_ota_download_msg(msg_ptr); - } else { - printf("OTA_DOWNLOAD_MSG: parse failed\n"); - } - free(msg_ptr); - } - else - { - printf("OTA_DOWNLOAD_MSG: alloc failed\n"); - } - } - } - printf("[%s] 数据长度: %u\n", tag, mail.len); - if (mail.data) - { - printf("free(mail.data)\n"); - free(mail.data); - } - } -} - /* 创建雷鸟http发送队列 */ static void create_ln_http_send_queue(void) { @@ -1515,16 +1113,6 @@ static void create_ln_http_send_queue(void) } } -/* 创建雷鸟http接收队列 */ -static void create_ln_http_recv_queue(void) -{ - if (osal_msg_queue_create("ln_http_recv_queue", 32, &g_ln_recv_queue, - 0, sizeof(ln_http_data_mail_t)) != ERRCODE_SUCC) - { - printf("[ln][service] app create mail fail\n"); - } -} - /* 通用发送接口:向 LN HTTP 发送队列投递消息 */ void do_ln_http_server_send_data(int cmd, char *data, uint32_t len) { @@ -1553,83 +1141,6 @@ void do_ln_http_server_send_data(int cmd, char *data, uint32_t len) } } -/* 通用接收接口:向 LN HTTP 接收队列投递消息 */ -void do_ln_http_server_recv_data(int cmd, char *data, uint32_t len) -{ - errcode_t ret; - ln_http_data_mail_t recv_data = {0}; - recv_data.cmd = cmd; - if (len && data) - { - recv_data.len = len; - recv_data.data = (char *)malloc(len); - if (recv_data.data == NULL) - { - printf("malloc failed\n"); - return; - } - memcpy(recv_data.data, data, len); - } - - uint32_t send_len = sizeof(recv_data); - - ret = osal_msg_queue_write_copy(g_ln_recv_queue, (void *)&recv_data, - send_len, 0); - if (ret != OSAL_SUCCESS) - { - printf("Error ln http recv send failed! err=0x%x! \r\n", ret); - if (recv_data.data) free(recv_data.data); - } -} - -static FILE* g_aiCtx_fp = NULL; -static int g_is_ai_record_start = 0; -// 示例音频数据回调函数 -static void LeiniaoDataCallback(const uint8_t* data, uint32_t size, void* userData)//用来上传的数据,可以边录音边上传 -{ - // 在这里处理音频数据,例如: - // - 发送到AI语音识别服务 - // - 保存到文件 - // - 进行音频处理等 - // 注意:data指针指向的缓冲区在回调函数返回后可能被释放,如需保存数据请复制 - if(g_is_ai_record_start) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"Received audio data: size=%u bytes", size); - if (!data || size == 0) return; - const char *path = uapi_ln_get_audio_path(); - if (!path || !path[0]) return; - if (!g_aiCtx_fp) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"fopen %s\r\n", path); - g_aiCtx_fp = fopen(path, "wb+"); - if (!g_aiCtx_fp) - { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"fopen fail %s\r\n", path); - return; - } - } - if (g_aiCtx_fp) - { - (void)fwrite(data, 1, size, g_aiCtx_fp); - } - } -} - -void ln_ai_record_start(void) -{ - g_is_ai_record_start = 1; -} - -void ln_ai_record_stop(void) -{ - g_is_ai_record_start = 0; - if (g_aiCtx_fp) - { - fclose(g_aiCtx_fp); - g_aiCtx_fp = NULL; - } -} - /* 预创建运行期所需目录,避免因目录不存在导致文件创建失败 */ static void ln_init_required_dirs(void) { @@ -1678,28 +1189,6 @@ errcode_t uapi_ln_http_server_init(void) return ERRCODE_FAIL; } - ln_recv_taskcfg.create_queue_handle = create_ln_http_recv_queue; - ln_recv_taskcfg.attr.name = "ln_http_server_recv_task"; - ln_recv_taskcfg.attr.priority = osPriorityNormal; - ln_recv_taskcfg.attr.stack_size = 0x1000; - ln_recv_taskcfg.func = ln_http_server_recv_task; - ln_recv_taskcfg.use_mem = USE_EXTEND_MEM; - - ret = uapi_create_osThread_custom(&ln_recv_taskcfg); - if (ret != ERRCODE_SUCC) - { - printf("create gnss_recv_service failed %d!\r\n", ret); - return ERRCODE_FAIL; - } - - // /* 初始化音频播放器(流式输出) */ - // if (ln_audio_player_init() != ERRCODE_SUCC) - // { - // printf("[LN_AUDIO] init failed in http server init\n"); - // /* 不中断HTTP服务,按需可返回失败 */ - // } - - // old_Start_AIVoice(LeiniaoDataCallback,NULL); ln_init_required_dirs(); ln_load_init_config(); diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h index 11195b407..e87f41675 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api.h @@ -113,26 +113,6 @@ typedef struct { char story_theme[64]; /* 为空则随机 */ } ln_story_random_req_t; - -/* OTA下载状态枚举(对应时序图中的下载阶段)*/ -typedef enum -{ - OTA_DOWNLOAD_STATUS_DOWNLOADING = 0, /* 下载中 */ - OTA_DOWNLOAD_STATUS_COMPLETED = 1, /* 下载完成 */ - OTA_DOWNLOAD_STATUS_FAILED = 2, /* 下载失败 */ - OTA_DOWNLOAD_STATUS_PROGRESS = 3 /* 进度更新 */ -} OtaDownloadStatus; - -/* OTA下载消息结构体(简化版)*/ -typedef struct -{ - OtaDownloadStatus status; /* 下载状态*/ - int progress; /* 进度(0-100),仅状态为PROGRESS时有效 */ - char file_path[256]; /* 文件路径,仅状态为COMPLETED时有效 */ - char error_msg[128]; /* 错误信息,仅状态为FAILED时有效 */ - char md5[65]; /* MD5(来自任务),仅状态为COMPLETED时有效 */ -} OtaDownloadMsg; - /* =================== Emoji 表情上报相关结构体 =================== */ typedef struct { @@ -186,8 +166,6 @@ typedef struct errcode_t uapi_ln_http_server_init(void); /* 通用发送接口:向 LN HTTP 发送队列投递消息 */ void do_ln_http_server_send_data(int cmd, char *data, uint32_t len); -/* 通用接收接口:向 LN HTTP 接收队列投递消息(供发送线程/HTTP回调使用) */ -void do_ln_http_server_recv_data(int cmd, char *data, uint32_t len); /* 获取设备id */ char *uapi_ln_get_device_id(void); @@ -266,11 +244,6 @@ void ln_parse_download_emoji(const char *json_str, int json_len); /* 解析删除表情 */ void ln_parse_delete_emoji(const char *json_str, int json_len); -/* 开始录音 */ -void ln_ai_record_start(void); -/* 停止录音 */ -void ln_ai_record_stop(void); - /* ============================== 各接口函数声明 ============================== */ uint32_t ln_get_device_token_http(const char *device_id, const char *device_iccid, diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c index cea8b2ab9..e05d24e61 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_device_info.c @@ -297,7 +297,6 @@ uint32_t ln_device_get_character_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { // int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - // do_ln_http_server_recv_data(LN_HTTP_RECV_DEVICE_CHARACTER, clientData.responseBuf, (uint32_t)real_len); printf("device getCharacter: responseBuf=%s\n", clientData.responseBuf); } goto end; @@ -306,7 +305,6 @@ uint32_t ln_device_get_character_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - do_ln_http_server_recv_data(LN_HTTP_RECV_DEVICE_CHARACTER, clientData.responseBuf, (uint32_t)real_len); } end: @@ -374,7 +372,6 @@ uint32_t ln_device_get_user_id_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - do_ln_http_server_recv_data(LN_HTTP_RECV_DEVICE_USER_ID, clientData.responseBuf, (uint32_t)real_len); } goto end; } @@ -382,7 +379,6 @@ uint32_t ln_device_get_user_id_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - do_ln_http_server_recv_data(LN_HTTP_RECV_DEVICE_USER_ID, clientData.responseBuf, (uint32_t)real_len); } end: diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c index c30b74a6c..aaf157592 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_ota.c @@ -4,6 +4,229 @@ #include "upg_common_porting.h" /* 使用统一的 UPG_FILE_NAME 路径宏 */ #include #include +#include "mbedtls/md5.h" +#include "errcode.h" +#include "custom_utils.h" + +/* OTA下载状态枚举(对应时序图中的下载阶段)*/ +typedef enum +{ + OTA_DOWNLOAD_STATUS_DOWNLOADING = 0, /* 下载中 */ + OTA_DOWNLOAD_STATUS_COMPLETED = 1, /* 下载完成 */ + OTA_DOWNLOAD_STATUS_FAILED = 2, /* 下载失败 */ + OTA_DOWNLOAD_STATUS_PROGRESS = 3 /* 进度更新 */ +} OtaDownloadStatus; +/* OTA下载消息结构体(简化版)*/ +typedef struct +{ + OtaDownloadStatus status; /* 下载状态*/ + int progress; /* 进度(0-100),仅状态为PROGRESS时有效 */ + char file_path[256]; /* 文件路径,仅状态为COMPLETED时有效 */ + char error_msg[128]; /* 错误信息,仅状态为FAILED时有效 */ + char md5[65]; /* MD5(来自任务),仅状态为COMPLETED时有效 */ +} OtaDownloadMsg; + +/* ============================ OTA 内部工具与处理 ============================ */ +static int ln_calc_file_md5_lower_hex(const char *file_path, char out_hex[33]) +{ + if (!file_path || !out_hex) return -1; + FILE *fp = fopen(file_path, "rb"); + if (!fp) return -1; + mbedtls_md5_context ctx; + mbedtls_md5_init(&ctx); + if (mbedtls_md5_starts(&ctx) != 0) { mbedtls_md5_free(&ctx); fclose(fp); return -1; } + unsigned char buf[1024]; + size_t n; + while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { + if (mbedtls_md5_update(&ctx, buf, n) != 0) { mbedtls_md5_free(&ctx); fclose(fp); return -1; } + } + fclose(fp); + unsigned char md5_ctx_buf[16]; + if (mbedtls_md5_finish(&ctx, md5_ctx_buf) != 0) { mbedtls_md5_free(&ctx); return -1; } + mbedtls_md5_free(&ctx); + static const char hex[] = "0123456789abcdef"; + for (int i = 0; i < 16; i++) { + out_hex[i * 2] = hex[(md5_ctx_buf[i] >> 4) & 0x0F]; + out_hex[i * 2 + 1] = hex[md5_ctx_buf[i] & 0x0F]; + } + out_hex[32] = '\0'; + return 0; +} + +static int ln_ota_is_md5_ok(const char *file_path, const char *expected_md5) +{ + if (file_path == NULL || expected_md5 == NULL || expected_md5[0] == '\0') { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] md5 verify: invalid args"); + return -1; + } + char calc[33] = {0}; + if (ln_calc_file_md5_lower_hex(file_path, calc) != 0) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] md5 verify: calc failed for %s", file_path); + return -1; + } + char expect_lower[33] = {0}; + size_t elen = strlen(expected_md5); + size_t copy_len = elen > 32 ? 32 : elen; + for (size_t i = 0; i < copy_len; i++) { + char ch = expected_md5[i]; + if (ch >= 'A' && ch <= 'Z') ch = (char)(ch - 'A' + 'a'); + expect_lower[i] = ch; + } + expect_lower[32] = '\0'; + int ok = (strcmp(calc, expect_lower) == 0) ? 1 : 0; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] md5 verify result=%s, calc=%s, expect=%s", ok ? "OK" : "FAIL", calc, expect_lower); + return ok; +} + +static void ln_ota_handle_download_msg(const OtaDownloadMsg *msg) +{ + static int reset_cnt = 0; + if (!msg) return; + switch (msg->status) { + case OTA_DOWNLOAD_STATUS_PROGRESS: + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载进度: %d%%", msg->progress); + push_ota_progress(msg->progress); + break; + case OTA_DOWNLOAD_STATUS_COMPLETED: + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载完成,文件路径: %s", msg->file_path); + if (msg->file_path[0] && msg->md5[0]) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 开始MD5校验: %s", msg->md5); + int md5_ok = ln_ota_is_md5_ok(msg->file_path, msg->md5); + if (md5_ok == 1) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] MD5校验成功,准备重启升级"); + push_ota_status(OTA_UI_STATUS_SUCCESS); + (void)uaip_cmd_update_request(); + } else if (md5_ok == 0) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] MD5校验失败,建议重新下载"); + push_ota_status(OTA_UI_STATUS_FAILED); + } else { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] MD5校验出错"); + push_ota_status(OTA_UI_STATUS_FAILED); + } + } + break; + case OTA_DOWNLOAD_STATUS_FAILED: + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载失败: %s", msg->error_msg); + reset_cnt++; + if (reset_cnt < 3) { + do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD, NULL, 0); + } + break; + default: + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 未知状态: %d", msg->status); + break; + } +} +#include "cJSON.h" + +//解析 OTA 任务 JSON,决定是否触发下载 +static void ln_handle_ota_task_response(const char *json_buf, int json_len) +{ + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"解析OTA升级任务JSON"); + if (!json_buf || json_len <= 0) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] handle ota task: invalid json"); + return; + } + char *text = (char *)malloc((size_t)json_len + 1); + if (!text) return; + memcpy(text, json_buf, (size_t)json_len); + text[json_len] = '\0'; + + cJSON *root = cJSON_Parse(text); + if (!root) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] handle ota task: json parse failed"); + free(text); + return; + } + + cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode"); + if (errorCode && cJSON_IsString(errorCode)) + { + /* 无升级或服务端错误 */ + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task errorCode=%s", errorCode->valuestring); + cJSON_Delete(root); + free(text); + return; + } + + cJSON *data = cJSON_GetObjectItem(root, "data"); + if (!data || !cJSON_IsObject(data)) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task data missing"); + cJSON_Delete(root); + free(text); + return; + } + + cJSON *targetVersion = cJSON_GetObjectItem(data, "targetVersion"); + cJSON *updateType = cJSON_GetObjectItem(data, "updateType"); + cJSON *packageUrl = cJSON_GetObjectItem(data, "packageUrl"); + cJSON *packageMd5 = cJSON_GetObjectItem(data, "packageMd5"); + cJSON *packageSize = cJSON_GetObjectItem(data, "packageSize"); + if (!(targetVersion && cJSON_IsString(targetVersion) && updateType && (cJSON_IsNumber(updateType) || cJSON_IsString(updateType)))) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task fields invalid"); + cJSON_Delete(root); + free(text); + return; + } + + /* 兼容 updateType 为数字或字符串的两种情况 */ + int ut = -1; + if (cJSON_IsNumber(updateType)) ut = (int)updateType->valuedouble; + else if (cJSON_IsString(updateType)) ut = atoi(updateType->valuestring); + g_ln_last_update_type = ut; + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task parsed: targetVersion=%s updateType=%d url=%s md5=%s", + targetVersion->valuestring, ut, + (packageUrl && cJSON_IsString(packageUrl)) ? packageUrl->valuestring : "", + (packageMd5 && cJSON_IsString(packageMd5)) ? packageMd5->valuestring : ""); + /* 记录必要字段供后续 AT^LN_OTA_DOWNLOAD 使用 */ + if (g_ln_last_target_version) { free(g_ln_last_target_version); g_ln_last_target_version = NULL; } + if (g_ln_last_package_url) { free(g_ln_last_package_url); g_ln_last_package_url = NULL; } + if (g_ln_last_package_md5) { free(g_ln_last_package_md5); g_ln_last_package_md5 = NULL; } + g_ln_last_package_size = 0; + if (targetVersion && cJSON_IsString(targetVersion)) { + size_t len = strlen(targetVersion->valuestring); + g_ln_last_target_version = (char *)malloc(len + 1); + if (g_ln_last_target_version) memcpy(g_ln_last_target_version, targetVersion->valuestring, len + 1); + } + if (packageUrl && cJSON_IsString(packageUrl)) { + size_t len = strlen(packageUrl->valuestring); + g_ln_last_package_url = (char *)malloc(len + 1); + if (g_ln_last_package_url) memcpy(g_ln_last_package_url, packageUrl->valuestring, len + 1); + } + if (packageMd5 && cJSON_IsString(packageMd5)) { + size_t len = strlen(packageMd5->valuestring); + g_ln_last_package_md5 = (char *)malloc(len + 1); + if (g_ln_last_package_md5) memcpy(g_ln_last_package_md5, packageMd5->valuestring, len + 1); + } + if (packageSize && cJSON_IsNumber(packageSize)) { + g_ln_last_package_size = (int)packageSize->valuedouble; + if (g_ln_last_package_size < 0) g_ln_last_package_size = 0; + } + + if (ut == 1) + { + /* 强制升级:直接触发下载 */ + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] force upgrade: trigger download(强制升级:直接触发下载)"); + push_ota_status(OTA_UI_STATUS_FORCE_START); + } + else if (ut == 0) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] optional upgrade: waiting for user confirmation(可选升级:等待用户确认)"); + push_ota_status(OTA_UI_STATUS_OPTIONAL_START); + } + else + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] unknown updateType, skip"); + } + + cJSON_Delete(root); + free(text); +} uint32_t ln_ota_get_task_http(const char *device_id, const char *device_token, @@ -92,7 +315,7 @@ uint32_t ln_ota_get_task_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - do_ln_http_server_recv_data(LN_HTTP_RECV_OTA_TASK, clientData.responseBuf, (uint32_t)real_len); + ln_handle_ota_task_response(clientData.responseBuf, real_len); } goto end; } @@ -100,7 +323,7 @@ uint32_t ln_ota_get_task_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - do_ln_http_server_recv_data(LN_HTTP_RECV_OTA_TASK, clientData.responseBuf, (uint32_t)real_len); + ln_handle_ota_task_response(clientData.responseBuf, real_len); } end: @@ -222,9 +445,11 @@ static int ln_download_file_to_path(const char *full_url, const char *save_path, int n = snprintf(prog, 64, "{\"status\":%d,\"progress\":%d}", OTA_DOWNLOAD_STATUS_PROGRESS, percent); - if (n > 0) - { - do_ln_http_server_recv_data(LN_HTTP_RECV_OTA_DOWNLOAD_MSG, prog, (uint32_t)n); + if (n > 0) { + OtaDownloadMsg om = {0}; + om.status = OTA_DOWNLOAD_STATUS_PROGRESS; + om.progress = percent; + ln_ota_handle_download_msg(&om); } } @@ -338,8 +563,12 @@ uint32_t ln_ota_download_flow_http(const char *device_id, if (msg) { int n = snprintf(msg, 64, "{\"status\":%d,\"error\":\"download failed\"}", OTA_DOWNLOAD_STATUS_FAILED); - if (n > 0) - do_ln_http_server_recv_data(LN_HTTP_RECV_OTA_DOWNLOAD_MSG, msg, (uint32_t)n); + if (n > 0) { + OtaDownloadMsg om = {0}; + om.status = OTA_DOWNLOAD_STATUS_FAILED; + snprintf(om.error_msg, sizeof(om.error_msg), "%s", "download failed"); + ln_ota_handle_download_msg(&om); + } free(msg); } free(save_path); @@ -348,11 +577,12 @@ uint32_t ln_ota_download_flow_http(const char *device_id, size_t done_cap = 64 + strlen(save_path) + (g_ln_last_package_md5 ? strlen(g_ln_last_package_md5) : 0); char *done = (char *)malloc(done_cap); - if (done) - { - int m = snprintf(done, done_cap, "{\"status\":%d,\"filePath\":\"%s\",\"md5\":\"%s\"}", OTA_DOWNLOAD_STATUS_COMPLETED, save_path, g_ln_last_package_md5 ? g_ln_last_package_md5 : ""); - if (m > 0) - do_ln_http_server_recv_data(LN_HTTP_RECV_OTA_DOWNLOAD_MSG, done, (uint32_t)m); + if (done) { + OtaDownloadMsg om = {0}; + om.status = OTA_DOWNLOAD_STATUS_COMPLETED; + snprintf(om.file_path, sizeof(om.file_path), "%s", save_path); + if (g_ln_last_package_md5) snprintf(om.md5, sizeof(om.md5), "%s", g_ln_last_package_md5); + ln_ota_handle_download_msg(&om); free(done); } free(save_path); diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_exit.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_exit.c index d1690fd62..6175f53e7 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_exit.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_exit.c @@ -55,7 +55,6 @@ uint32_t ln_story_exit_inquiry_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - do_ln_http_server_recv_data(LN_HTTP_RECV_STORY_EXIT, clientData.responseBuf, real_len); } goto end; } @@ -63,7 +62,6 @@ uint32_t ln_story_exit_inquiry_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - do_ln_http_server_recv_data(LN_HTTP_RECV_STORY_EXIT, clientData.responseBuf, (uint32_t)real_len); } end: diff --git a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_finish.c b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_finish.c index 5918bff2e..5a8961670 100644 --- a/code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_finish.c +++ b/code/sdk/zhihui/leiniao/imsl_ln/ln_api_story_finish.c @@ -54,7 +54,6 @@ uint32_t ln_story_finish_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - do_ln_http_server_recv_data(LN_HTTP_RECV_STORY_FINISH, clientData.responseBuf, (uint32_t)real_len); } goto end; } @@ -62,7 +61,6 @@ uint32_t ln_story_finish_http(const char *device_id, if (clientData.responseBuf && clientData.responseBufLen > 0) { int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen; - do_ln_http_server_recv_data(LN_HTTP_RECV_STORY_FINISH, clientData.responseBuf, (uint32_t)real_len); } end: -- 2.17.1