0f7b5a87fb
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号) 一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、 采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。 门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏 undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8 reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF; id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。 test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字, 是本批唯一改变构建产物的文件集合。 为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged 部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、 不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。 实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01% (基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 / 告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。 基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处 引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使 识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。 ⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二 根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有), 今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。 固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0; App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889 断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿; check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带 BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。 .cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法: Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。 同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%, RW_CCM 57,376 B / 87.9%,阈值 92%。
169 lines
8.7 KiB
C
169 lines
8.7 KiB
C
/*
|
||
* 模块名称:System Log — 运行/自检记录引擎(双端共享核心)
|
||
* 模块功能:定长 64B 文本日志记录的公共 API、格式常量与行拆分;
|
||
* MCU 后端 = FatFS /log 目录日志文件(sys_log_mcu.c),模拟器后端 = 宿主文件(sim_syslog.c)
|
||
* 适用平台:STM32F407ZGTx + PC 模拟器(共享头,无 OS/LVGL 依赖)
|
||
* 作者:王建锋
|
||
* 创建日期:2026-09-14
|
||
* 修改记录:
|
||
* v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §4)
|
||
* v1.1 2026-10-24 王建锋 仅契约注释更正,与 App/log/sys_log_mcu.c 的 C/F 两项
|
||
* 整改同步(无签名、无行为改动):
|
||
* syslog_get_record 写死容量要求(ts_len > SYSLOG_TS_LEN、
|
||
* desc_len > SYSLOG_DESC_MAX)并明确"容量不足一律 -1、
|
||
* 不截断、不写调用方缓冲"——旧注释像是"给少了会被截断",
|
||
* 实际是整条读不到;syslog_post 写明必须假设丢弃存在、
|
||
* 且丢弃留痕已按通道节流(节流后无丢弃计数)
|
||
*/
|
||
|
||
#ifndef SYS_LOG_H
|
||
#define SYS_LOG_H
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#include <stdint.h>
|
||
#include <stddef.h>
|
||
|
||
/*
|
||
* 日志 ID:决定落盘文件与容量上限
|
||
*/
|
||
typedef enum {
|
||
SYSLOG_RUN = 0u, /* 运行记录 /log/run.log:上电/重启事件 */
|
||
SYSLOG_SELFTEST, /* 自检记录 /log/selftest.log:开机自检故障(仅故障才写) */
|
||
SYSLOG_COUNT /* 哨兵值,非有效 ID */
|
||
} syslog_id_t;
|
||
|
||
/*
|
||
* 记录格式(定长 64B,一行一条;设计文档 §4.2):
|
||
* [0..18] "YYYY-MM-DD HH:MM:SS"(ISO 8601 空格变体,19 字符;墙钟无效 = "1970-01-01 08:00:00")
|
||
* [19] ' '
|
||
* [20..29] epoch 秒 ASCII "%010u"(机器解析/上电重启分类用;墙钟无效 = "0000000000")
|
||
* [30] ' '
|
||
* [31..58] 描述 UTF-8(≤28B = 9 汉字),不足补 '\0'
|
||
* [59..63] '\0' 填充
|
||
*/
|
||
#define SYSLOG_REC_SIZE 64u /* 单条记录字节数(定长) */
|
||
#define SYSLOG_TS_LEN 19u /* 时间戳字段长度 "YYYY-MM-DD HH:MM:SS" */
|
||
#define SYSLOG_EPOCH_OFFS 20u /* epoch ASCII 字段偏移 */
|
||
#define SYSLOG_EPOCH_LEN 10u /* epoch ASCII 字段长度(2026 年 Unix 秒 = 10 位) */
|
||
#define SYSLOG_DESC_OFFS 31u /* 描述字段偏移 */
|
||
#define SYSLOG_DESC_MAX 28u /* 描述最大字节数(受框架行缓冲 CP_ROW_BUF_LEN=32 约束) */
|
||
|
||
/*
|
||
* 容量上限(条数;满时删除重建,设计文档 §4.3)
|
||
*/
|
||
#define SYSLOG_RUN_MAX 1024u /* /log/run.log:64KB */
|
||
#define SYSLOG_SELFTEST_MAX 512u /* /log/selftest.log:32KB */
|
||
|
||
/*
|
||
* 上电/重启分类窗口(秒):run.log 末条 epoch 距 now < 该值 → "reboot",否则 "power on"
|
||
*/
|
||
#define SYSLOG_REBOOT_WINDOW_S 600u
|
||
|
||
/*
|
||
* 函数功能:投递一条日志(任意上下文;非阻塞,队列满则丢弃)
|
||
* 入口参数:id - 日志 ID syslog_id_t
|
||
* desc - 描述串(UTF-8;超过 SYSLOG_DESC_MAX 的部分丢弃,落盘只留前 28 字节)
|
||
* 本参数**投递时即拷贝进待发条目**:调用方缓冲区可立即复用,
|
||
* 不要求生命周期覆盖到落盘(2026-10-24 台架实证后由「只存指针」
|
||
* 改为条目内嵌定长缓冲,理由见 sys_log_mcu.c 的 syslog_entry_t 注释)
|
||
* 指针本身不可为 NULL const char *
|
||
* 返回值:无
|
||
* 限定条件:MCU 端 FreeRTOS 启动前调用走静态待发数组(由 defaultTask 挂载后落盘)
|
||
* 函数说明:调用方**必须假设投递可能被丢弃**——MCU 后端(sys_log_mcu.c)队列 8 槽、
|
||
* 启动前待发数组 8 槽,卷被 ftpTask 长期占用时 defaultTask 会把整批回投
|
||
* 队列(队列因此可长时间处于满态),此时本函数直接丢弃该条,并只在
|
||
* "转入故障"的那一次留痕(丢弃留痕按正常→故障节流,F 项整改 2026-10-24;
|
||
* 节流后**丢弃总数没有计数**,要计量得另加计数器)。必须留住的证据走
|
||
* sys_log_evidence(),不要靠 syslog_post 重试。
|
||
* 模拟器后端(simulator/sim/sim_syslog.c)是单线程直写宿主文件、**没有
|
||
* 队列与丢弃**,因此上面这段满态语义只约束 MCU 端。
|
||
*/
|
||
void syslog_post(syslog_id_t id, const char *desc);
|
||
|
||
/*
|
||
* 函数功能:获取记录条数(= filesize / SYSLOG_REC_SIZE,O(1))
|
||
* 入口参数:id - 日志 ID syslog_id_t
|
||
* 返回值:条数;文件缺失/未挂载返回 0 uint32_t
|
||
*/
|
||
uint32_t syslog_count(syslog_id_t id);
|
||
|
||
/*
|
||
* 函数功能:读取第 idx 条记录(时间戳 + 描述,均 NUL 结尾)
|
||
* 入口参数:id - 日志 ID syslog_id_t
|
||
* idx - 记录序号(0 起) uint32_t
|
||
* p_ts - 时间戳输出缓冲(不可为 NULL) char *
|
||
* ts_len- p_ts 容量,必须 > SYSLOG_TS_LEN(即 ≥ SYSLOG_TS_LEN+1 = 20 字节) size_t
|
||
* p_desc- 描述输出缓冲(不可为 NULL) char *
|
||
* desc_len - p_desc 容量,必须 > SYSLOG_DESC_MAX(即 ≥ 29 字节) size_t
|
||
* 返回值:0 - 成功;-1 - 越界 / 读失败 / **缓冲容量不足** int
|
||
* 限定条件:容量不足一律返回 -1,**不做静默截断、也不写调用方一个字节**
|
||
* (C 项整改 2026-10-24;旧注释只写"≥…+1 字节"像是"给少了会被截断",
|
||
* 照它给 28 字节缓冲的结果是整条读不到而不是读到 27 字符)。
|
||
* 描述字段在写入侧已截到 SYSLOG_DESC_MAX,所以按上面的最小容量给缓冲
|
||
* 就一定装得下,读侧不需要第二道截断。
|
||
* MCU 后端与模拟器后端(sim_syslog.c)在这条守卫上取值相同,行为一致;
|
||
* 模拟器侧那份同样不可达的截断支还留在文件里(未列入本轮改动范围)。
|
||
* 调用方:uiTask 的日志页(行缓冲 32B)与 SYSLOG 取证打印。
|
||
*/
|
||
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);
|
||
|
||
/*
|
||
* 函数功能:读取末条记录的 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);
|
||
|
||
/*
|
||
* MCU 后端专属 API(sys_log_mcu.c;模拟器端无对应实现,勿在共享代码调用)
|
||
*/
|
||
|
||
/*
|
||
* 函数功能:日志引擎初始化(创建 FreeRTOS 消息队列)
|
||
* 入口参数:无
|
||
* 返回值:0 - 成功;-1 - 队列创建失败 int
|
||
* 限定条件:app_main_init() 起始处调用,先于任何 syslog_post()
|
||
*/
|
||
int syslog_mcu_init(void);
|
||
|
||
/*
|
||
* 函数功能:卷挂载成功后的一次性处理(建 /log 目录 + 写"上电/重启"分类记录)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:defaultTask f_mount/f_mkfs 成功且置位 SYS_EVT_STORAGE_READY 前调用一次
|
||
*/
|
||
void syslog_mcu_mounted(void);
|
||
|
||
/*
|
||
* 函数功能:排空待发日志并落盘(启动前数组 + 运行期队列,FIFO)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:仅 defaultTask(唯一写者)调用;卷未就绪时直接返回(条目滞留待重试)
|
||
*/
|
||
void syslog_drain(void);
|
||
|
||
/*
|
||
* 函数功能:把一条取证行追加到 /log/wdev.log(掉电不丢;变长行,不走 64B 定长格式)
|
||
* 入口参数:p_desc - 取证描述行(调用方已格式化;不可为 NULL) const char *
|
||
* 返回值:0 - 成功;-1 - 卷占用或写失败(证据可能回滚,**不阻断判定**) int
|
||
* 限定条件:poller(defaultTask)上下文调用;卷锁一律 try(绝不阻塞,
|
||
* 本上下文 = 看门狗喂狗点)
|
||
* 函数说明:第 7 轮取证通道(设计见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录:
|
||
* 误复位后 RAM 态随复位消失、stall 行经僵尸串口丢失,故证据必须落盘)。
|
||
* 内部在释放卷锁后立刻追加一条 s_append(SYSLOG_RUN, ...) 写另一个
|
||
* 文件——FTL 单页缓存语义为"写到不同 LPN 才把前一脏页刷进 NAND",
|
||
* 没有该 flush 诱导,本行证据会在约 1.7s 后的 IWDG 复位中回滚丢失。
|
||
* 文件 > 8KB 时先 unlink 重建,防长跑无界增长。
|
||
*/
|
||
int sys_log_evidence(const char *p_desc);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* SYS_LOG_H */
|