ecode.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. /*
  2. * Copyright (c) 2026, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2026-03-06 user first version
  9. * 2026-04-16 user add power-loss protection, fix index wrap, reduce RAM
  10. */
  11. #define DBG_TAG "ecode"
  12. #define DBG_LVL DBG_INFO
  13. #include <rtdbg.h>
  14. #include <rthw.h>
  15. #include <fal.h>
  16. #include <string.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include "ecode.h"
  20. /* 分区名称 */
  21. #define ECODE_PART_NAME "ecode"
  22. /* NOR Flash 扇区大小 */
  23. #define SECTOR_SIZE 4096
  24. /* 每条记录固定为64字节 */
  25. #define RECORD_SIZE sizeof(ecode_record_t)
  26. /* 每扇区记录数 */
  27. #define RECORDS_PER_SECTOR (SECTOR_SIZE / RECORD_SIZE)
  28. _Static_assert(sizeof(ecode_record_t) == 64, "ecode_record_t size must be 64 bytes");
  29. /* 头部结构 */
  30. typedef struct
  31. {
  32. uint32_t magic;
  33. uint32_t version;
  34. uint32_t write_seq;
  35. } ecode_header_t;
  36. /* 缓存扇区尾部信息(位于扇区末尾16字节) */
  37. #define CACHE_MAGIC_VALID 0xA5A5A5A5
  38. #define CACHE_TAIL_OFFSET (SECTOR_SIZE - 16)
  39. typedef struct __attribute__((packed))
  40. {
  41. uint32_t magic;
  42. uint32_t target_sector;
  43. uint32_t new_write_seq;
  44. uint32_t crc32;
  45. } cache_tail_t;
  46. static const struct fal_partition *ecode_part = RT_NULL;
  47. static uint32_t max_sectors = 0; // 数据扇区总数(不含头部和缓存扇区)
  48. static rt_mutex_t ecode_lock = RT_NULL;
  49. static rt_bool_t initialized = RT_FALSE;
  50. static rt_mutex_t init_mutex = RT_NULL;
  51. /* 全局唯一缓冲区(受 ecode_lock 保护),用于所有 Flash 扇区操作 */
  52. static uint8_t g_buf[SECTOR_SIZE];
  53. extern struct rtc_time time_now;
  54. /* 清理detail字符串,确保只包含可打印字符(保留中文UTF-8),遇垃圾数据或无效UTF-8序列截断 */
  55. static void sanitize_detail(char *detail, size_t size)
  56. {
  57. if (size == 0)
  58. return;
  59. detail[size - 1] = '\0'; /* 确保结尾有终止符 */
  60. for (size_t i = 0; i < size - 1; i++)
  61. {
  62. unsigned char c = detail[i];
  63. if (c == 0x00)
  64. break; /* 正常结束 */
  65. if (c == 0xFF) /* Flash擦除状态,视为未写入数据 */
  66. {
  67. detail[i] = '\0';
  68. break;
  69. }
  70. /* 控制字符(除\t \n \r外)也截断 */
  71. if (c < 0x20 && c != '\t' && c != '\n' && c != '\r')
  72. {
  73. detail[i] = '\0';
  74. break;
  75. }
  76. /* UTF-8多字节序列验证 - 防止截断的UTF-8字符导致乱码 */
  77. if (c >= 0x80)
  78. {
  79. int bytes_needed = 0;
  80. /* 确定需要的字节数 */
  81. if ((c & 0xE0) == 0xC0)
  82. bytes_needed = 2; /* 2字节序列: 110xxxxx */
  83. else if ((c & 0xF0) == 0xE0)
  84. bytes_needed = 3; /* 3字节序列: 1110xxxx */
  85. else if ((c & 0xF8) == 0xF0)
  86. bytes_needed = 4; /* 4字节序列: 11110xxx */
  87. else /* 无效的UTF-8首字节 */
  88. {
  89. detail[i] = '\0';
  90. break;
  91. }
  92. /* 检查后续字节是否足够 */
  93. if (i + bytes_needed >= size)
  94. {
  95. detail[i] = '\0';
  96. break;
  97. }
  98. /* 验证后续字节是否符合UTF-8格式(必须是10xxxxxx) */
  99. int valid = 1;
  100. for (int j = 1; j < bytes_needed; j++)
  101. {
  102. if ((detail[i + j] & 0xC0) != 0x80) /* 必须以10开头 */
  103. {
  104. valid = 0;
  105. break;
  106. }
  107. }
  108. if (!valid)
  109. {
  110. detail[i] = '\0';
  111. break;
  112. }
  113. /* 跳过已验证的多字节字符 */
  114. i += bytes_needed - 1;
  115. }
  116. }
  117. }
  118. /* CRC32 计算 */
  119. static uint32_t crc32_calc(const uint8_t *data, size_t len)
  120. {
  121. uint32_t crc = 0xFFFFFFFF;
  122. for (size_t i = 0; i < len; i++)
  123. {
  124. crc ^= data[i];
  125. for (int j = 0; j < 8; j++)
  126. {
  127. if (crc & 1)
  128. crc = (crc >> 1) ^ 0xEDB88320;
  129. else
  130. crc >>= 1;
  131. }
  132. }
  133. return ~crc;
  134. }
  135. /* 获取有效记录数 */
  136. static uint32_t get_valid_count(uint32_t write_seq)
  137. {
  138. uint32_t max_records = max_sectors * RECORDS_PER_SECTOR;
  139. return (write_seq < max_records) ? write_seq : max_records;
  140. }
  141. /* 读取头部 */
  142. static int read_header(ecode_header_t *hdr)
  143. {
  144. if (fal_partition_read(ecode_part, 0, (uint8_t *)hdr, sizeof(ecode_header_t)) < 0)
  145. {
  146. LOG_E("read header failed");
  147. return -RT_ERROR;
  148. }
  149. return RT_EOK;
  150. }
  151. /* 写入头部 */
  152. static int write_header(const ecode_header_t *hdr)
  153. {
  154. int ret = RT_EOK;
  155. if (fal_partition_read(ecode_part, 0, g_buf, SECTOR_SIZE) < 0)
  156. {
  157. LOG_E("read header sector failed");
  158. return -RT_ERROR;
  159. }
  160. memcpy(g_buf, hdr, sizeof(ecode_header_t));
  161. if (fal_partition_erase(ecode_part, 0, SECTOR_SIZE) < 0)
  162. {
  163. LOG_E("erase header sector failed");
  164. return -RT_ERROR;
  165. }
  166. if (fal_partition_write(ecode_part, 0, g_buf, SECTOR_SIZE) != SECTOR_SIZE)
  167. {
  168. LOG_E("write header sector failed");
  169. ret = -RT_ERROR;
  170. }
  171. return ret;
  172. }
  173. /* 擦除整个分区 */
  174. static int erase_whole_partition(void)
  175. {
  176. if (fal_partition_erase(ecode_part, 0, ecode_part->len) < 0)
  177. {
  178. LOG_E("erase whole partition failed");
  179. return -RT_ERROR;
  180. }
  181. return RT_EOK;
  182. }
  183. /* 获取缓存扇区地址 */
  184. static uint32_t get_cache_sector_addr(void)
  185. {
  186. return ecode_part->len - SECTOR_SIZE;
  187. }
  188. /* 从缓存扇区恢复未完成写入 */
  189. static void recover_from_cache(void)
  190. {
  191. uint32_t cache_addr = get_cache_sector_addr();
  192. cache_tail_t tail;
  193. uint32_t calc_crc;
  194. if (fal_partition_read(ecode_part, cache_addr, g_buf, SECTOR_SIZE) < 0)
  195. {
  196. LOG_E("read cache sector failed");
  197. return;
  198. }
  199. /* 读取尾部信息 */
  200. memcpy(&tail, g_buf + CACHE_TAIL_OFFSET, sizeof(cache_tail_t));
  201. if (tail.magic != CACHE_MAGIC_VALID)
  202. {
  203. LOG_D("no valid cache found");
  204. return;
  205. }
  206. /* 计算数据部分 CRC */
  207. calc_crc = crc32_calc(g_buf, CACHE_TAIL_OFFSET);
  208. if (calc_crc != tail.crc32)
  209. {
  210. LOG_W("cache sector CRC mismatch, discard");
  211. fal_partition_erase(ecode_part, cache_addr, SECTOR_SIZE);
  212. return;
  213. }
  214. /* 校验目标扇区地址 */
  215. if (tail.target_sector < SECTOR_SIZE ||
  216. tail.target_sector >= cache_addr ||
  217. (tail.target_sector - SECTOR_SIZE) % SECTOR_SIZE != 0)
  218. {
  219. LOG_E("invalid target sector 0x%x in cache", tail.target_sector);
  220. fal_partition_erase(ecode_part, cache_addr, SECTOR_SIZE);
  221. return;
  222. }
  223. LOG_I("found incomplete write, recovering target sector 0x%x", tail.target_sector);
  224. /* 恢复目标扇区 */
  225. if (fal_partition_erase(ecode_part, tail.target_sector, SECTOR_SIZE) < 0)
  226. {
  227. LOG_E("erase target sector failed during recovery");
  228. return;
  229. }
  230. if (fal_partition_write(ecode_part, tail.target_sector, g_buf, SECTOR_SIZE) != SECTOR_SIZE)
  231. {
  232. LOG_E("write target sector failed during recovery");
  233. return;
  234. }
  235. /* 更新头部 */
  236. ecode_header_t hdr;
  237. if (read_header(&hdr) == RT_EOK)
  238. {
  239. if (tail.new_write_seq > hdr.write_seq)
  240. {
  241. hdr.write_seq = tail.new_write_seq;
  242. if (write_header(&hdr) != RT_EOK)
  243. {
  244. LOG_E("update header after recovery failed");
  245. }
  246. }
  247. }
  248. /* 清除缓存扇区 */
  249. fal_partition_erase(ecode_part, cache_addr, SECTOR_SIZE);
  250. LOG_I("recovery completed");
  251. }
  252. /* 核心初始化 */
  253. static int ecode_init_core(void)
  254. {
  255. ecode_header_t hdr;
  256. uint32_t part_size;
  257. ecode_part = fal_partition_find(ECODE_PART_NAME);
  258. if (ecode_part == RT_NULL)
  259. {
  260. LOG_E("partition '%s' not found", ECODE_PART_NAME);
  261. return -RT_ERROR;
  262. }
  263. part_size = ecode_part->len;
  264. LOG_D("partition size: %u bytes", part_size);
  265. if (part_size < SECTOR_SIZE * 3)
  266. {
  267. LOG_E("partition too small (need at least %d bytes)", SECTOR_SIZE * 3);
  268. return -RT_ERROR;
  269. }
  270. max_sectors = (part_size / SECTOR_SIZE) - 2; // 头部扇区 + 缓存扇区
  271. LOG_D("max_sectors=%u, records per sector=%u", max_sectors, RECORDS_PER_SECTOR);
  272. ecode_lock = rt_mutex_create("ecode", RT_IPC_FLAG_FIFO);
  273. if (ecode_lock == RT_NULL)
  274. {
  275. LOG_E("create mutex failed");
  276. return -RT_ERROR;
  277. }
  278. /* 尝试恢复未完成写入 */
  279. recover_from_cache();
  280. if (read_header(&hdr) == RT_EOK && hdr.magic == ECODE_MAGIC && hdr.version == ECODE_VERSION)
  281. {
  282. LOG_D("ecode already initialized, write_seq=%u", hdr.write_seq);
  283. return RT_EOK;
  284. }
  285. LOG_W("partition not initialized or invalid, erasing...");
  286. if (erase_whole_partition() != RT_EOK)
  287. {
  288. LOG_E("erase failed");
  289. return -RT_ERROR;
  290. }
  291. hdr.magic = ECODE_MAGIC;
  292. hdr.version = ECODE_VERSION;
  293. hdr.write_seq = 0;
  294. if (write_header(&hdr) != RT_EOK)
  295. {
  296. LOG_E("write header failed");
  297. return -RT_ERROR;
  298. }
  299. LOG_I("ecode initialized, max_sectors=%d", max_sectors);
  300. return RT_EOK;
  301. }
  302. static int ensure_initialized(void)
  303. {
  304. if (initialized)
  305. return RT_EOK;
  306. if (init_mutex == RT_NULL)
  307. {
  308. init_mutex = rt_mutex_create("ecode_init", RT_IPC_FLAG_FIFO);
  309. if (init_mutex == RT_NULL)
  310. {
  311. LOG_E("create init mutex failed");
  312. return -RT_ERROR;
  313. }
  314. }
  315. rt_mutex_take(init_mutex, RT_WAITING_FOREVER);
  316. if (!initialized)
  317. {
  318. if (ecode_init_core() == RT_EOK)
  319. initialized = RT_TRUE;
  320. else
  321. LOG_E("ecode_init_core failed");
  322. }
  323. rt_mutex_release(init_mutex);
  324. return initialized ? RT_EOK : -RT_ERROR;
  325. }
  326. int ecode_init(void)
  327. {
  328. return ensure_initialized();
  329. }
  330. /**
  331. * @brief 记录异常事件
  332. *
  333. * @param code 事件码
  334. * @param para 参数码
  335. * @param detail 描述
  336. */
  337. void Recode_e_code(ecode_list code, uint16_t para, char *detail)
  338. {
  339. ecode_header_t hdr;
  340. ecode_record_t rec;
  341. uint32_t seq, sector_index, record_offset_in_sector;
  342. uint32_t sector_start;
  343. uint32_t cache_addr;
  344. cache_tail_t tail;
  345. if (ensure_initialized() != RT_EOK)
  346. {
  347. LOG_E("ecode not initialized, abort write");
  348. return;
  349. }
  350. rt_mutex_take(ecode_lock, RT_WAITING_FOREVER);
  351. if (read_header(&hdr) != RT_EOK)
  352. {
  353. LOG_E("read header failed");
  354. rt_mutex_release(ecode_lock);
  355. return;
  356. }
  357. if (hdr.magic != ECODE_MAGIC || hdr.version != ECODE_VERSION)
  358. {
  359. LOG_E("header corrupted");
  360. rt_mutex_release(ecode_lock);
  361. return;
  362. }
  363. seq = hdr.write_seq;
  364. /* 构建记录 */
  365. rec.time_stamp.tm_year = time_now.year - 2000;
  366. rec.time_stamp.tm_mon = time_now.month;
  367. rec.time_stamp.tm_mday = time_now.date;
  368. rec.time_stamp.tm_hour = time_now.hour;
  369. rec.time_stamp.tm_min = time_now.minute;
  370. rec.time_stamp.tm_sec = time_now.second;
  371. rec.code = (uint16_t)code;
  372. rec.para = para;
  373. rec.num = seq;
  374. rt_strncpy(rec.detail, detail, sizeof(rec.detail) - 1);
  375. rec.detail[sizeof(rec.detail) - 1] = '\0';
  376. /* 计算物理位置(修正循环逻辑) */
  377. sector_index = (seq / RECORDS_PER_SECTOR) % max_sectors;
  378. sector_start = SECTOR_SIZE + sector_index * SECTOR_SIZE;
  379. record_offset_in_sector = (seq % RECORDS_PER_SECTOR) * RECORD_SIZE;
  380. LOG_D("write: seq=%u sector_index=%u sector_start=0x%x offset=%u",
  381. seq, sector_index, sector_start, record_offset_in_sector);
  382. /* 读取目标扇区到 g_buf */
  383. if (fal_partition_read(ecode_part, sector_start, g_buf, SECTOR_SIZE) < 0)
  384. {
  385. LOG_E("read target sector failed");
  386. rt_mutex_release(ecode_lock);
  387. return;
  388. }
  389. /* 修改对应记录 */
  390. memcpy(g_buf + record_offset_in_sector, &rec, RECORD_SIZE);
  391. /* 准备缓存扇区尾部信息 */
  392. cache_addr = get_cache_sector_addr();
  393. tail.magic = CACHE_MAGIC_VALID;
  394. tail.target_sector = sector_start;
  395. tail.new_write_seq = seq + 1;
  396. tail.crc32 = crc32_calc(g_buf, CACHE_TAIL_OFFSET);
  397. /* 将尾部信息填入 g_buf 末尾 */
  398. memcpy(g_buf + CACHE_TAIL_OFFSET, &tail, sizeof(cache_tail_t));
  399. /* 步骤1:写入缓存扇区 */
  400. if (fal_partition_erase(ecode_part, cache_addr, SECTOR_SIZE) < 0)
  401. {
  402. LOG_E("erase cache sector failed");
  403. rt_mutex_release(ecode_lock);
  404. return;
  405. }
  406. if (fal_partition_write(ecode_part, cache_addr, g_buf, SECTOR_SIZE) != SECTOR_SIZE)
  407. {
  408. LOG_E("write cache sector failed");
  409. rt_mutex_release(ecode_lock);
  410. return;
  411. }
  412. /* 步骤2:擦除目标扇区 */
  413. if (fal_partition_erase(ecode_part, sector_start, SECTOR_SIZE) < 0)
  414. {
  415. LOG_E("erase target sector failed");
  416. /* 尝试清除缓存扇区 */
  417. fal_partition_erase(ecode_part, cache_addr, SECTOR_SIZE);
  418. rt_mutex_release(ecode_lock);
  419. return;
  420. }
  421. /* 步骤3:写回目标扇区(注意此时 g_buf 末尾仍带有缓存信息,但不影响数据部分) */
  422. if (fal_partition_write(ecode_part, sector_start, g_buf, SECTOR_SIZE) != SECTOR_SIZE)
  423. {
  424. LOG_E("write target sector failed, data may be lost!");
  425. /* 缓存扇区仍有效,下次上电可恢复 */
  426. rt_mutex_release(ecode_lock);
  427. return;
  428. }
  429. /* 步骤4:更新头部 */
  430. hdr.write_seq = seq + 1;
  431. if (write_header(&hdr) != RT_EOK)
  432. {
  433. LOG_E("write header failed, but data written");
  434. }
  435. /* 步骤5:清除缓存扇区 */
  436. if (fal_partition_erase(ecode_part, cache_addr, SECTOR_SIZE) < 0)
  437. {
  438. LOG_W("clear cache sector failed");
  439. }
  440. LOG_W("e_code SET 0 c %d p %d d %s", code, para, detail);
  441. uint32_t total_records = max_sectors * RECORDS_PER_SECTOR;
  442. uint32_t used_records = hdr.write_seq;
  443. if (used_records > total_records)
  444. used_records = total_records;
  445. uint32_t used_bytes = used_records * RECORD_SIZE;
  446. LOG_I("e_code used %d cnt %d/%d", used_bytes, used_records, total_records);
  447. rt_mutex_release(ecode_lock);
  448. }
  449. /* 读取记录 */
  450. int ecode_read(uint32_t index, ecode_record_t *rec)
  451. {
  452. ecode_header_t hdr;
  453. uint32_t valid_count;
  454. uint32_t logical_seq, sector_index, record_offset_in_sector;
  455. uint32_t sector_start;
  456. if (ensure_initialized() != RT_EOK)
  457. return -RT_ERROR;
  458. rt_mutex_take(ecode_lock, RT_WAITING_FOREVER);
  459. if (read_header(&hdr) != RT_EOK)
  460. {
  461. rt_mutex_release(ecode_lock);
  462. return -RT_ERROR;
  463. }
  464. if (hdr.magic != ECODE_MAGIC || hdr.version != ECODE_VERSION)
  465. {
  466. LOG_E("header corrupted");
  467. rt_mutex_release(ecode_lock);
  468. return -RT_ERROR;
  469. }
  470. valid_count = get_valid_count(hdr.write_seq);
  471. if (index >= valid_count)
  472. {
  473. rt_mutex_release(ecode_lock);
  474. return -RT_ERROR;
  475. }
  476. logical_seq = hdr.write_seq - 1 - index;
  477. sector_index = (logical_seq / RECORDS_PER_SECTOR) % max_sectors;
  478. sector_start = SECTOR_SIZE + sector_index * SECTOR_SIZE;
  479. record_offset_in_sector = (logical_seq % RECORDS_PER_SECTOR) * RECORD_SIZE;
  480. LOG_D("read: index=%u logical_seq=%u sector_start=0x%x offset=%u",
  481. index, logical_seq, sector_start, record_offset_in_sector);
  482. if (fal_partition_read(ecode_part, sector_start, g_buf, SECTOR_SIZE) < 0)
  483. {
  484. LOG_E("read sector failed");
  485. rt_mutex_release(ecode_lock);
  486. return -RT_ERROR;
  487. }
  488. memcpy(rec, g_buf + record_offset_in_sector, RECORD_SIZE);
  489. sanitize_detail(rec->detail, sizeof(rec->detail)); // 新增这一行
  490. /* 检查是否为全0xFF */
  491. uint8_t *p = (uint8_t *)rec;
  492. int all_ff = 1;
  493. for (uint32_t i = 0; i < RECORD_SIZE; i++)
  494. {
  495. if (p[i] != 0xFF)
  496. {
  497. all_ff = 0;
  498. break;
  499. }
  500. }
  501. if (all_ff)
  502. {
  503. LOG_W("record at index %d is all 0xFF (empty)", index);
  504. rt_mutex_release(ecode_lock);
  505. return -RT_ERROR;
  506. }
  507. if (rec->num > hdr.write_seq)
  508. {
  509. LOG_W("record at index %d has invalid num %u", index, rec->num);
  510. rt_mutex_release(ecode_lock);
  511. return -RT_ERROR;
  512. }
  513. rt_mutex_release(ecode_lock);
  514. return RT_EOK;
  515. }
  516. uint32_t ecode_get_count(void)
  517. {
  518. ecode_header_t hdr;
  519. uint32_t count;
  520. if (ensure_initialized() != RT_EOK)
  521. return 0;
  522. rt_mutex_take(ecode_lock, RT_WAITING_FOREVER);
  523. if (read_header(&hdr) != RT_EOK)
  524. {
  525. rt_mutex_release(ecode_lock);
  526. return 0;
  527. }
  528. if (hdr.magic != ECODE_MAGIC || hdr.version != ECODE_VERSION)
  529. {
  530. rt_mutex_release(ecode_lock);
  531. return 0;
  532. }
  533. count = get_valid_count(hdr.write_seq);
  534. rt_mutex_release(ecode_lock);
  535. return count;
  536. }
  537. void ecode_clear(void)
  538. {
  539. ecode_header_t hdr;
  540. if (ensure_initialized() != RT_EOK)
  541. {
  542. LOG_E("ecode not initialized");
  543. return;
  544. }
  545. rt_mutex_take(ecode_lock, RT_WAITING_FOREVER);
  546. if (erase_whole_partition() != RT_EOK)
  547. {
  548. LOG_E("erase whole partition failed");
  549. rt_mutex_release(ecode_lock);
  550. return;
  551. }
  552. hdr.magic = ECODE_MAGIC;
  553. hdr.version = ECODE_VERSION;
  554. hdr.write_seq = 0;
  555. if (write_header(&hdr) != RT_EOK)
  556. {
  557. LOG_E("write header failed after clear");
  558. }
  559. else
  560. {
  561. LOG_I("ecode cleared");
  562. }
  563. rt_mutex_release(ecode_lock);
  564. }
  565. /*====================== MSH 命令(保持不变)======================*/
  566. #include <finsh.h>
  567. static void recode_test(void)
  568. {
  569. Recode_e_code(e_test_fun, 6, "3-6 is a test record 123456789");
  570. }
  571. MSH_CMD_EXPORT(recode_test, write a test ecode);
  572. static void pre(void)
  573. {
  574. ecode_record_t rec;
  575. uint32_t cnt = ecode_get_count();
  576. LOG_I("ecode count: %u", cnt);
  577. for (uint32_t i = 0; i < cnt; i++)
  578. {
  579. if (ecode_read(i, &rec) == 0)
  580. {
  581. LOG_I("%u - %02u/%02u/%02u %02u:%02u:%02u: (%5u-%-5u) %s",
  582. rec.num,
  583. rec.time_stamp.tm_year, rec.time_stamp.tm_mon, rec.time_stamp.tm_mday,
  584. rec.time_stamp.tm_hour, rec.time_stamp.tm_min, rec.time_stamp.tm_sec,
  585. rec.code, rec.para, rec.detail);
  586. }
  587. else
  588. {
  589. LOG_I("read error at index %u", i);
  590. }
  591. }
  592. }
  593. MSH_CMD_EXPORT(pre, print all ecode records from newest to oldest);
  594. static void delecode(void)
  595. {
  596. ecode_clear();
  597. }
  598. MSH_CMD_EXPORT(delecode, clear all ecode records);
  599. static void ecode_test(int argc, char **argv)
  600. {
  601. ecode_record_t rec;
  602. uint32_t cnt_before, cnt_after;
  603. int i, ret;
  604. LOG_I("===== ecode module test start =====");
  605. ecode_clear();
  606. cnt_before = ecode_get_count();
  607. LOG_I("after clear, count=%u", cnt_before);
  608. if (cnt_before != 0)
  609. {
  610. LOG_E("clear failed");
  611. return;
  612. }
  613. for (i = 0; i < 10; i++)
  614. {
  615. char detail[64];
  616. rt_snprintf(detail, sizeof(detail), "test data %d", i);
  617. Recode_e_code(e_test_fun, i, detail);
  618. }
  619. cnt_after = ecode_get_count();
  620. LOG_I("after write 10, count=%u", cnt_after);
  621. if (cnt_after != 10)
  622. {
  623. LOG_E("write count mismatch");
  624. return;
  625. }
  626. for (i = 0; i < 10; i++)
  627. {
  628. ret = ecode_read(i, &rec);
  629. if (ret != 0)
  630. {
  631. LOG_E("read index %d failed", i);
  632. return;
  633. }
  634. LOG_I("read[%d]: num=%u time=%02u/%02u/%02u %02u:%02u:%02u code=%u para=%u detail=%s",
  635. i, rec.num,
  636. rec.time_stamp.tm_year, rec.time_stamp.tm_mon, rec.time_stamp.tm_mday,
  637. rec.time_stamp.tm_hour, rec.time_stamp.tm_min, rec.time_stamp.tm_sec,
  638. rec.code, rec.para, rec.detail);
  639. if (rec.num != (uint32_t)(9 - i))
  640. {
  641. LOG_E("num mismatch: expected %d, got %u", 9 - i, rec.num);
  642. }
  643. if (rec.code != e_test_fun || rec.para != (uint16_t)(9 - i))
  644. {
  645. LOG_E("code/para mismatch");
  646. }
  647. }
  648. ret = ecode_read(10, &rec);
  649. if (ret == 0)
  650. {
  651. LOG_E("read beyond count should fail");
  652. }
  653. else
  654. {
  655. LOG_I("read beyond count failed as expected");
  656. }
  657. ecode_clear();
  658. cnt_after = ecode_get_count();
  659. if (cnt_after != 0)
  660. {
  661. LOG_E("clear after test failed");
  662. }
  663. else
  664. {
  665. LOG_I("clear ok");
  666. }
  667. LOG_I("===== ecode module test passed =====");
  668. }
  669. MSH_CMD_EXPORT(ecode_test, run ecode module self - test);