/* * 模块名称:System Log (模拟器后端) — 运行/自检记录落盘 * 模块功能:宿主文件 sim_run.log / sim_selftest.log(工作目录),与 MCU 端同格式 * (定长 64B 记录);单线程直写,无队列 * 适用平台:PC 模拟器 * 作者:王建锋 * 创建日期:2026-09-14 * 修改记录: * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §4) */ /* 头文件包含区 */ #include #include #include #include #include "sys_log.h" /* 静态变量定义区 */ static const char s_paths[SYSLOG_COUNT][16] = { "sim_run.log", "sim_selftest.log" }; static const uint32_t s_caps[SYSLOG_COUNT] = { SYSLOG_RUN_MAX, SYSLOG_SELFTEST_MAX }; /* 函数定义区 */ /* * 函数功能:取当前宿主本地时间(ISO 8601 空格变体 + epoch) * 入口参数:p_ts - 输出缓冲(≥SYSLOG_TS_LEN+1 字节,不可为 NULL) char * * p_epoch- 输出 epoch 秒(不可为 NULL) uint32_t * * 返回值:无 */ static void s_now(char *p_ts, uint32_t *p_epoch) { time_t t = time(NULL); struct tm tmv; #if defined(_WIN32) localtime_s(&tmv, &t); #else localtime_r(&t, &tmv); #endif (void)sprintf(p_ts, "%04d-%02d-%02d %02d:%02d:%02d", tmv.tm_year + 1900, tmv.tm_mon + 1, tmv.tm_mday, tmv.tm_hour, tmv.tm_min, tmv.tm_sec); *p_epoch = (uint32_t)t; } /* * 函数功能:追加一条记录(格式化 64B;容量满先删除重建) * 入口参数:id - 日志 ID syslog_id_t * desc - 描述串(UTF-8,超长截断至 SYSLOG_DESC_MAX) const char * * 返回值:0 - 成功;-1 - 写失败 int */ static int s_append(syslog_id_t id, const char *desc) { FILE *fp; uint8_t rec[SYSLOG_REC_SIZE]; char ts[SYSLOG_TS_LEN + 1u]; uint32_t epoch = 0u; size_t dl; if ((uint32_t)id >= (uint32_t)SYSLOG_COUNT || desc == NULL) { return -1; } if (syslog_count(id) >= s_caps[id]) { (void)remove(s_paths[id]); } memset(rec, 0u, sizeof(rec)); s_now(ts, &epoch); memcpy(rec, ts, SYSLOG_TS_LEN); rec[SYSLOG_TS_LEN] = ' '; (void)sprintf((char *)(rec + SYSLOG_EPOCH_OFFS), "%010u", (unsigned int)epoch); rec[SYSLOG_DESC_OFFS - 1u] = ' '; dl = strlen(desc); if (dl > SYSLOG_DESC_MAX) { dl = SYSLOG_DESC_MAX; } memcpy(rec + SYSLOG_DESC_OFFS, desc, dl); fp = fopen(s_paths[id], "ab"); if (fp == NULL) { return -1; } if (fwrite(rec, 1u, sizeof(rec), fp) != sizeof(rec)) { (void)fclose(fp); return -1; } (void)fclose(fp); return 0; } /* * 函数功能:读取第 idx 条原始记录(64B) * 入口参数:id - 日志 ID syslog_id_t * idx - 记录序号(0 起) uint32_t * p_rec- 输出缓冲(SYSLOG_REC_SIZE 字节,不可为 NULL) uint8_t * * 返回值:0 - 成功;-1 - 越界/读失败 int */ static int s_read_raw(syslog_id_t id, uint32_t idx, uint8_t *p_rec) { FILE *fp; if (p_rec == NULL || idx >= syslog_count(id)) { return -1; } fp = fopen(s_paths[id], "rb"); if (fp == NULL) { return -1; } if (fseek(fp, (long)idx * SYSLOG_REC_SIZE, SEEK_SET) != 0 || fread(p_rec, 1u, sizeof(uint8_t) * SYSLOG_REC_SIZE, fp) != SYSLOG_REC_SIZE) { (void)fclose(fp); return -1; } (void)fclose(fp); return 0; } /* * 函数功能:投递一条日志(单线程直写宿主文件) * 入口参数:id - 日志 ID syslog_id_t * desc - 描述串(UTF-8,超长截断) const char * * 返回值:无 */ void syslog_post(syslog_id_t id, const char *desc) { (void)s_append(id, desc); } /* * 函数功能:获取记录条数(= filesize / SYSLOG_REC_SIZE,O(1)) * 入口参数:id - 日志 ID syslog_id_t * 返回值:条数;文件缺失返回 0 uint32_t */ uint32_t syslog_count(syslog_id_t id) { struct stat st; if ((uint32_t)id >= (uint32_t)SYSLOG_COUNT) { return 0u; } if (stat(s_paths[id], &st) != 0) { return 0u; } return (uint32_t)st.st_size / SYSLOG_REC_SIZE; } /* * 函数功能:读取第 idx 条记录(时间戳 + 描述,均 NUL 结尾) * 入口参数:id - 日志 ID syslog_id_t * idx - 记录序号(0 起) uint32_t * p_ts - 时间戳输出缓冲(≥SYSLOG_TS_LEN+1 字节,不可为 NULL) char * * ts_len - p_ts 容量 size_t * p_desc - 描述输出缓冲(≥SYSLOG_DESC_MAX+1 字节,不可为 NULL) char * * desc_len - p_desc 容量 size_t * 返回值:0 - 成功;-1 - 越界/读失败 int */ int syslog_get_record(syslog_id_t id, uint32_t idx, char *p_ts, size_t ts_len, char *p_desc, size_t desc_len) { uint8_t rec[SYSLOG_REC_SIZE]; size_t dl = 0u; if (p_ts == NULL || p_desc == NULL || ts_len <= SYSLOG_TS_LEN || desc_len <= SYSLOG_DESC_MAX) { return -1; } if (s_read_raw(id, idx, rec) != 0) { return -1; } memcpy(p_ts, rec, SYSLOG_TS_LEN); p_ts[SYSLOG_TS_LEN] = '\0'; while (dl < SYSLOG_DESC_MAX && rec[SYSLOG_DESC_OFFS + dl] != '\0') { dl++; } if (dl >= desc_len) { dl = desc_len - 1u; } memcpy(p_desc, rec + SYSLOG_DESC_OFFS, dl); p_desc[dl] = '\0'; return 0; } /* * 函数功能:读取末条记录的 epoch 秒(上电/重启分类用) * 入口参数:id - 日志 ID syslog_id_t * p_epoch- 输出缓冲(不可为 NULL;无记录时置 0) uint32_t * * 返回值:0 - 成功读到末条;-1 - 文件缺失/空 int */ int syslog_last_epoch(syslog_id_t id, uint32_t *p_epoch) { uint8_t rec[SYSLOG_REC_SIZE]; uint32_t v = 0u; uint32_t i; if (p_epoch == NULL || syslog_count(id) == 0u) { *p_epoch = 0u; return -1; } if (s_read_raw(id, syslog_count(id) - 1u, rec) != 0) { *p_epoch = 0u; return -1; } for (i = 0u; i < SYSLOG_EPOCH_LEN; i++) { char c = (char)rec[SYSLOG_EPOCH_OFFS + i]; if (c >= '0' && c <= '9') { v = v * 10u + (uint32_t)(c - '0'); } } *p_epoch = v; return 0; }