/** * @file app_log.c * @author LokLiang * @brief app_log.h 模块源码 * @version 0.1 * @date 2023-11-242 * * @copyright Copyright (c) 2023 * */ #include "app_log.h" #include "nvs.h" #include "utils/crc.h" #include "os/os.h" #undef CONFIG_SYS_LOG_LEVEL #define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF #define SYS_LOG_DOMAIN "APLOG" #define CONS_ABORT() #include "sys_log.h" #define _NVS_KEY_MAIN "log-main" /* 数据区 -------------------------------------------------------------------------------------- */ extern nvs_handle g_nvs_hdl; static struct { os_time_t flight_time_start; } s_cm; /* nvs 接口 ------------------------------------------------------------------------------------ */ static int _read_key(const char *key, void *out_value, size_t tar_length, uint32_t *crc_base); static int _set_crc(const void *struct_base, size_t struct_length, uint32_t *crc_base); static int _do_factory_set(log_app_t *log); /** * @brief 读取指定的数据,并按给出的特性进行校验,返回校验结果 * * @param key 在 nvs 的存储块的键值 * @param out_value[out] 输出内存 * @param tar_length 输出的内存预期的长度 * @param crc_base[io] CRC 校验值所在内存块的地址(绝对值)。输入:校验值,输出:新的正确校验值 * @retval 0 正确读取了已存储数据 * @retval 1 nvs 模块可用, CRC 校验值已更新 * @retval -1 nvs 模块不可用 */ static int _read_key(const char *key, void *out_value, size_t tar_length, uint32_t *crc_base) { size_t required_size = 0; esp_err_t err = ESP_OK; /* 在确认已存储的数据长度前,仅获取长度以免数据溢出 */ err = nvs_get_blob(g_nvs_hdl, key, NULL, &required_size); if (err != ESP_OK) { SYS_LOG_WRN("failed to read prfsdata data length, err:%d", err); return -1; } if (required_size != tar_length) { SYS_LOG_WRN("invalid prfsdata length, length:%d, err:%d", required_size, err); return -1; } /* 读出已存储的数据 */ err = nvs_get_blob(g_nvs_hdl, key, out_value, &required_size); if (err != ESP_OK) { SYS_LOG_ERR("failed to read prfsdata data, err:%d", err); return -1; } /* 设置并校验 CRC */ if (_set_crc(out_value, tar_length, crc_base)) { SYS_LOG_WRN("read frs failed, incorrect crc and update"); return 1; } return 0; } /** * @brief 校验并更新结构体中 CRC 的值。 * 这将校验整个结构(包含 CRC 本身),因此 CRC 自身的初始值为 0,然后更新 CRC 的结果到结构中 * * @param struct_base 待分析和设置的内存块 * @param struct_length 待分析和设置的内存块的实际长度 * @param crc_base[io] CRC 校验值所在内存块的地址(绝对值) * @retval 0 校验正确 * @retval 1 校验错误,并且设置了正确的校验值 */ static int _set_crc(const void *struct_base, size_t struct_length, uint32_t *crc_base) { // 校验读出来的数据的正确性 uint32_t crc_read = *crc_base; *crc_base = 0; *crc_base = crc32_calc(0, struct_base, struct_length); return (crc_read != *crc_base); } static int _do_factory_set(log_app_t *log) { memset(log, 0, sizeof(*log)); log->flight_data.crc16 = crc16_calc(NULL, 0); return app_log_do_save(log); } /** * @brief 初始化 */ void app_log_init(void) { SYS_ASSERT(g_nvs_hdl != 0, ""); } /** * @brief 使内存的数据恢复到默认设置 * * @retval 0 操作成功 * @retval -1 操作失败 */ int app_log_factory_set(void) { log_app_t *log = os_malloc(sizeof(log_app_t)); if (log == NULL) { SYS_LOG_WRN("malloc fail"); return -1; } int ret = _do_factory_set(log); os_free(log); return ret; } /** * @brief 立即执行保存 配置数据 到 nvs 的动作 * * @param delay_ms 0: 立即保存, > 0 多少毫秒后执行保存 * @return int @ref esp_err_t */ int app_log_do_save(log_app_t *log) { log->version = APP_LOG_DATA_VER; _set_crc(log, sizeof(*log), &log->crc32); esp_err_t err = nvs_set_blob(g_nvs_hdl, _NVS_KEY_MAIN, log, sizeof(*log)); if (err != ESP_OK) { SYS_LOG_WRN("nvs_set_blob() err: %d", err); } return err; } /* 读接口 -------------------------------------------------------------------------------------- */ /** * @brief 读取日志信息 * 将从 nvs 读取已保存的数据到内存 log_app_t 中。 * 当出现以下情况时,配置数据被恢复到默认值: * nvs 未初始化或操作失败; * 使用 app_log_factory_set() 立即恢复到默认值; * 结构体的长度 log_app_t 发生改变; * APP_CONFIG_DATA_VER 定义的值发生改变; * * @param out_log[out] 用于保存日志信息的内存 * @retval 0 成功读取已有记录 * @retval 1 已恢复默认设置 * @retval -1 nvs 操作失败 */ int app_log_read(log_app_t *out_log) { int ret = _read_key(_NVS_KEY_MAIN, out_log, sizeof(*out_log), &out_log->crc32); if (ret) { goto use_default_prfs; } if (out_log->version != APP_LOG_DATA_VER) { ret = 1; SYS_LOG_WRN("Upgrade to the latest version: %u ==> %u", out_log->version, APP_LOG_DATA_VER); goto use_default_prfs; } return ret; use_default_prfs: _do_factory_set(out_log); return ret; } /* 写接口 -------------------------------------------------------------------------------------- */ /** * @brief 开始计时 * */ void app_log_flight_time_start(void) { log_app_t *log = os_malloc(sizeof(log_app_t)); if (log == NULL) { SYS_LOG_WRN("malloc fail"); return; } if (log->flight_data.flight_number < __ARRAY_SIZE(log->flight_data.datas)) { s_cm.flight_time_start = os_get_sys_time(); } else { s_cm.flight_time_start = 0; SYS_LOG_INF("The maximum number of groups: %d", __ARRAY_SIZE(log->flight_data.datas)); } os_free(log); } /** * @brief 结束计时,并保存这个记录 * */ void app_log_flight_time_stop(void) { if (s_cm.flight_time_start == 0) { SYS_LOG_WRN("never exec app_log_flight_time_start()"); return; } log_app_t *log = os_malloc(sizeof(log_app_t)); if (log == NULL) { SYS_LOG_WRN("malloc fail"); return; } os_time_t time_cost_sec = (os_get_sys_time() - s_cm.flight_time_start) / 1000; if (time_cost_sec > 10) { log->flight_data.datas[log->flight_data.flight_number++] = time_cost_sec; log->flight_data.crc16 = crc16_calc(log->flight_data.datas, sizeof(log->flight_data.datas[0]) * log->flight_data.flight_number); app_log_do_save(log); } s_cm.flight_time_start = 0; os_free(log); } /** * @brief 清除全部计时记录 * */ void app_log_flight_time_clr(void) { log_app_t *log = os_malloc(sizeof(log_app_t)); if (log == NULL) { SYS_LOG_WRN("malloc fail"); return; } memset(&log->flight_data, 0, sizeof(log->flight_data)); app_log_do_save(log); os_free(log); }