feat(main): 添加启动模块自检与汇总功能
- 新增 SystemBoot 类用于跟踪各模块初始化状态,提供位图标记与报告功能 - 在 main.cpp 中重构初始化流程,按顺序初始化各模块并记录状态 - 添加 system_boot.cpp 模块文件到构建配置 - CAN反馈任务增加冷启动延迟,避免与GD32初始化窗口冲突 refactor(ota): 优化OTA协调器线程安全机制 - 修改 lock()/unlock() 方法返回take结果,确保mutex正确释放 - 修复多任务并发时mutex死锁问题,避免vTaskPriorityDisinherit断言 - 统一finalize失败后的状态复位逻辑,确保协调器回到IDLE状态 - 优化忙状态超时检测与自愈机制 perf(boot): 优化冷启动时序与资源管理 - 简化README中冷启动优化说明,聚焦核心逻辑 - 优化BOD阈值设置为LVL5,改进电源稳定性 - 增加BLE OTA工作线程优先级,减少丢包情况 - 优化堆告警节流机制,在OTA期间降低告警频率 fix(relay): 改进GD32应用存活检测逻辑 - 调整GD32 APP存活验证时序,增加会话建立验证 - 修复HELLO建立session后未验证role的问题 - 优化FINISH后等待APP启动的静默时间与重试机制 - 统一GD32探测逻辑到OTA中继任务 refactor(server): 优化HTTP服务端内存与性能 - 使用静态缓冲区替代动态分配,减少堆碎片化风险 - 优化CAN批量发送期间暂停反馈任务的机制 - 调整BLE连接参数请求逻辑,提升传输性能 - 改进错误处理流程,避免堆紧张时的额外分配 bump(version): 更新固件版本至0.3.50 - 更新版本号从0.3.49到0.3.50 - 更新编译时间戳与Git哈希值
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
#include "monitor/can_feedback_task.h"
|
||||
#include "monitor/task_stack_check.h"
|
||||
#include "ble_ota.h"
|
||||
#include "system_boot.h"
|
||||
|
||||
static size_t parseHexToBytes(const char *hex, uint8_t *out, size_t max_out)
|
||||
{
|
||||
@@ -641,7 +642,7 @@ esp_err_t can_feedback_handler(httpd_req_t *req)
|
||||
valid[i] = getLatestCanFeedback(i, &results[i]);
|
||||
}
|
||||
|
||||
/* ★ 修复:改用静态缓冲区,避免每次请求 malloc 8KB 加剧堆碎片化 */
|
||||
/* 静态响应缓冲,减少频繁大块堆分配 */
|
||||
static char s_feedback_json[8192];
|
||||
char *resp = s_feedback_json;
|
||||
memset(resp, 0, sizeof(s_feedback_json));
|
||||
@@ -837,17 +838,15 @@ esp_err_t can_send_handler(httpd_req_t *req)
|
||||
* @brief POST /can/send_batch — 同通道连续发送多帧,单次 HTTP 完成。
|
||||
*
|
||||
* JSON: { "ch": 0, "frames": [ { "id": n, "dlc": n, "data": "HEX" }, ... ] }
|
||||
* 返回: { "status":"ok", "results":[ { "status":"ok"|"error", ... }, ... ] }
|
||||
* 帧间 5ms;暂停 CAN 反馈,避免与批量 SPI 竞态导致偶发崩溃。
|
||||
* 返回: { "status":"ok", "results":[ ... ], 可选 skipped/abort_* }
|
||||
* 帧间间隔 kInterFrameDelayMs;批量期间暂停 CAN 反馈任务。
|
||||
*/
|
||||
esp_err_t can_send_batch_handler(httpd_req_t *req)
|
||||
{
|
||||
http_set_cors_headers(req);
|
||||
|
||||
/* 单次尽量装下整批,避免前端分块导致“一阵一阵” */
|
||||
constexpr int kMaxBody = 49152;
|
||||
constexpr int kMaxFrames = 128;
|
||||
/* 成功路径通常 <20ms;失败路径不再拖到数百 ms */
|
||||
constexpr uint32_t kPerFrameTimeoutMs = 80U;
|
||||
constexpr uint32_t kInterFrameDelayMs = 5U;
|
||||
|
||||
@@ -906,7 +905,7 @@ esp_err_t can_send_batch_handler(httpd_req_t *req)
|
||||
cJSON_AddStringToObject(out_root, "status", "ok");
|
||||
cJSON_AddItemToObject(out_root, "results", results);
|
||||
|
||||
/* 暂停 0xC1 反馈抢 SPI,降低 worker 竞态 / 长时间占线导致的 WDT 风险 */
|
||||
/* 批量发送期间暂停 CAN 反馈,独占 SPI 业务路径 */
|
||||
pauseCanFeedbackTask(true);
|
||||
|
||||
auto fill_err = [](cJSON *item, int code, uint8_t gd32_err) {
|
||||
@@ -918,11 +917,6 @@ esp_err_t can_send_batch_handler(httpd_req_t *req)
|
||||
cJSON_AddNumberToObject(item, "gd32_err", gd32_err);
|
||||
};
|
||||
|
||||
/* 中途致命错误后不再逐帧 SPI 重试(否则每帧再等一整段超时) */
|
||||
bool abort_rest = false;
|
||||
int abort_code = -2;
|
||||
uint8_t abort_gd32 = 0U;
|
||||
|
||||
for (int i = 0; i < frame_count; i++) {
|
||||
(void)esp_task_wdt_reset();
|
||||
|
||||
@@ -932,11 +926,6 @@ esp_err_t can_send_batch_handler(httpd_req_t *req)
|
||||
}
|
||||
cJSON_AddItemToArray(results, item);
|
||||
|
||||
if (abort_rest) {
|
||||
fill_err(item, abort_code, abort_gd32);
|
||||
continue; /* 无 SPI、无帧间延时 */
|
||||
}
|
||||
|
||||
cJSON *fr = cJSON_GetArrayItem(frames, i);
|
||||
if (!cJSON_IsObject(fr)) {
|
||||
fill_err(item, -1, 0);
|
||||
@@ -987,25 +976,22 @@ esp_err_t can_send_batch_handler(httpd_req_t *req)
|
||||
cJSON_AddStringToObject(item, "recv", resp_hex);
|
||||
} else {
|
||||
fill_err(item, resp_len, gd32_err);
|
||||
/* SPI 失败 / BUS_OFF / TEC 过高:后面不再逐帧 SPI,也不再为每帧分配 cJSON */
|
||||
/* SPI 失败 / BUS_OFF / TEC 过高:中止剩余帧,由 skipped 字段告知前端 */
|
||||
const bool fatal_spi = (resp_len == -2);
|
||||
const bool fatal_bus = (resp_len == -3) &&
|
||||
(gd32_err == 0xE2U || gd32_err == 0xE3U);
|
||||
if (fatal_spi || fatal_bus) {
|
||||
abort_rest = true;
|
||||
abort_code = resp_len;
|
||||
abort_gd32 = gd32_err;
|
||||
const int skipped = frame_count - i - 1;
|
||||
if (skipped > 0) {
|
||||
cJSON_AddNumberToObject(out_root, "skipped", skipped);
|
||||
cJSON_AddNumberToObject(out_root, "abort_code", abort_code);
|
||||
cJSON_AddNumberToObject(out_root, "abort_gd32_err", abort_gd32);
|
||||
cJSON_AddNumberToObject(out_root, "abort_code", resp_len);
|
||||
cJSON_AddNumberToObject(out_root, "abort_gd32_err", gd32_err);
|
||||
}
|
||||
break; /* 不再为剩余帧 CreateObject,避免堆被打穿 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i + 1 < frame_count && !abort_rest) {
|
||||
if (i + 1 < frame_count) {
|
||||
vTaskDelay(pdMS_TO_TICKS(kInterFrameDelayMs));
|
||||
}
|
||||
}
|
||||
@@ -1018,9 +1004,8 @@ esp_err_t can_send_batch_handler(httpd_req_t *req)
|
||||
cJSON_Delete(out_root);
|
||||
out_root = nullptr;
|
||||
if (!printed) {
|
||||
/* 堆已紧时勿再走大段错误路径分配;给前端最小 JSON */
|
||||
static const char kOom[] =
|
||||
"{\"status\":\"error\",\"code\":\"json_print\",\"message\":\"JSON输出失败(堆不足)\"}";
|
||||
"{\"status\":\"error\",\"code\":\"json_print\",\"message\":\"JSON输出失败\"}";
|
||||
httpd_resp_set_status(req, "500 Internal Server Error");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_send(req, kOom, HTTPD_RESP_USE_STRLEN);
|
||||
@@ -1104,7 +1089,8 @@ esp_err_t spi_send_handler(httpd_req_t *req)
|
||||
|
||||
static constexpr uint32_t INTERNAL_HEAP_CAPS = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
|
||||
/* 状态 JSON 用静态缓冲:OTA 后 largest free 常 <2KB,calloc(8KB) 会 500 "no memory"。 */
|
||||
static constexpr size_t SYSTEM_STATUS_JSON_CAP = 6144;
|
||||
/* 含 boot 模块列表与 CPU 任务;OTA 后 largest free 常偏紧,仍用静态缓冲避免 calloc */
|
||||
static constexpr size_t SYSTEM_STATUS_JSON_CAP = 7168;
|
||||
static char s_status_json[SYSTEM_STATUS_JSON_CAP];
|
||||
static SemaphoreHandle_t s_status_json_mux = nullptr;
|
||||
static portMUX_TYPE s_status_json_init_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
@@ -1344,6 +1330,9 @@ esp_err_t system_status_handler(httpd_req_t *req)
|
||||
(unsigned long)ls.stats.max_resp_latency_us);
|
||||
}
|
||||
|
||||
/* 启动自检位图(与串口 BOOT report 同源) */
|
||||
len = SystemBoot::appendStatusJson(resp, SYSTEM_STATUS_JSON_CAP, len);
|
||||
|
||||
// 前导逗号与前一块分隔;之前因 append_cpu_status_json 尾随逗号叠加
|
||||
// 形成 "],," 导致前端 JSON.parse 报 "Expected double-quoted property name"
|
||||
len = append_jsonf(resp, SYSTEM_STATUS_JSON_CAP, len, ",\"task_stack\": {");
|
||||
|
||||
Reference in New Issue
Block a user