#include "netstack/http/include/httpclient.h"
/* for HMAC-SHA256 */
#include "mbedtls/md.h"
+/* for MD5 file verify */
+#include "mbedtls/md5.h"
/* for JSON parsing */
#include "cJSON.h"
#include <thread_init.h>
static char *g_device_model = NULL;
static char *g_device_version = NULL;
+/* 记录最近一次 OTA 查询的升级类型与保存路径 */
+static int g_ln_last_update_type = -1; // -1:未知 0:可选 1:强制
+static char g_ln_last_saved_path[256] = {0}; // 最近一次保存的 OTA 包路径
+static char g_ln_last_package_url[512] = {0}; // 最近一次查询到的 OTA 包 URL
+static char g_ln_last_target_version[64] = {0}; // 最近一次查询到的 OTA 包目标版本
+static char g_ln_last_package_md5[65] = {0}; // 最近一次查询到的 OTA 包 MD5
+
/* 获取设备id: 设备唯一标识 */
char *uapi_ln_get_device_id(void)
{
g_device_model = "abmybox_1";
return g_device_model;
}
-/* 获取设备version */
+/* 获取设备固件版本version */
char *uapi_ln_get_device_version(void)
{
g_device_version = "1.0.0";
g_device_token = NULL;
}
}
+/* 提供给外部查询最近一次 OTA 查询到的升级类型 */
+int uapi_ln_ota_get_update_type(void)
+{
+ return g_ln_last_update_type;
+}
+/* 提供给外部查询最近一次保存的 OTA 包路径 */
+const char *uapi_ln_ota_get_saved_path(void)
+{
+ return (g_ln_last_saved_path[0] != '\0') ? g_ln_last_saved_path : NULL;
+}
/* ============================== 雷鸟设备Token获取 ============================== */
#define LN_DEVICE_GET_USER_ID_URL LN_BASE_URL LN_DEVICE_GET_USER_ID_PATH
#define LN_OTA_GET_TASK_URL LN_BASE_URL LN_OTA_GET_TASK_PATH
#define LN_SIGN_SECRET "vDdXVXgPUEUM6_piUjz5AEhMvYTSFzQdPaN-ogKnSus="
+/* OTA 包保存目录(最终文件仍将放在 /user/ 下)*/
+#define LN_OTA_SAVE_DIR "/user"
+
/* 生成[a-zA-Z0-9]随机串 */
static void ln_gen_nonce(char *out, size_t len)
}
if (clientData.responseBuf && clientData.responseBufLen > 0) {
+ /* 关键打印:返回的响应数据 */
+ printf("[OTA] getTask response: %.*s\n",
+ clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen,
+ clientData.responseBuf);
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);
}
+/* ============================ OTA 包下载实现 Start ============================ */
+/* 将响应缓冲写入文件 */
+static int ln_write_buffer_to_file(const char *save_path, const char *buf, int content_len, int buf_len)
+{
+ if (save_path == NULL || buf == NULL)
+ {
+ return -1;
+ }
+ printf("[OTA] write file: path=%s, content_len=%d, buf_len=%d\n", save_path, content_len, buf_len);
+ int actual_size = content_len > 0 ? content_len : buf_len;
+ if (actual_size <= 0)
+ {
+ printf("[OTA] write file failed: invalid size\n");
+ return -1;
+ }
+ FILE *fp = fopen(save_path, "wb");
+ if (fp == NULL)
+ {
+ printf("[OTA] write file failed: open error\n");
+ return -1;
+ }
+ size_t written = fwrite(buf, 1, (size_t)actual_size, fp);
+ fclose(fp);
+ printf("[OTA] write file done: written=%zu\n", written);
+ return (written == (size_t)actual_size) ? 0 : -1;
+}
+
+/* 计算文件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;
+ }
+ unsigned char buf[4096];
+ size_t n;
+ while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
+ {
+ if (mbedtls_md5_update(&ctx, buf, n) != 0)
+ {
+ fclose(fp);
+ mbedtls_md5_free(&ctx);
+ return -1;
+ }
+ }
+ fclose(fp);
+ 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;
+}
+
+/* 简单下载器:支持 http/https 全URL,保存到 save_path */
+static int ln_download_file_to_path(const char *full_url, const char *save_path)
+{
+ if (!full_url || !save_path)
+ {
+ return -1;
+ }
+ printf("[OTA] start download: url=%s -> %s\n", full_url, save_path);
+
+ /* CAT1 网络保活处理 */
+ cat1_api_add_cat1_vote(); cat1_api_wakeup_cat1(1); cat1_api_keep_wakeup(1); cat1_api_disable_cat1_cmd(0);
+
+ HttpClient client = (HttpClient){0};
+ HttpClientData clientData = (HttpClientData){0};
+
+ /* 判断协议 */
+ bool is_https = (strstr(full_url, "https://") == full_url);
+ client.isHttp = is_https ? false : true;
+ client.remotePort = is_https ? 443 : 80;
+
+ HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 256 * 1024);
+ if (prep != HTTP_SUCCESS)
+ {
+ return -1;
+ }
+
+ HTTPC_RESULT res = HttpClientGetRequest(&client, full_url, &clientData);
+ if (res != HTTP_SUCCESS)
+ {
+ printf("[OTA] download request failed: %d\n", res);
+ HttpClientUnprepare(&clientData);
+ HttpClientClse(&client);
+ return -1;
+ }
+
+ int code = HttpClientGetResponseCode(&client);
+ if (code != 200)
+ {
+ printf("[OTA] download HTTP code=%d\n", code);
+ HttpClientUnprepare(&clientData);
+ HttpClientClse(&client);
+ return -1;
+ }
+
+ if (clientData.responseBuf == NULL || clientData.responseBufLen <= 0)
+ {
+ printf("[OTA] download empty body\n");
+ HttpClientUnprepare(&clientData);
+ HttpClientClse(&client);
+ return -1;
+ }
+
+ int content_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
+ int ret = ln_write_buffer_to_file(save_path, clientData.responseBuf, content_len, clientData.responseBufLen);
+ if (ret == 0) {
+ printf("[OTA] download saved: %s (%d bytes)\n", save_path, content_len);
+ } else {
+ printf("[OTA] download save failed\n");
+ }
+
+ HttpClientUnprepare(&clientData);
+ HttpClientClse(&client);
+ return ret;
+}
+
+/* 仅下载:根据最近一次 getTask 的记录字段执行下载,不再主动请求任务 */
+static uint32_t ln_ota_download_flow_http(const char *device_id,
+ const char *device_token,
+ const char *source_version)
+{
+ (void)device_id;
+ (void)device_token;
+ (void)source_version;
+ if (g_ln_last_target_version[0] == '\0' || g_ln_last_package_url[0] == '\0')
+ {
+ printf("[OTA] no cached task to download, please GET_TASK first\n");
+ return ERRCODE_FAIL;
+ }
+ /* 保存路径:LN_OTA_SAVE_DIR/upgrade_<ver>.bin */
+ char save_path[256];
+ snprintf(save_path, sizeof(save_path), LN_OTA_SAVE_DIR "/upgrade_%s.bin", g_ln_last_target_version);
+ memset(g_ln_last_saved_path, 0, sizeof(g_ln_last_saved_path));
+ strncpy(g_ln_last_saved_path, save_path, sizeof(g_ln_last_saved_path) - 1);
+
+ printf("[OTA] download only: version=%s, url=%s -> %s\n", g_ln_last_target_version, g_ln_last_package_url, save_path);
+ int dres = ln_download_file_to_path(g_ln_last_package_url, save_path);
+ if (dres != 0)
+ {
+ char msg[256];
+ int n = snprintf(msg, sizeof(msg), "{\"status\":%d,\"error\":\"download failed\"}", 2);
+ if (n > 0) do_ln_http_server_recv_data(LN_HTTP_RECV_OTA_DOWNLOAD_MSG, msg, (uint32_t)n);
+ return ERRCODE_FAIL;
+ }
+
+ /* 发送完成消息(不在此处做MD5,保持AT^LN_OTA_DOWNLOAD单一职责:下载)*/
+ char done[512];
+ int m = snprintf(done, sizeof(done), "{\"status\":%d,\"filePath\":\"%s\",\"md5\":\"%s\"}", 1, save_path, g_ln_last_package_md5);
+ if (m > 0) do_ln_http_server_recv_data(LN_HTTP_RECV_OTA_DOWNLOAD_MSG, done, (uint32_t)m);
+ return ERRCODE_SUCC;
+}
+
+
+/* 在接收线程内使用:解析 OTA 任务 JSON,决定是否触发下载 */
+static void ln_handle_ota_task_response(const char *json_buf, int json_len)
+{
+ if (!json_buf || json_len <= 0)
+ {
+ printf("[OTA] handle ota task: invalid json\n");
+ 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)
+ {
+ printf("[OTA] handle ota task: json parse failed\n");
+ free(text);
+ return;
+ }
+
+ cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode");
+ if (errorCode && cJSON_IsString(errorCode))
+ {
+ /* 无升级或服务端错误 */
+ printf("[OTA] task errorCode=%s\n", errorCode->valuestring);
+ cJSON_Delete(root);
+ free(text);
+ return;
+ }
+
+ cJSON *data = cJSON_GetObjectItem(root, "data");
+ if (!data || !cJSON_IsObject(data))
+ {
+ printf("[OTA] task data missing\n");
+ 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");
+ if (!(targetVersion && cJSON_IsString(targetVersion) && updateType && (cJSON_IsNumber(updateType) || cJSON_IsString(updateType))))
+ {
+ printf("[OTA] task fields invalid\n");
+ 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;
+
+ printf("[OTA] task parsed: targetVersion=%s updateType=%d url=%s md5=%s\n",
+ targetVersion->valuestring, ut,
+ (packageUrl && cJSON_IsString(packageUrl)) ? packageUrl->valuestring : "<null>",
+ (packageMd5 && cJSON_IsString(packageMd5)) ? packageMd5->valuestring : "<null>");
+ /* 记录必要字段供后续 AT^LN_OTA_DOWNLOAD 使用 */
+ memset(g_ln_last_target_version, 0, sizeof(g_ln_last_target_version)); // 目标版本
+ memset(g_ln_last_package_url, 0, sizeof(g_ln_last_package_url)); // 升级包URL
+ memset(g_ln_last_package_md5, 0, sizeof(g_ln_last_package_md5)); // 升级包MD5
+ if (targetVersion && cJSON_IsString(targetVersion)) {
+ strncpy(g_ln_last_target_version, targetVersion->valuestring, sizeof(g_ln_last_target_version) - 1);
+ }
+ if (packageUrl && cJSON_IsString(packageUrl)) {
+ strncpy(g_ln_last_package_url, packageUrl->valuestring, sizeof(g_ln_last_package_url) - 1);
+ }
+ if (packageMd5 && cJSON_IsString(packageMd5)) {
+ strncpy(g_ln_last_package_md5, packageMd5->valuestring, sizeof(g_ln_last_package_md5) - 1);
+ }
+
+ if (ut == 1)
+ {
+ /* 强制升级:直接触发下载 */
+ printf("[OTA] force upgrade: trigger download(强制升级:直接触发下载)\n");
+ do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD, NULL, 0);
+ }
+ else if (ut == 0)
+ {
+ /* 可选升级:此处仅提示,实际是否下载由上层UI决定(例如用户确认后再触发)*/
+ printf("[OTA] optional upgrade: waiting for user confirmation(可选升级:等待用户确认)\n");
+ /* 如需自动下载,可取消下面注释 */
+ // do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD, NULL, 0);
+ }
+ else
+ {
+ printf("[OTA] unknown updateType, skip\n");
+ }
+
+ cJSON_Delete(root);
+ free(text);
+}
+/* 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) */
+ char file_path[256]; /* 文件路径 */
+ char error_msg[128]; /* 错误信息 */
+ char md5[65]; /* MD5(来自任务)*/
+} OtaDownloadMsg;
+
+/* 解析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')
+ {
+ printf("[OTA] md5 verify: invalid args\n");
+ return -1;
+ }
+ char calc[33] = {0};
+ if (ln_calc_file_md5_lower_hex(file_path, calc) != 0)
+ {
+ printf("[OTA] md5 verify: calc failed for %s\n", 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;
+ printf("[OTA] md5 verify result=%s, calc=%s, expect=%s\n", ok ? "OK" : "FAIL", calc, expect_lower);
+ return ok;
+}
+
+/* OTA下载消息处理(对接时序图中的后续流程)*/
+static void handle_ota_download_msg(const OtaDownloadMsg *msg)
+{
+ if (!msg) return;
+ switch (msg->status) {
+ case OTA_DOWNLOAD_STATUS_PROGRESS:
+ printf("[OTA_DOWNLOAD] 下载进度: %d%%\n", msg->progress);
+ break;
+ case OTA_DOWNLOAD_STATUS_COMPLETED:
+ printf("[OTA_DOWNLOAD] 下载完成,文件路径: %s\n", msg->file_path);
+ if (msg->file_path[0] && msg->md5[0]) {
+ printf("[OTA_DOWNLOAD] 开始MD5校验: %s\n", msg->md5);
+ int md5_ok = ln_ota_is_md5_ok(msg->file_path, msg->md5);
+ if (md5_ok == 1) {
+ printf("[OTA_DOWNLOAD] MD5校验成功,准备重启升级\n");
+ /* 外部可在此触发升级动作 */
+ } else if (md5_ok == 0) {
+ printf("[OTA_DOWNLOAD] MD5校验失败,建议重新下载\n");
+ } else {
+ printf("[OTA_DOWNLOAD] MD5校验出错\n");
+ }
+ }
+ break;
+ case OTA_DOWNLOAD_STATUS_FAILED:
+ printf("[OTA_DOWNLOAD] 下载失败: %s\n", msg->error_msg);
+ break;
+ default:
+ printf("[OTA_DOWNLOAD] 未知状态: %d\n", msg->status);
+ break;
+ }
+}
+
+/* ============================ OTA 包下载实现 End ============================ */
+
// 发送队列
static void ln_http_server_send_task(void const *arg)
{
{
if (mail.data != NULL && mail.len == sizeof(ln_device_token_req_t))
{
- printf("LN_HTTP_SERVICE_CMD_GET_DEVICE_TOKEN\n");
+ printf("LN_HTTP_SERVICE_CMD_GET_DEVICE_TOKEN:获取雷鸟设备Token\n");
ln_device_token_req_t req;
memcpy(&req, mail.data, sizeof(req));
ln_get_device_token_http(req.device_id, req.device_iccid, req.device_model, req.device_version);
else if (mail.cmd == LN_HTTP_SERVICE_CMD_STORY_CHOOSE)
{
if (mail.data && mail.len == sizeof(ln_story_choose_req_t)) {
- printf("LN_HTTP_SERVICE_CMD_STORY_CHOOSE\n");
+ printf("LN_HTTP_SERVICE_CMD_STORY_CHOOSE:选择故事\n");
ln_story_choose_req_t req; memcpy(&req, mail.data, sizeof(req));
ln_story_choose_http(uapi_ln_get_device_id(), uapi_ln_get_device_token(), req.audio_path);
}
else if (mail.cmd == LN_HTTP_SERVICE_CMD_STORY_RANDOM)
{
if (mail.data && mail.len == sizeof(ln_story_random_req_t)) {
- printf("LN_HTTP_SERVICE_CMD_STORY_RANDOM\n");
+ printf("LN_HTTP_SERVICE_CMD_STORY_RANDOM:随机故事\n");
ln_story_random_req_t req; memcpy(&req, mail.data, sizeof(req));
ln_story_random_http(uapi_ln_get_device_id(), uapi_ln_get_device_token(), req.story_theme[0] ? req.story_theme : NULL);
}
}
else if (mail.cmd == LN_HTTP_SERVICE_CMD_STORY_EXIT_INQUIRY)
{
- printf("LN_HTTP_SERVICE_CMD_STORY_EXIT_INQUIRY\n");
+ printf("LN_HTTP_SERVICE_CMD_STORY_EXIT_INQUIRY:获取退出哄睡询问\n");
ln_story_exit_inquiry_http(uapi_ln_get_device_id(), uapi_ln_get_device_token());
}
else if (mail.cmd == LN_HTTP_SERVICE_CMD_STORY_FINISH)
{
- printf("LN_HTTP_SERVICE_CMD_STORY_FINISH\n");
+ printf("LN_HTTP_SERVICE_CMD_STORY_FINISH:确认结束哄睡\n");
ln_story_finish_http(uapi_ln_get_device_id(), uapi_ln_get_device_token());
}
else if (mail.cmd == LN_HTTP_SERVICE_CMD_GET_CHARACTER)
{
- printf("LN_HTTP_SERVICE_CMD_GET_CHARACTER\n");
+ printf("LN_HTTP_SERVICE_CMD_GET_CHARACTER:获取设备角色信息\n");
ln_device_get_character_http(uapi_ln_get_device_id(), uapi_ln_get_device_token());
}
else if (mail.cmd == LN_HTTP_SERVICE_CMD_GET_USER_ID)
{
- printf("LN_HTTP_SERVICE_CMD_GET_USER_ID\n");
+ printf("LN_HTTP_SERVICE_CMD_GET_USER_ID:获取设备绑定用户ID\n");
ln_device_get_user_id_http(uapi_ln_get_device_id(), uapi_ln_get_device_token());
}
else if (mail.cmd == LN_HTTP_SERVICE_CMD_OTA_GET_TASK)
{
- printf("LN_HTTP_SERVICE_CMD_OTA_GET_TASK\n");
+ printf("LN_HTTP_SERVICE_CMD_OTA_GET_TASK:获取OTA升级任务\n");
ln_ota_get_task_http(uapi_ln_get_device_id(), uapi_ln_get_device_token(), uapi_ln_get_device_version());
}
+ else if (mail.cmd == LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD)
+ {
+ 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());
+ }
if (mail.data != NULL)
{
printf("free(mail.data)\n");
OSAL_MSGQ_WAIT_FOREVER);
if (ret != OSAL_SUCCESS)
{
+ printf("[RECV_TASK] 消息读取失败: %d\n", ret);
continue;
}
- if (mail.cmd == LN_HTTP_SERVICE_CMD_SSE_RECV
- || mail.cmd == LN_HTTP_RECV_STORY_CHOOSE
- || mail.cmd == LN_HTTP_RECV_STORY_RANDOM
- || mail.cmd == LN_HTTP_RECV_STORY_EXIT
- || mail.cmd == LN_HTTP_RECV_STORY_FINISH
- || mail.cmd == LN_HTTP_RECV_DEVICE_CHARACTER
- || mail.cmd == LN_HTTP_RECV_DEVICE_USER_ID
- || mail.cmd == LN_HTTP_RECV_OTA_TASK)
- {
- if (mail.data && mail.len)
- {
- /* 分类打印,便于区分不同来源 */
- const char *tag = (mail.cmd == LN_HTTP_SERVICE_CMD_SSE_RECV) ? "SPEECH_CHAT" :
- (mail.cmd == LN_HTTP_RECV_STORY_CHOOSE) ? "STORY_CHOOSE" :
- (mail.cmd == LN_HTTP_RECV_STORY_RANDOM) ? "STORY_RANDOM" :
- (mail.cmd == LN_HTTP_RECV_STORY_EXIT) ? "STORY_EXIT" :
- (mail.cmd == LN_HTTP_RECV_STORY_FINISH) ? "STORY_FINISH" :
- (mail.cmd == LN_HTTP_RECV_DEVICE_CHARACTER) ? "DEVICE_CHARACTER" :
- (mail.cmd == LN_HTTP_RECV_DEVICE_USER_ID) ? "DEVICE_USER_ID" :
- (mail.cmd == LN_HTTP_RECV_OTA_TASK) ? "OTA_TASK" : "UNKNOWN";
- printf("[%s] SSE raw len=%u\n", tag, mail.len);
- /* 确保可打印:追加\0 */
- char *buf = (char *)malloc(mail.len + 1);
- if (buf) {
- memcpy(buf, mail.data, mail.len);
- buf[mail.len] = '\0';
- printf("[%s] SSE raw data=%s\n", tag, buf);
- free(buf);
+
+ // 消息有效性检查
+ if (!mail.data || mail.len <= 0)
+ {
+ printf("[RECV_TASK] 无效消息: data=%p, len=%u\n", mail.data, mail.len);
+ continue;
+ }
+
+ // 分类处理消息
+ const char *tag = "UNKNOWN";
+ if (mail.cmd == LN_HTTP_SERVICE_CMD_SSE_RECV) {
+ tag = "SPEECH_CHAT";
+ } else if (mail.cmd == LN_HTTP_RECV_STORY_CHOOSE) {
+ tag = "STORY_CHOOSE";
+ } else if (mail.cmd == LN_HTTP_RECV_STORY_RANDOM) {
+ tag = "STORY_RANDOM";
+ } 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";
+ } 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";
+ } else if (mail.cmd == LN_HTTP_RECV_OTA_DOWNLOAD_MSG) {
+ tag = "OTA_DOWNLOAD_MSG";
+ }
+
+ printf("[%s] 数据长度: %u\n", tag, mail.len);
+ /* 确保可打印:追加\0 */
+ char *buf = (char *)malloc(mail.len + 1);
+ if (buf) {
+ memcpy(buf, mail.data, mail.len);
+ buf[mail.len] = '\0';
+ printf("[%s] 原始数据:%s\n", tag, buf);
+ /* 在收到 OTA_TASK 后,根据升级策略决定是否触发下载 */
+ if (mail.cmd == LN_HTTP_RECV_OTA_TASK) {
+ ln_handle_ota_task_response(buf, (int)mail.len);
+ }
+ /* 在收到 OTA_DOWNLOAD_MSG 后,解析状态并回调处理(进度/完成/失败) */
+ if (mail.cmd == LN_HTTP_RECV_OTA_DOWNLOAD_MSG) {
+ OtaDownloadMsg m = {0};
+ if (parse_ota_download_msg(buf, &m) == 0) {
+ handle_ota_download_msg(&m);
}
}
+ free(buf);
}
}
}