- 启动自检日志:远程日志启动后记录 endpoint、目录、待上传文件数、队列配置、coredump 状态。
- 上传状态统计:记录成功/失败次数、最后 HTTP 状态、最后错误码、最后上传时间、最后成功时间。
- 独立日志 Flash 分区:新增 logfs LittleFS 分区 512KB,日志不再占用音频资源分区;远程日志文件上限为 24 * 16KB =
384KB。
- 本地诊断 factory CLI:
- log status
- log flush
- log clear
- Coredump 支持:启用 flash coredump,新增 64KB coredump 分区;当前会上报 coredump 存在状态、地址、大小、panic
reason 和设备状态快照。
- 上传附带设备状态快照:reset reason、固件版本、project name、heap、任务数、多模态连接/活动状态、coredump 状态。
This commit is contained in:
@@ -3,7 +3,9 @@
|
|||||||
nvs, data, nvs, 0x9000, 0x4000,
|
nvs, data, nvs, 0x9000, 0x4000,
|
||||||
otadata, data, ota, 0xd000, 0x2000,
|
otadata, data, ota, 0xd000, 0x2000,
|
||||||
phy_init, data, phy, 0xf000, 0x1000,
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
gifpack, data, littlefs, , 0x800000,
|
gifpack, data, littlefs, , 0x780000,
|
||||||
|
logfs, data, littlefs, , 0x80000,
|
||||||
ota_0, app, ota_0, , 0x330000,
|
ota_0, app, ota_0, , 0x330000,
|
||||||
ota_1, app, ota_1, , 0x330000,
|
ota_1, app, ota_1, , 0x330000,
|
||||||
model, data, spiffs, , 0x180000,
|
model, data, spiffs, , 0x180000,
|
||||||
|
coredump, data, coredump,, 0x10000,
|
||||||
|
|||||||
|
@@ -22,6 +22,8 @@ idf_component_register(
|
|||||||
"internal"
|
"internal"
|
||||||
PRIV_REQUIRES
|
PRIV_REQUIRES
|
||||||
domain
|
domain
|
||||||
|
espcoredump
|
||||||
|
esp_app_format
|
||||||
esp_driver_gpio
|
esp_driver_gpio
|
||||||
esp_timer
|
esp_timer
|
||||||
esp_system
|
esp_system
|
||||||
|
|||||||
@@ -145,3 +145,6 @@ void control_plane_set_ptt_active(bool active);
|
|||||||
|
|
||||||
esp_err_t control_plane_remote_log_start(void);
|
esp_err_t control_plane_remote_log_start(void);
|
||||||
void control_plane_remote_log_stop(int timeout_ms);
|
void control_plane_remote_log_stop(int timeout_ms);
|
||||||
|
esp_err_t control_plane_remote_log_flush_now(void);
|
||||||
|
esp_err_t control_plane_remote_log_clear(void);
|
||||||
|
esp_err_t control_plane_remote_log_format_status(char *out, size_t out_len);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "control_plane.h"
|
#include "control_plane.h"
|
||||||
|
#include "control_plane_internal.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -357,6 +358,41 @@ static void handle_prepare_rotate(uint32_t key_version)
|
|||||||
cli_write_ok();
|
cli_write_ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_log_command(size_t argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (argc < 2 || is_text_empty(argv[1])) {
|
||||||
|
cli_write_error("missing_log_command");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(argv[1], "status") == 0) {
|
||||||
|
char json[FACTORY_CLI_JSON_MAX_LEN] = {0};
|
||||||
|
esp_err_t err = control_plane_remote_log_format_status(json, sizeof(json));
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
cli_write_error("log_status_failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cli_write_line(json);
|
||||||
|
cli_write_ok();
|
||||||
|
} else if (strcmp(argv[1], "flush") == 0) {
|
||||||
|
esp_err_t err = control_plane_remote_log_flush_now();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
cli_write_error("log_flush_failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cli_write_ok();
|
||||||
|
} else if (strcmp(argv[1], "clear") == 0) {
|
||||||
|
esp_err_t err = control_plane_remote_log_clear();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
cli_write_error("log_clear_failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cli_write_ok();
|
||||||
|
} else {
|
||||||
|
cli_write_error("unknown_log_command");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_command_line(char *line)
|
static void handle_command_line(char *line)
|
||||||
{
|
{
|
||||||
trim_text(line);
|
trim_text(line);
|
||||||
@@ -429,6 +465,8 @@ static void handle_command_line(char *line)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handle_prepare_rotate(key_version);
|
handle_prepare_rotate(key_version);
|
||||||
|
} else if (strcmp(cmd, "log") == 0) {
|
||||||
|
handle_log_command(argc, argv);
|
||||||
} else {
|
} else {
|
||||||
cli_write_error("unknown_command");
|
cli_write_error("unknown_command");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,11 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "domain.h"
|
#include "domain.h"
|
||||||
|
#include "esp_app_desc.h"
|
||||||
|
#include "esp_core_dump.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_system.h"
|
||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
@@ -76,7 +80,7 @@
|
|||||||
|
|
||||||
#define REMOTE_LOG_TASK_STACK_SIZE (1024 * 8)
|
#define REMOTE_LOG_TASK_STACK_SIZE (1024 * 8)
|
||||||
#define REMOTE_LOG_TASK_PRIORITY 3
|
#define REMOTE_LOG_TASK_PRIORITY 3
|
||||||
#define REMOTE_LOG_DIR "/littlefs/remote_logs"
|
#define REMOTE_LOG_DIR "/logfs/remote_logs"
|
||||||
#define REMOTE_LOG_FILE_PREFIX "log_"
|
#define REMOTE_LOG_FILE_PREFIX "log_"
|
||||||
#define REMOTE_LOG_FILE_SUFFIX ".jsonl"
|
#define REMOTE_LOG_FILE_SUFFIX ".jsonl"
|
||||||
#define REMOTE_LOG_PATH_MAX 96
|
#define REMOTE_LOG_PATH_MAX 96
|
||||||
@@ -104,6 +108,16 @@ static volatile bool s_stop_requested;
|
|||||||
static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED;
|
static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
static vprintf_like_t s_original_vprintf;
|
static vprintf_like_t s_original_vprintf;
|
||||||
static bool s_hook_installed;
|
static bool s_hook_installed;
|
||||||
|
static uint32_t s_successful_uploads;
|
||||||
|
static uint32_t s_failed_uploads;
|
||||||
|
static int s_last_http_status = -1;
|
||||||
|
static esp_err_t s_last_upload_err = ESP_ERR_NOT_FOUND;
|
||||||
|
static int64_t s_last_upload_ms;
|
||||||
|
static int64_t s_last_success_ms;
|
||||||
|
static bool s_coredump_present;
|
||||||
|
static bool s_coredump_reported;
|
||||||
|
static size_t s_coredump_addr;
|
||||||
|
static size_t s_coredump_size;
|
||||||
|
|
||||||
static bool remote_log_path_active(void)
|
static bool remote_log_path_active(void)
|
||||||
{
|
{
|
||||||
@@ -230,6 +244,15 @@ static bool dequeue_entry(remote_log_entry_t *out_entry)
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t snapshot_queue_count(void)
|
||||||
|
{
|
||||||
|
size_t count = 0U;
|
||||||
|
portENTER_CRITICAL(&s_lock);
|
||||||
|
count = s_queue_count;
|
||||||
|
portEXIT_CRITICAL(&s_lock);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
static uint32_t snapshot_and_clear_dropped_count(void)
|
static uint32_t snapshot_and_clear_dropped_count(void)
|
||||||
{
|
{
|
||||||
uint32_t dropped = 0;
|
uint32_t dropped = 0;
|
||||||
@@ -451,6 +474,12 @@ static size_t collect_files(uint32_t *out_sequences, size_t capacity)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t count_pending_files(void)
|
||||||
|
{
|
||||||
|
uint32_t sequences[CONFIG_TQ_REMOTE_LOG_MAX_FILES + 8] = {0};
|
||||||
|
return collect_files(sequences, sizeof(sequences) / sizeof(sequences[0]));
|
||||||
|
}
|
||||||
|
|
||||||
static void trim_old_files(void)
|
static void trim_old_files(void)
|
||||||
{
|
{
|
||||||
uint32_t sequences[CONFIG_TQ_REMOTE_LOG_MAX_FILES + 8] = {0};
|
uint32_t sequences[CONFIG_TQ_REMOTE_LOG_MAX_FILES + 8] = {0};
|
||||||
@@ -541,6 +570,102 @@ static bool realtime_path_busy(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *reset_reason_name(esp_reset_reason_t reason)
|
||||||
|
{
|
||||||
|
switch (reason) {
|
||||||
|
case ESP_RST_POWERON:
|
||||||
|
return "poweron";
|
||||||
|
case ESP_RST_EXT:
|
||||||
|
return "external";
|
||||||
|
case ESP_RST_SW:
|
||||||
|
return "software";
|
||||||
|
case ESP_RST_PANIC:
|
||||||
|
return "panic";
|
||||||
|
case ESP_RST_INT_WDT:
|
||||||
|
return "interrupt_wdt";
|
||||||
|
case ESP_RST_TASK_WDT:
|
||||||
|
return "task_wdt";
|
||||||
|
case ESP_RST_WDT:
|
||||||
|
return "other_wdt";
|
||||||
|
case ESP_RST_DEEPSLEEP:
|
||||||
|
return "deepsleep";
|
||||||
|
case ESP_RST_BROWNOUT:
|
||||||
|
return "brownout";
|
||||||
|
case ESP_RST_SDIO:
|
||||||
|
return "sdio";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t append_device_state(char *body, size_t body_len, size_t *offset)
|
||||||
|
{
|
||||||
|
if (body == NULL || offset == NULL || *offset >= body_len) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
const esp_app_desc_t *app = esp_app_get_description();
|
||||||
|
esp_reset_reason_t reset_reason = esp_reset_reason();
|
||||||
|
control_plane_multimodal_runtime_state_t mm = {0};
|
||||||
|
bool have_mm = (control_plane_multimodal_get_runtime_state(&mm) == ESP_OK);
|
||||||
|
|
||||||
|
int written = snprintf(body + *offset,
|
||||||
|
body_len - *offset,
|
||||||
|
",\"device_state\":{"
|
||||||
|
"\"reset_reason\":\"%s\","
|
||||||
|
"\"reset_reason_code\":%d,"
|
||||||
|
"\"firmware_version\":\"",
|
||||||
|
reset_reason_name(reset_reason),
|
||||||
|
(int)reset_reason);
|
||||||
|
if (written < 0 || (size_t)written >= body_len - *offset) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
*offset += (size_t)written;
|
||||||
|
json_append_escaped(body, body_len, offset, app != NULL ? app->version : "");
|
||||||
|
written = snprintf(body + *offset,
|
||||||
|
body_len - *offset,
|
||||||
|
"\",\"project_name\":\"");
|
||||||
|
if (written < 0 || (size_t)written >= body_len - *offset) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
*offset += (size_t)written;
|
||||||
|
json_append_escaped(body, body_len, offset, app != NULL ? app->project_name : "");
|
||||||
|
written = snprintf(body + *offset,
|
||||||
|
body_len - *offset,
|
||||||
|
"\",\"free_heap\":%lu,"
|
||||||
|
"\"minimum_free_heap\":%lu,"
|
||||||
|
"\"free_internal\":%lu,"
|
||||||
|
"\"largest_internal\":%lu,"
|
||||||
|
"\"free_spiram\":%lu,"
|
||||||
|
"\"largest_spiram\":%lu,"
|
||||||
|
"\"task_count\":%lu,"
|
||||||
|
"\"multimodal_connected\":%s,"
|
||||||
|
"\"multimodal_active_task\":%s,"
|
||||||
|
"\"multimodal_client_speech_active\":%s,"
|
||||||
|
"\"multimodal_remote_responding\":%s,"
|
||||||
|
"\"coredump_present\":%s,"
|
||||||
|
"\"coredump_size\":%lu"
|
||||||
|
"}",
|
||||||
|
(unsigned long)esp_get_free_heap_size(),
|
||||||
|
(unsigned long)esp_get_minimum_free_heap_size(),
|
||||||
|
(unsigned long)heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT),
|
||||||
|
(unsigned long)heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT),
|
||||||
|
(unsigned long)heap_caps_get_free_size(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT),
|
||||||
|
(unsigned long)heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT),
|
||||||
|
(unsigned long)uxTaskGetNumberOfTasks(),
|
||||||
|
(have_mm && mm.connected) ? "true" : "false",
|
||||||
|
(have_mm && mm.has_active_task) ? "true" : "false",
|
||||||
|
(have_mm && mm.client_speech_active) ? "true" : "false",
|
||||||
|
(have_mm && mm.remote_responding) ? "true" : "false",
|
||||||
|
s_coredump_present ? "true" : "false",
|
||||||
|
(unsigned long)s_coredump_size);
|
||||||
|
if (written < 0 || (size_t)written >= body_len - *offset) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
*offset += (size_t)written;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t build_upload_body(uint32_t sequence,
|
static esp_err_t build_upload_body(uint32_t sequence,
|
||||||
uint32_t dropped_count,
|
uint32_t dropped_count,
|
||||||
char *body,
|
char *body,
|
||||||
@@ -616,7 +741,16 @@ static esp_err_t build_upload_body(uint32_t sequence,
|
|||||||
}
|
}
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
written = snprintf(body + offset, body_len - offset, "]}");
|
written = snprintf(body + offset, body_len - offset, "]");
|
||||||
|
if (written < 0 || (size_t)written >= body_len - offset) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
offset += (size_t)written;
|
||||||
|
esp_err_t state_err = append_device_state(body, body_len, &offset);
|
||||||
|
if (state_err != ESP_OK) {
|
||||||
|
return state_err;
|
||||||
|
}
|
||||||
|
written = snprintf(body + offset, body_len - offset, "}");
|
||||||
if (written < 0 || (size_t)written >= body_len - offset) {
|
if (written < 0 || (size_t)written >= body_len - offset) {
|
||||||
return ESP_ERR_INVALID_SIZE;
|
return ESP_ERR_INVALID_SIZE;
|
||||||
}
|
}
|
||||||
@@ -669,11 +803,16 @@ static bool upload_one_file(uint32_t sequence)
|
|||||||
|
|
||||||
free(body);
|
free(body);
|
||||||
free(response);
|
free(response);
|
||||||
|
s_last_upload_ms = esp_timer_get_time() / 1000;
|
||||||
|
s_last_upload_err = err;
|
||||||
|
s_last_http_status = status;
|
||||||
|
|
||||||
if (err == ESP_OK && (status == 200 || status == 202 || status == 204)) {
|
if (err == ESP_OK && (status == 200 || status == 202 || status == 204)) {
|
||||||
char path[REMOTE_LOG_PATH_MAX] = {0};
|
char path[REMOTE_LOG_PATH_MAX] = {0};
|
||||||
make_log_path(sequence, path, sizeof(path));
|
make_log_path(sequence, path, sizeof(path));
|
||||||
(void)unlink(path);
|
(void)unlink(path);
|
||||||
|
s_successful_uploads++;
|
||||||
|
s_last_success_ms = s_last_upload_ms;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,10 +821,12 @@ static bool upload_one_file(uint32_t sequence)
|
|||||||
make_log_path(sequence, path, sizeof(path));
|
make_log_path(sequence, path, sizeof(path));
|
||||||
(void)unlink(path);
|
(void)unlink(path);
|
||||||
restore_dropped_count(dropped);
|
restore_dropped_count(dropped);
|
||||||
|
s_failed_uploads++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
restore_dropped_count(dropped);
|
restore_dropped_count(dropped);
|
||||||
|
s_failed_uploads++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -703,6 +844,144 @@ static bool upload_pending_files(void)
|
|||||||
return upload_one_file(sequences[0]);
|
return upload_one_file(sequences[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void detect_coredump(void)
|
||||||
|
{
|
||||||
|
size_t addr = 0U;
|
||||||
|
size_t size = 0U;
|
||||||
|
esp_err_t err = esp_core_dump_image_get(&addr, &size);
|
||||||
|
if (err == ESP_OK && size > 0U) {
|
||||||
|
s_coredump_present = true;
|
||||||
|
s_coredump_addr = addr;
|
||||||
|
s_coredump_size = size;
|
||||||
|
} else {
|
||||||
|
s_coredump_present = false;
|
||||||
|
s_coredump_addr = 0U;
|
||||||
|
s_coredump_size = 0U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t build_coredump_report_body(char *body, size_t body_len)
|
||||||
|
{
|
||||||
|
if (body == NULL || body_len == 0U) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
char device_id[64] = {0};
|
||||||
|
char serial_number[64] = {0};
|
||||||
|
domain_runtime_read_device_identity(device_id, sizeof(device_id), serial_number, sizeof(serial_number));
|
||||||
|
|
||||||
|
char panic_reason[192] = {0};
|
||||||
|
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF
|
||||||
|
(void)esp_core_dump_get_panic_reason(panic_reason, sizeof(panic_reason));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_t offset = 0U;
|
||||||
|
int written = snprintf(body,
|
||||||
|
body_len,
|
||||||
|
"{\"schema\":\"tq.coredump.report.v1\",\"device_id\":\"");
|
||||||
|
if (written < 0 || (size_t)written >= body_len) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
offset = (size_t)written;
|
||||||
|
json_append_escaped(body, body_len, &offset, device_id);
|
||||||
|
written = snprintf(body + offset, body_len - offset, "\",\"serial_number\":\"");
|
||||||
|
if (written < 0 || (size_t)written >= body_len - offset) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
offset += (size_t)written;
|
||||||
|
json_append_escaped(body, body_len, &offset, serial_number);
|
||||||
|
written = snprintf(body + offset,
|
||||||
|
body_len - offset,
|
||||||
|
"\",\"sent_at_ms\":%lld,\"coredump_addr\":%lu,\"coredump_size\":%lu,\"panic_reason\":\"",
|
||||||
|
(long long)(esp_timer_get_time() / 1000),
|
||||||
|
(unsigned long)s_coredump_addr,
|
||||||
|
(unsigned long)s_coredump_size);
|
||||||
|
if (written < 0 || (size_t)written >= body_len - offset) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
offset += (size_t)written;
|
||||||
|
json_append_escaped(body, body_len, &offset, panic_reason);
|
||||||
|
written = snprintf(body + offset, body_len - offset, "\"");
|
||||||
|
if (written < 0 || (size_t)written >= body_len - offset) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
offset += (size_t)written;
|
||||||
|
esp_err_t state_err = append_device_state(body, body_len, &offset);
|
||||||
|
if (state_err != ESP_OK) {
|
||||||
|
return state_err;
|
||||||
|
}
|
||||||
|
written = snprintf(body + offset, body_len - offset, "}");
|
||||||
|
if (written < 0 || (size_t)written >= body_len - offset) {
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool upload_coredump_report(void)
|
||||||
|
{
|
||||||
|
if (!s_coredump_present || s_coredump_reported ||
|
||||||
|
!control_plane_is_sta_connected() || realtime_path_busy()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *body = (char *)malloc(2048);
|
||||||
|
char *response = (char *)malloc(REMOTE_LOG_RESPONSE_MAX);
|
||||||
|
if (body == NULL || response == NULL) {
|
||||||
|
free(body);
|
||||||
|
free(response);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = build_coredump_report_body(body, 2048);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
free(body);
|
||||||
|
free(response);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = -1;
|
||||||
|
size_t response_len = 0U;
|
||||||
|
err = domain_runtime_post_json(CONFIG_TQ_REMOTE_LOG_ENDPOINT,
|
||||||
|
body,
|
||||||
|
NULL,
|
||||||
|
CONFIG_TQ_REMOTE_LOG_AUTH_TOKEN,
|
||||||
|
CONFIG_TQ_REMOTE_LOG_UPLOAD_TIMEOUT_MS,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
response,
|
||||||
|
REMOTE_LOG_RESPONSE_MAX,
|
||||||
|
&response_len,
|
||||||
|
&status);
|
||||||
|
free(body);
|
||||||
|
free(response);
|
||||||
|
|
||||||
|
s_last_upload_ms = esp_timer_get_time() / 1000;
|
||||||
|
s_last_upload_err = err;
|
||||||
|
s_last_http_status = status;
|
||||||
|
if (err == ESP_OK && (status == 200 || status == 202 || status == 204)) {
|
||||||
|
s_successful_uploads++;
|
||||||
|
s_last_success_ms = s_last_upload_ms;
|
||||||
|
s_coredump_reported = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
s_failed_uploads++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_startup_self_check(void)
|
||||||
|
{
|
||||||
|
ESP_LOGW("remote_log",
|
||||||
|
"enabled endpoint=%s dir=%s pending_files=%u queue=%u max_files=%u file_max=%u coredump_present=%d coredump_size=%u",
|
||||||
|
CONFIG_TQ_REMOTE_LOG_ENDPOINT,
|
||||||
|
REMOTE_LOG_DIR,
|
||||||
|
(unsigned)count_pending_files(),
|
||||||
|
(unsigned)CONFIG_TQ_REMOTE_LOG_QUEUE_LEN,
|
||||||
|
(unsigned)CONFIG_TQ_REMOTE_LOG_MAX_FILES,
|
||||||
|
(unsigned)CONFIG_TQ_REMOTE_LOG_FILE_MAX_BYTES,
|
||||||
|
s_coredump_present,
|
||||||
|
(unsigned)s_coredump_size);
|
||||||
|
}
|
||||||
|
|
||||||
static void install_hook(void)
|
static void install_hook(void)
|
||||||
{
|
{
|
||||||
if (s_hook_installed) {
|
if (s_hook_installed) {
|
||||||
@@ -726,7 +1005,7 @@ static void remote_log_task(void *arg)
|
|||||||
{
|
{
|
||||||
(void)arg;
|
(void)arg;
|
||||||
|
|
||||||
esp_err_t mount_err = domain_runtime_mount_littlefs();
|
esp_err_t mount_err = domain_runtime_mount_logfs();
|
||||||
if (mount_err != ESP_OK) {
|
if (mount_err != ESP_OK) {
|
||||||
s_remote_log_task = NULL;
|
s_remote_log_task = NULL;
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
@@ -734,8 +1013,10 @@ static void remote_log_task(void *arg)
|
|||||||
}
|
}
|
||||||
(void)make_log_dir();
|
(void)make_log_dir();
|
||||||
s_file_sequence = find_next_file_sequence();
|
s_file_sequence = find_next_file_sequence();
|
||||||
|
detect_coredump();
|
||||||
|
|
||||||
install_hook();
|
install_hook();
|
||||||
|
log_startup_self_check();
|
||||||
|
|
||||||
TickType_t last_flush = xTaskGetTickCount();
|
TickType_t last_flush = xTaskGetTickCount();
|
||||||
uint32_t backoff_ms = 0U;
|
uint32_t backoff_ms = 0U;
|
||||||
@@ -750,6 +1031,7 @@ static void remote_log_task(void *arg)
|
|||||||
|
|
||||||
if (backoff_ms == 0U || now >= next_upload_tick) {
|
if (backoff_ms == 0U || now >= next_upload_tick) {
|
||||||
drain_memory_queue_to_flash();
|
drain_memory_queue_to_flash();
|
||||||
|
(void)upload_coredump_report();
|
||||||
bool ok = upload_pending_files();
|
bool ok = upload_pending_files();
|
||||||
if (ok) {
|
if (ok) {
|
||||||
backoff_ms = 0U;
|
backoff_ms = 0U;
|
||||||
@@ -792,6 +1074,81 @@ esp_err_t control_plane_remote_log_start(void)
|
|||||||
return created == pdPASS ? ESP_OK : ESP_ERR_NO_MEM;
|
return created == pdPASS ? ESP_OK : ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_remote_log_flush_now(void)
|
||||||
|
{
|
||||||
|
if (s_remote_log_task == NULL) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
drain_memory_queue_to_flash();
|
||||||
|
if (!control_plane_is_sta_connected()) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
(void)upload_coredump_report();
|
||||||
|
return upload_pending_files() ? ESP_OK : ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_remote_log_clear(void)
|
||||||
|
{
|
||||||
|
portENTER_CRITICAL(&s_lock);
|
||||||
|
s_queue_read = 0U;
|
||||||
|
s_queue_count = 0U;
|
||||||
|
s_dropped_count = 0U;
|
||||||
|
portEXIT_CRITICAL(&s_lock);
|
||||||
|
|
||||||
|
uint32_t sequences[CONFIG_TQ_REMOTE_LOG_MAX_FILES + 8] = {0};
|
||||||
|
size_t count = collect_files(sequences, sizeof(sequences) / sizeof(sequences[0]));
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
char path[REMOTE_LOG_PATH_MAX] = {0};
|
||||||
|
make_log_path(sequences[i], path, sizeof(path));
|
||||||
|
(void)unlink(path);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_remote_log_format_status(char *out, size_t out_len)
|
||||||
|
{
|
||||||
|
if (out == NULL || out_len == 0U) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t dropped = 0U;
|
||||||
|
portENTER_CRITICAL(&s_lock);
|
||||||
|
dropped = s_dropped_count;
|
||||||
|
portEXIT_CRITICAL(&s_lock);
|
||||||
|
|
||||||
|
int written = snprintf(out,
|
||||||
|
out_len,
|
||||||
|
"{\"enabled\":true,\"running\":%s,\"hook_installed\":%s,"
|
||||||
|
"\"endpoint\":\"%s\",\"dir\":\"%s\",\"queue_count\":%u,"
|
||||||
|
"\"queue_capacity\":%u,\"pending_files\":%u,"
|
||||||
|
"\"max_files\":%u,\"file_max_bytes\":%u,"
|
||||||
|
"\"dropped_count\":%lu,\"successful_uploads\":%lu,"
|
||||||
|
"\"failed_uploads\":%lu,\"last_http_status\":%d,"
|
||||||
|
"\"last_upload_err\":%d,\"last_upload_ms\":%lld,"
|
||||||
|
"\"last_success_ms\":%lld,\"coredump_present\":%s,"
|
||||||
|
"\"coredump_reported\":%s,\"coredump_size\":%lu}",
|
||||||
|
s_remote_log_task != NULL ? "true" : "false",
|
||||||
|
s_hook_installed ? "true" : "false",
|
||||||
|
CONFIG_TQ_REMOTE_LOG_ENDPOINT,
|
||||||
|
REMOTE_LOG_DIR,
|
||||||
|
(unsigned)snapshot_queue_count(),
|
||||||
|
(unsigned)CONFIG_TQ_REMOTE_LOG_QUEUE_LEN,
|
||||||
|
(unsigned)count_pending_files(),
|
||||||
|
(unsigned)CONFIG_TQ_REMOTE_LOG_MAX_FILES,
|
||||||
|
(unsigned)CONFIG_TQ_REMOTE_LOG_FILE_MAX_BYTES,
|
||||||
|
(unsigned long)dropped,
|
||||||
|
(unsigned long)s_successful_uploads,
|
||||||
|
(unsigned long)s_failed_uploads,
|
||||||
|
s_last_http_status,
|
||||||
|
(int)s_last_upload_err,
|
||||||
|
(long long)s_last_upload_ms,
|
||||||
|
(long long)s_last_success_ms,
|
||||||
|
s_coredump_present ? "true" : "false",
|
||||||
|
s_coredump_reported ? "true" : "false",
|
||||||
|
(unsigned long)s_coredump_size);
|
||||||
|
return (written > 0 && (size_t)written < out_len) ? ESP_OK : ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
void control_plane_remote_log_stop(int timeout_ms)
|
void control_plane_remote_log_stop(int timeout_ms)
|
||||||
{
|
{
|
||||||
if (s_remote_log_task == NULL) {
|
if (s_remote_log_task == NULL) {
|
||||||
@@ -821,4 +1178,23 @@ void control_plane_remote_log_stop(int timeout_ms)
|
|||||||
{
|
{
|
||||||
(void)timeout_ms;
|
(void)timeout_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_remote_log_flush_now(void)
|
||||||
|
{
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_remote_log_clear(void)
|
||||||
|
{
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t control_plane_remote_log_format_status(char *out, size_t out_len)
|
||||||
|
{
|
||||||
|
if (out == NULL || out_len == 0U) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
int written = snprintf(out, out_len, "{\"enabled\":false}");
|
||||||
|
return (written > 0 && (size_t)written < out_len) ? ESP_OK : ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ bool domain_runtime_slot_feature_switch_is_active(void);
|
|||||||
|
|
||||||
esp_err_t domain_runtime_prepare_storage(void);
|
esp_err_t domain_runtime_prepare_storage(void);
|
||||||
esp_err_t domain_runtime_mount_littlefs(void);
|
esp_err_t domain_runtime_mount_littlefs(void);
|
||||||
|
esp_err_t domain_runtime_mount_logfs(void);
|
||||||
void domain_runtime_read_device_identity(char *device_id,
|
void domain_runtime_read_device_identity(char *device_id,
|
||||||
size_t device_id_len,
|
size_t device_id_len,
|
||||||
char *serial_number,
|
char *serial_number,
|
||||||
|
|||||||
@@ -758,6 +758,11 @@ esp_err_t domain_runtime_mount_littlefs(void)
|
|||||||
return platform_mount_littlefs();
|
return platform_mount_littlefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t domain_runtime_mount_logfs(void)
|
||||||
|
{
|
||||||
|
return platform_mount_logfs();
|
||||||
|
}
|
||||||
|
|
||||||
void domain_runtime_read_device_identity(char *device_id,
|
void domain_runtime_read_device_identity(char *device_id,
|
||||||
size_t device_id_len,
|
size_t device_id_len,
|
||||||
char *serial_number,
|
char *serial_number,
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ typedef struct {
|
|||||||
esp_err_t platform_init(void);
|
esp_err_t platform_init(void);
|
||||||
esp_err_t platform_prepare_storage(void);
|
esp_err_t platform_prepare_storage(void);
|
||||||
esp_err_t platform_mount_littlefs(void);
|
esp_err_t platform_mount_littlefs(void);
|
||||||
|
esp_err_t platform_mount_logfs(void);
|
||||||
void platform_read_device_identity(char *device_id,
|
void platform_read_device_identity(char *device_id,
|
||||||
size_t device_id_len,
|
size_t device_id_len,
|
||||||
char *serial_number,
|
char *serial_number,
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
#define VOICE_PROMPT_DEFAULT_BASE_PATH "/littlefs/audio_prompts"
|
#define VOICE_PROMPT_DEFAULT_BASE_PATH "/littlefs/audio_prompts"
|
||||||
#define VOICE_PROMPT_LITTLEFS_BASE_PATH "/littlefs"
|
#define VOICE_PROMPT_LITTLEFS_BASE_PATH "/littlefs"
|
||||||
#define VOICE_PROMPT_LITTLEFS_PARTITION "gifpack"
|
#define VOICE_PROMPT_LITTLEFS_PARTITION "gifpack"
|
||||||
|
#define PLATFORM_LOGFS_BASE_PATH "/logfs"
|
||||||
|
#define PLATFORM_LOGFS_PARTITION "logfs"
|
||||||
#define VOICE_PROMPT_FRAME_HEADER_BYTES 4U
|
#define VOICE_PROMPT_FRAME_HEADER_BYTES 4U
|
||||||
#define VOICE_PROMPT_MAX_OPUS_PACKET_BYTES 2048U
|
#define VOICE_PROMPT_MAX_OPUS_PACKET_BYTES 2048U
|
||||||
#define VOICE_PROMPT_OPUS_FRAME_MS 60U
|
#define VOICE_PROMPT_OPUS_FRAME_MS 60U
|
||||||
@@ -43,6 +45,8 @@ static const char *VOICE_TAG = "voice_prompt";
|
|||||||
static bool s_power_hold_configured;
|
static bool s_power_hold_configured;
|
||||||
static bool s_voice_prompt_littlefs_mount_attempted;
|
static bool s_voice_prompt_littlefs_mount_attempted;
|
||||||
static esp_err_t s_voice_prompt_littlefs_mount_result = ESP_ERR_INVALID_STATE;
|
static esp_err_t s_voice_prompt_littlefs_mount_result = ESP_ERR_INVALID_STATE;
|
||||||
|
static bool s_logfs_mount_attempted;
|
||||||
|
static esp_err_t s_logfs_mount_result = ESP_ERR_INVALID_STATE;
|
||||||
|
|
||||||
static uint64_t power_manager_gpio_bit_mask(int gpio_num)
|
static uint64_t power_manager_gpio_bit_mask(int gpio_num)
|
||||||
{
|
{
|
||||||
@@ -154,6 +158,49 @@ esp_err_t platform_mount_littlefs(void)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t platform_mount_logfs(void)
|
||||||
|
{
|
||||||
|
if (s_logfs_mount_attempted) {
|
||||||
|
return s_logfs_mount_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_vfs_littlefs_conf_t conf = {
|
||||||
|
.base_path = PLATFORM_LOGFS_BASE_PATH,
|
||||||
|
.partition_label = PLATFORM_LOGFS_PARTITION,
|
||||||
|
.partition = NULL,
|
||||||
|
.format_if_mount_failed = true,
|
||||||
|
.dont_mount = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t err = esp_vfs_littlefs_register(&conf);
|
||||||
|
if (err == ESP_ERR_INVALID_STATE &&
|
||||||
|
esp_littlefs_mounted(PLATFORM_LOGFS_PARTITION)) {
|
||||||
|
err = ESP_OK;
|
||||||
|
}
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
size_t total_bytes = 0;
|
||||||
|
size_t used_bytes = 0;
|
||||||
|
esp_err_t info_err = esp_littlefs_info(PLATFORM_LOGFS_PARTITION,
|
||||||
|
&total_bytes,
|
||||||
|
&used_bytes);
|
||||||
|
if (info_err == ESP_OK) {
|
||||||
|
ESP_LOGI(VOICE_TAG,
|
||||||
|
"LogFS mounted at %s, used=%u total=%u",
|
||||||
|
PLATFORM_LOGFS_BASE_PATH,
|
||||||
|
(unsigned)used_bytes,
|
||||||
|
(unsigned)total_bytes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(VOICE_TAG,
|
||||||
|
"Mount LogFS failed: %s",
|
||||||
|
esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
s_logfs_mount_attempted = true;
|
||||||
|
s_logfs_mount_result = err;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t voice_prompt_play_file(const char *path)
|
esp_err_t voice_prompt_play_file(const char *path)
|
||||||
{
|
{
|
||||||
if (path == NULL || path[0] == '\0') {
|
if (path == NULL || path[0] == '\0') {
|
||||||
|
|||||||
+13
-1
@@ -34,8 +34,20 @@ CONFIG_TQ_REMOTE_LOG_ENTRY_MAX_BYTES=256
|
|||||||
CONFIG_TQ_REMOTE_LOG_BATCH_MAX_ENTRIES=30
|
CONFIG_TQ_REMOTE_LOG_BATCH_MAX_ENTRIES=30
|
||||||
CONFIG_TQ_REMOTE_LOG_FLUSH_INTERVAL_MS=5000
|
CONFIG_TQ_REMOTE_LOG_FLUSH_INTERVAL_MS=5000
|
||||||
CONFIG_TQ_REMOTE_LOG_UPLOAD_TIMEOUT_MS=8000
|
CONFIG_TQ_REMOTE_LOG_UPLOAD_TIMEOUT_MS=8000
|
||||||
CONFIG_TQ_REMOTE_LOG_MAX_FILES=32
|
CONFIG_TQ_REMOTE_LOG_MAX_FILES=24
|
||||||
CONFIG_TQ_REMOTE_LOG_FILE_MAX_BYTES=16384
|
CONFIG_TQ_REMOTE_LOG_FILE_MAX_BYTES=16384
|
||||||
|
CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=y
|
||||||
|
# CONFIG_ESP_COREDUMP_ENABLE_TO_UART is not set
|
||||||
|
# CONFIG_ESP_COREDUMP_ENABLE_TO_NONE is not set
|
||||||
|
CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF=y
|
||||||
|
# CONFIG_ESP_COREDUMP_DATA_FORMAT_BIN is not set
|
||||||
|
CONFIG_ESP_COREDUMP_CHECKSUM_CRC32=y
|
||||||
|
# CONFIG_ESP_COREDUMP_CHECKSUM_SHA256 is not set
|
||||||
|
# CONFIG_ESP_COREDUMP_CAPTURE_DRAM is not set
|
||||||
|
CONFIG_ESP_COREDUMP_CHECK_BOOT=y
|
||||||
|
# CONFIG_ESP_COREDUMP_LOGS is not set
|
||||||
|
CONFIG_ESP_COREDUMP_MAX_TASKS_NUM=16
|
||||||
|
CONFIG_ESP_COREDUMP_STACK_SIZE=1792
|
||||||
CONFIG_TQ_KEY_INPUT_ENABLE=y
|
CONFIG_TQ_KEY_INPUT_ENABLE=y
|
||||||
CONFIG_TQ_KEY_INPUT_TASK_STACK=4096
|
CONFIG_TQ_KEY_INPUT_TASK_STACK=4096
|
||||||
CONFIG_TQ_KEY_IRQ_QUEUE_LEN=16
|
CONFIG_TQ_KEY_IRQ_QUEUE_LEN=16
|
||||||
|
|||||||
Reference in New Issue
Block a user