kv_flash.c 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. /* USER CODE BEGIN Header */
  2. /**@Doxygen风格注释
  3. ******************************************************************************
  4. * @file : kv_flash.c
  5. * @brief :
  6. ******************************************************************************
  7. * @attention
  8. *
  9. *
  10. *
  11. *
  12. *
  13. *
  14. *
  15. *
  16. ******************************************************************************
  17. */
  18. /* USER CODE END Header */
  19. /* Private includes ----------------------------------------------------------*/
  20. /* USER CODE BEGIN Includes */
  21. #include "kv_flash.h"
  22. #include "uType.h"
  23. #include "Printf.h"
  24. #include <string.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include "cmsis_os.h"
  28. #include <inttypes.h> // PRIu32 等宏
  29. #include <ctype.h> // for isprint
  30. #include <stddef.h> // offsetof
  31. /* USER CODE END Includes */
  32. /* Exported types ------------------------------------------------------------*/
  33. /* USER CODE BEGIN ET */
  34. #define KV_ROOT(root, name, type) { #name, offsetof(root, name), type, sizeof(((root*)0)->name), 0 }
  35. #define KV_WORK(root, name, type) { #name, offsetof(root, name), type, sizeof(((root*)0)->name), 1 }
  36. extern IWDG_HandleTypeDef hiwdg;
  37. size_t init_index = 0;
  38. static char db_cache[3][DB_FLASH_SIZE]; // 每个数据库的缓存
  39. static uint16_t db_cache_len[3]; // 每个数据库内的缓存长度
  40. /**@BEGIN ------------ 映射表 -------------------*/
  41. static const uint32_t DB_ADDR[DB_ID_MAX] = {
  42. [DB_ID_SYSTEM] = 0x0803C000,
  43. [DB_ID_CALIB] = 0x0803C800,
  44. [DB_ID_SNAPSHOT] = 0x0803D000,
  45. };
  46. static const InitEntry init_table[] = {
  47. // SystemConfig
  48. {DB_ID_SYSTEM, KV_KEY_MAC "=001800000000"},
  49. {DB_ID_SYSTEM, KV_KEY_ROLE "=1"},
  50. {DB_ID_SYSTEM, KV_KEY_CONSOLE "=1"},
  51. {DB_ID_SYSTEM, KV_KEY_ADDR "=255"},
  52. {DB_ID_SYSTEM, KV_KEY_BAUDRATE "=2"},
  53. {DB_ID_SYSTEM, KV_KEY_VERSION "=0007C128"},
  54. // CalibrationParams
  55. {DB_ID_CALIB, KV_KEY_ROD_VOL0 "=2.637"},
  56. {DB_ID_CALIB, KV_KEY_ROD_VOL1 "=2.905"},
  57. {DB_ID_CALIB, KV_KEY_ROD_VOL2 "=3.165"},
  58. {DB_ID_CALIB, KV_KEY_ROD_VOL3 "=3.504"},
  59. {DB_ID_CALIB, KV_KEY_ROD_VOL4 "=3.863"},
  60. {DB_ID_CALIB, KV_KEY_ROD_VOL5 "=4.125"},
  61. {DB_ID_CALIB, KV_KEY_ROD_VOL6 "=4.402"},
  62. {DB_ID_CALIB, KV_KEY_OIL_CAP0 "=1.86"},
  63. {DB_ID_CALIB, KV_KEY_OIL_CAP1 "=2.01"},
  64. {DB_ID_CALIB, KV_KEY_OIL_CAP2 "=2.14"},
  65. {DB_ID_CALIB, KV_KEY_OIL_CAP3 "=2.35"},
  66. {DB_ID_CALIB, KV_KEY_OIL_CAP4 "=3.05"},
  67. {DB_ID_CALIB, KV_KEY_OIL_CAP5 "=3.12"},
  68. {DB_ID_CALIB, KV_KEY_OIL_CAP6 "=3.22"},
  69. {DB_ID_CALIB, KV_KEY_OIL_CAP7 "=3.39"},
  70. {DB_ID_CALIB, KV_KEY_OIL_CAP8 "=3.65"},
  71. {DB_ID_CALIB, KV_KEY_OIL_CAP9 "=3.84"},
  72. {DB_ID_CALIB, KV_KEY_OIL_CAP10 "=3.98"},
  73. {DB_ID_CALIB, KV_KEY_RES_VOL0 "=2.713"},
  74. {DB_ID_CALIB, KV_KEY_RES_VOL1 "=2.739"},
  75. {DB_ID_CALIB, KV_KEY_RES_VOL2 "=2.766"},
  76. {DB_ID_CALIB, KV_KEY_RES_VOL3 "=2.788"},
  77. {DB_ID_CALIB, KV_KEY_RES_VOL4 "=2.821"},
  78. {DB_ID_CALIB, KV_KEY_RES_VOL5 "=2.855"},
  79. {DB_ID_CALIB, KV_KEY_RES_CAP0 "=2.678"},
  80. {DB_ID_CALIB, KV_KEY_RES_CAP1 "=2.356"},
  81. {DB_ID_CALIB, KV_KEY_RES_CAP2 "=1.863"},
  82. {DB_ID_CALIB, KV_KEY_RES_CAP3 "=1.545"},
  83. {DB_ID_CALIB, KV_KEY_RES_CAP4 "=9.87"},
  84. {DB_ID_CALIB, KV_KEY_RES_CAP5 "=3.98"},
  85. {DB_ID_CALIB, KV_KEY_ROD_VOL_OFFSET "=0.008"},
  86. {DB_ID_CALIB, KV_KEY_ROD_DIALOG "=10"},
  87. {DB_ID_CALIB, KV_KEY_ROD_COL_TIME "=10"},
  88. {DB_ID_CALIB, KV_KEY_OIL_PARA_INDU "=18"},
  89. {DB_ID_CALIB, KV_KEY_OIL_PARA_CAP "=33"},
  90. {DB_ID_CALIB, KV_KEY_OIL_CAP_OFFSET "=0"},
  91. {DB_ID_CALIB, KV_KEY_OIL_DIALOG "=10"},
  92. {DB_ID_CALIB, KV_KEY_OIL_COL_TIME "=10"},
  93. {DB_ID_CALIB, KV_KEY_RES_PARA_INDU "=18"},
  94. {DB_ID_CALIB, KV_KEY_RES_PARA_CAP "=33"},
  95. {DB_ID_CALIB, KV_KEY_RES_CAP_OFFSET "=0"},
  96. {DB_ID_CALIB, KV_KEY_RES_VOL_OFFSET "=0.008"},
  97. {DB_ID_CALIB, KV_KEY_RES_CAP_DIALOG "=10"},
  98. {DB_ID_CALIB, KV_KEY_RES_VOL_DIALOG "=10"},
  99. {DB_ID_CALIB, KV_KEY_RES_CAP_COL_TIME "=10"},
  100. {DB_ID_CALIB, KV_KEY_RES_VOL_COL_TIME "=10"},
  101. {DB_ID_CALIB, KV_KEY_TEMP_VOL_OFFSET "=0.008"},
  102. {DB_ID_CALIB, KV_KEY_TEMP_DIALOG "=10"},
  103. {DB_ID_CALIB, KV_KEY_TEMP_COL_TIME "=10"},
  104. // SnapshotInfo
  105. {DB_ID_SNAPSHOT, KV_KEY_IAP_LOAD "=0"},
  106. {DB_ID_SNAPSHOT, KV_KEY_IAP_SIZE "=40960"},
  107. {DB_ID_SNAPSHOT, KV_KEY_IAP_MD5 "=00000000"},
  108. {DB_ID_SNAPSHOT, KV_KEY_UPDATE_OK "=2"},
  109. {DB_ID_SNAPSHOT, KV_KEY_UPDATE_ERR "=2"},
  110. {DB_ID_SNAPSHOT, KV_KEY_UBOOTBACK "=400"}
  111. };
  112. typedef enum {
  113. FIELD_INT,
  114. FIELD_UINT8,
  115. FIELD_UINT16,
  116. FIELD_UINT32,
  117. FIELD_FLOAT,
  118. FIELD_STRING
  119. } FieldType;
  120. typedef struct {
  121. const char *key; // KV 名
  122. size_t offset; // 在 JsRoot/J sRoot.work 中的偏移
  123. FieldType type; // 字段类型
  124. size_t size; // 字段大小(字符串用)
  125. uint8_t in_work; // 0 表示在 JsRoot,1 表示在 JsRoot.work
  126. } KvFieldMap;
  127. const KvFieldMap kv_map[] = {
  128. // ===== JsRoot =====
  129. {"mac", offsetof(JsonDat_Root, mac), FIELD_STRING, sizeof(JsRoot.mac), 0},
  130. {"role", offsetof(JsonDat_Root, role), FIELD_INT, sizeof(JsRoot.role), 0},
  131. {"console", offsetof(JsonDat_Root, console), FIELD_INT, sizeof(JsRoot.console), 0},
  132. {"addr", offsetof(JsonDat_Root, addr), FIELD_UINT8, sizeof(JsRoot.addr), 0},
  133. {"baudrate", offsetof(JsonDat_Root, baudrate), FIELD_INT, sizeof(JsRoot.baudrate), 0},
  134. {"version", offsetof(JsonDat_Root, vers_id), FIELD_UINT32, sizeof(JsRoot.vers_id), 0},
  135. {"iapLoadStatus", offsetof(JsonDat_Root, iapLoadStatus), FIELD_UINT32, sizeof(JsRoot.iapLoadStatus), 0},
  136. {"iapsize", offsetof(JsonDat_Root, iapSize), FIELD_INT, sizeof(JsRoot.iapSize), 0},
  137. {"iapmd5", offsetof(JsonDat_Root, iapMd5), FIELD_STRING, sizeof(JsRoot.iapMd5), 0},
  138. {"subcode", offsetof(JsonDat_Root, subcode), FIELD_UINT16, sizeof(JsRoot.subcode), 0},
  139. {"update_ok", offsetof(JsonDat_Root, update_ok), FIELD_INT, sizeof(JsRoot.update_ok), 0},
  140. {"update_err",offsetof(JsonDat_Root, update_err),FIELD_INT, sizeof(JsRoot.update_err), 0},
  141. {"ubootback", offsetof(JsonDat_Root, ubootback), FIELD_INT, sizeof(JsRoot.ubootback), 0},
  142. // ===== JsWork =====
  143. {"rod_vol0", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[0]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[0]), 1},
  144. {"rod_vol1", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[1]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[1]), 1},
  145. {"rod_vol2", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[2]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[2]), 1},
  146. {"rod_vol3", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[3]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[3]), 1},
  147. {"rod_vol4", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[4]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[4]), 1},
  148. {"rod_vol5", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[5]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[5]), 1},
  149. {"rod_vol6", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[6]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[6]), 1},
  150. {"oil_cap0", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[0]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[0]), 1},
  151. {"oil_cap1", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[1]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[1]), 1},
  152. {"oil_cap2", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[2]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[2]), 1},
  153. {"oil_cap3", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[3]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[3]), 1},
  154. {"oil_cap4", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[4]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[4]), 1},
  155. {"oil_cap5", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[5]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[5]), 1},
  156. {"oil_cap6", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[6]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[6]), 1},
  157. {"oil_cap7", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[7]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[7]), 1},
  158. {"oil_cap8", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[8]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[8]), 1},
  159. {"oil_cap9", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[9]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[9]), 1},
  160. {"oil_cap10", offsetof(JsonDat_Work, calib_oil_water) + offsetof(CalibPoints_t, cap_values[10]), FIELD_FLOAT, sizeof(JsWork.calib_oil_water.cap_values[10]), 1},
  161. {"res_vol0", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, vol_values[0]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.vol_values[0]), 1},
  162. {"res_vol1", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, vol_values[1]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.vol_values[1]), 1},
  163. {"res_vol2", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, vol_values[2]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.vol_values[2]), 1},
  164. {"res_vol3", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, vol_values[3]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.vol_values[3]), 1},
  165. {"res_vol4", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, vol_values[4]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.vol_values[4]), 1},
  166. {"res_vol5", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, vol_values[5]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.vol_values[5]), 1},
  167. {"res_cap0", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, cap_values[0]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.cap_values[0]), 1},
  168. {"res_cap1", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, cap_values[1]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.cap_values[1]), 1},
  169. {"res_cap2", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, cap_values[2]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.cap_values[2]), 1},
  170. {"res_cap3", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, cap_values[3]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.cap_values[3]), 1},
  171. {"res_cap4", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, cap_values[4]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.cap_values[4]), 1},
  172. {"res_cap5", offsetof(JsonDat_Work, calib_residual_oil) + offsetof(Calib_residual_oil_t, cap_values[5]), FIELD_FLOAT, sizeof(JsWork.calib_residual_oil.cap_values[5]), 1},
  173. {"rod_vol_offset", offsetof(JsonDat_Work, rod_vol_offset), FIELD_FLOAT, sizeof(JsWork.rod_vol_offset), 1},
  174. {"rod_dialog", offsetof(JsonDat_Work, rod_dialog), FIELD_FLOAT, sizeof(JsWork.rod_dialog), 1},
  175. {"rod_coltime", offsetof(JsonDat_Work, rod_coltime), FIELD_UINT16, sizeof(JsWork.rod_coltime), 1},
  176. {"oil_para_indu", offsetof(JsonDat_Work, oil_para_indu), FIELD_FLOAT, sizeof(JsWork.oil_para_indu), 1},
  177. {"oil_para_cap", offsetof(JsonDat_Work, oil_para_cap), FIELD_FLOAT, sizeof(JsWork.oil_para_cap), 1},
  178. {"oil_cap_offset", offsetof(JsonDat_Work, oil_cap_offset), FIELD_FLOAT, sizeof(JsWork.oil_cap_offset), 1},
  179. {"oil_dialog", offsetof(JsonDat_Work, oil_dialog), FIELD_FLOAT, sizeof(JsWork.oil_dialog), 1},
  180. {"rod_oil_coltime", offsetof(JsonDat_Work, rod_oil_coltime), FIELD_UINT16, sizeof(JsWork.rod_oil_coltime), 1},
  181. {"res_para_indu", offsetof(JsonDat_Work, res_para_indu), FIELD_FLOAT, sizeof(JsWork.res_para_indu), 1},
  182. {"res_para_cap", offsetof(JsonDat_Work, res_para_cap), FIELD_FLOAT, sizeof(JsWork.res_para_cap), 1},
  183. {"res_cap_offset", offsetof(JsonDat_Work, res_cap_offset), FIELD_FLOAT, sizeof(JsWork.res_cap_offset), 1},
  184. {"res_vol_offset", offsetof(JsonDat_Work, res_vol_offset), FIELD_FLOAT, sizeof(JsWork.res_vol_offset), 1},
  185. {"res_cap_dialog", offsetof(JsonDat_Work, res_cap_dialog), FIELD_FLOAT, sizeof(JsWork.res_cap_dialog), 1},
  186. {"res_vol_dialog", offsetof(JsonDat_Work, res_vol_dialog), FIELD_FLOAT, sizeof(JsWork.res_vol_dialog), 1},
  187. {"res_oil_cap_coltime", offsetof(JsonDat_Work, res_oil_cap_coltime), FIELD_UINT16, sizeof(JsWork.res_oil_cap_coltime), 1},
  188. {"res_oil_vol_coltime", offsetof(JsonDat_Work, res_oil_vol_coltime), FIELD_UINT16, sizeof(JsWork.res_oil_vol_coltime), 1},
  189. {"temp_vol_offset",offsetof(JsonDat_Work, temp_vol_offset), FIELD_FLOAT, sizeof(JsWork.temp_vol_offset), 1},
  190. {"temp_dialog", offsetof(JsonDat_Work, temp_dialog), FIELD_FLOAT, sizeof(JsWork.temp_dialog), 1},
  191. {"temp_coltime", offsetof(JsonDat_Work, temp_coltime), FIELD_UINT16, sizeof(JsWork.temp_coltime), 1},
  192. };
  193. /**@END ---映射表 */
  194. /* USER CODE END ET */
  195. /* Exported functions prototypes ---------------------------------------------*/
  196. /**@BEGIN ------------ 小工具函数 -------------------*/
  197. static uint8_t is_valid_db_id(uint8_t id) { // 数据库编号是否过界
  198. return id < DB_ID_MAX;
  199. }
  200. size_t get_db_used_len(uint8_t db_id){ // 倒着查找该数据库里写入了多少数据
  201. uint32_t addr = DB_ADDR[db_id];
  202. const uint8_t *p = (const uint8_t *)addr;
  203. for (int i = 2047; i >= 0; i--) {
  204. if (p[i] != 0xFF) {
  205. return i + 1;
  206. }
  207. }
  208. return 0; // 全空
  209. }
  210. static inline float str_to_float(const char *s) { // char转float
  211. return (float)strtof(s, NULL);
  212. }
  213. const char* memmem_custom(const char *haystack, size_t haylen,const char *needle, size_t neelen) {
  214. if (!haystack || !needle || neelen == 0 || haylen < neelen) return NULL;
  215. for (size_t i = 0; i <= haylen - neelen; i++) {
  216. if (memcmp(haystack + i, needle, neelen) == 0)
  217. return haystack + i;
  218. }
  219. return NULL;
  220. }
  221. /**@END --- 小工具函数 */
  222. /**@BEGIN ------------ FLASH擦除写入函数 -------------------*/
  223. static uint8_t flash_erase_page(uint32_t addr) { // 擦除指定数据库区域(2KB) 20~40ms
  224. __disable_irq(); // 关闭全局中断
  225. HAL_IWDG_Refresh(&hiwdg);
  226. HAL_FLASH_Unlock();
  227. FLASH_EraseInitTypeDef erase;
  228. uint32_t PageError = 0;
  229. erase.TypeErase = FLASH_TYPEERASE_PAGES;
  230. erase.Page = (addr - FLASH_BASE) / DB_FLASH_PAGE_SIZE;
  231. erase.NbPages = 1;
  232. erase.Banks = FLASH_BANK_1;
  233. if (HAL_FLASHEx_Erase(&erase, &PageError) != HAL_OK) {
  234. HAL_FLASH_Lock();
  235. return 0;
  236. }
  237. HAL_FLASH_Lock();
  238. HAL_IWDG_Refresh(&hiwdg);
  239. __enable_irq(); // 开启全局中断
  240. return 1;
  241. }
  242. uint8_t DB_Save(uint8_t id, const char *buf, uint16_t len) { // 写入Flash(整个数据库内容)
  243. if (!is_valid_db_id(id) || buf == NULL || len > DB_FLASH_SIZE) {
  244. printf("DB_Save par error\n");
  245. return 0;
  246. }
  247. uint32_t addr = DB_ADDR[id];
  248. // Step1: 擦除整页
  249. if (!flash_erase_page(addr)) {
  250. printf("DB_Save: flash_erase fail 0x%08X\n", addr);
  251. return 0;
  252. }
  253. // Step2: 写入字符串数据(按64位字节写入)
  254. HAL_FLASH_Unlock();
  255. for (uint32_t i = 0; i < len; i += 8) {
  256. uint64_t word64 = 0xFFFFFFFFFFFFFFFFULL;
  257. memcpy(&word64, &buf[i], (len - i >= 8) ? 8 : (len - i));
  258. if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr + i, word64) != HAL_OK) {
  259. KV_PRINTF("DB_Save: flash_write fail 0x%08X\n", addr + i);
  260. HAL_FLASH_Lock();
  261. return 0;
  262. }
  263. }
  264. HAL_FLASH_Lock();
  265. KV_PRINTF("DB_Save: flash_write succ id=%d, len=%d\n", id, len);
  266. return 1;
  267. }
  268. /**@END --- FLASH擦除写入函数 */
  269. /**@BEGIN ------------ 键值对处理函数 -------------------*/
  270. uint8_t SaveKvCache(uint8_t id, const char *kv_str){ // 支持有则更新,无则新增,值为空则删除
  271. static char db_buf[DB_FLASH_SIZE];
  272. static char new_buf[DB_FLASH_SIZE];
  273. if (!is_valid_db_id(id) || kv_str == NULL || strlen(kv_str) >= KV_ITEM_MAX_LEN) {
  274. printf("[KV] Invalid DB ID or kv_str too long/null\n");
  275. return 0;
  276. }
  277. char key[KV_MAX_KEY_LEN + 1] = {0};
  278. const char *eq = strchr(kv_str, '=');
  279. if (!eq || eq - kv_str > KV_MAX_KEY_LEN) {
  280. printf("[KV] '=' not found or key too long in '%s'\n", kv_str);
  281. return 0;
  282. }
  283. strncpy(key, kv_str, eq - kv_str);
  284. key[eq - kv_str] = '\0';
  285. KV_PRINTF("[KV] Processing key='%s'\n", key);
  286. memset(db_buf, 0, DB_FLASH_SIZE);
  287. uint16_t old_len = DB_Dump(id, db_buf, DB_FLASH_SIZE); // 从 flash 对应 DB 区域读取数据到 db_buf
  288. KV_PRINTF("[KV] Existing DB length: %u bytes\n", old_len);
  289. memset(new_buf, 0, DB_FLASH_SIZE);
  290. int found = 0; // 是否找到相同 key
  291. const char *pos = db_buf;
  292. while ((pos = strstr(pos, "##")) != NULL) {
  293. const char *kv = pos + 2;
  294. const char *next = strstr(kv, "##");
  295. size_t len = next ? (size_t)(next - kv) : strlen(kv);
  296. char temp[KV_ITEM_MAX_LEN] = {0};
  297. strncpy(temp, kv, len);
  298. temp[len] = '\0';
  299. const char *eq_in = strchr(temp, '=');
  300. if (eq_in) {
  301. char key_in[KV_MAX_KEY_LEN + 1] = {0};
  302. strncpy(key_in, temp, eq_in - temp);
  303. key_in[eq_in - temp] = '\0';
  304. if (strcmp(key_in, key) == 0) {
  305. // 找到相同 key
  306. found = 1;
  307. if (strlen(eq + 1) > 0) { // 找到相同 key , 更新值或删除
  308. // 更新:保持位置不变
  309. strcat(new_buf, "##");
  310. strcat(new_buf, kv_str);
  311. KV_PRINTF("[KV] Updating key in place: %s\n", kv_str);
  312. } else {
  313. // 删除:跳过这个 key
  314. KV_PRINTF("[KV] Deleting key: %s\n", key_in);
  315. }
  316. } else {
  317. // 不是目标 key,原样拷贝
  318. strcat(new_buf, "##");
  319. strcat(new_buf, temp); // 找不到相同 key , 复制到 new_buf
  320. }
  321. }
  322. if (!next) break;
  323. pos = next;
  324. }
  325. // 如果没找到,且新值不为空 → 追加到末尾
  326. if (!found && strlen(eq + 1) > 0) {
  327. strcat(new_buf, "##");
  328. strcat(new_buf, kv_str);
  329. KV_PRINTF("[KV] Adding new kv at end: %s\n", kv_str);
  330. }
  331. strcat(new_buf, "##"); // 结尾
  332. KV_PRINTF("[KV] Final DB content (len=%u):\n%s\n", (unsigned int)strlen(new_buf), new_buf);
  333. uint8_t result = DB_Save(id, new_buf, strlen(new_buf)); // 把更新后的数据存回 flash
  334. if (result) {
  335. // KV_PRINTF("[KV] Flash save success\n");
  336. } else {
  337. // printf("[KV] Flash save failed\n");
  338. }
  339. return result;
  340. }
  341. void handle_cfg_set_common(uint8_t db_id, const char *key, const char *value){ // 通用处理函数 删除 更新 新增
  342. if (!is_valid_db_id(db_id)) {
  343. printf("error=invalid db id, rlt=411\r\n");
  344. return;
  345. }
  346. if (strlen(value) == 0) {
  347. // value 为空,删除 key
  348. KV_HandleDelete(db_id, key);
  349. return;
  350. }
  351. char tmp[64] = {0};
  352. if (pickup_string_from_key(db_id, key, tmp, sizeof(tmp))) {
  353. // key 存在 → 更新
  354. KV_HandleUpdate(db_id, key, value);
  355. } else {
  356. // key 不存在 → 新建
  357. KV_HandleInsert(db_id, key, value);
  358. }
  359. }
  360. void KV_HandleQuery(uint8_t db_id, const char* key){ // 读取
  361. if (!is_valid_db_id(db_id) || key == NULL) {
  362. printf("error=invalid db id or null key, rlt=450\r\n");
  363. return;
  364. }
  365. char val[KV_MAX_VAL_LEN] = {0};
  366. if (pickup_string_from_key(db_id, key, val, sizeof(val))) {
  367. printf("fileid=%d, [%s=%s], ok\r\n", db_id, key, val);
  368. } else {
  369. printf("rlt=404, error=not exist\r\n");
  370. }
  371. }
  372. void KV_HandleUpdate(uint8_t db_id, const char* key, const char* value){ // 更新
  373. if (!is_valid_db_id(db_id) || key == NULL || value == NULL) {
  374. printf("error=invalid params, rlt=451\r\n");
  375. return;
  376. }
  377. char kv_buf[KV_ITEM_MAX_LEN] = {0};
  378. snprintf(kv_buf, sizeof(kv_buf), "%s=%s", key, value);
  379. uint8_t ok = SaveKvCache(db_id, kv_buf);
  380. if (ok) {
  381. printf("ok\r\n");
  382. } else {
  383. printf("error=update fail, rlt=452\r\n");
  384. }
  385. }
  386. void KV_HandleDelete(uint8_t db_id, const char* key){ // 删除
  387. if (!is_valid_db_id(db_id) || key == NULL) {
  388. printf("error=invalid params, rlt=453\r\n");
  389. return;
  390. }
  391. char kv_buf[KV_ITEM_MAX_LEN] = {0};
  392. char value[1] = {0}; // 空串表示删除
  393. snprintf(kv_buf, sizeof(kv_buf), "%s=%s", key, value);
  394. uint8_t ok = SaveKvCache(db_id, kv_buf);
  395. if (ok) {
  396. printf("ok\r\n");
  397. } else {
  398. printf("error=delete fail, rlt=454\r\n");
  399. }
  400. }
  401. void KV_HandleInsert(uint8_t db_id, const char* key, const char* value){ // 新增
  402. if (!is_valid_db_id(db_id) || key == NULL || value == NULL) {
  403. printf("error=invalid params, rlt=455\r\n");
  404. return;
  405. }
  406. char tmp[KV_MAX_VAL_LEN] = {0};
  407. if (pickup_string_from_key(db_id, key, tmp, sizeof(tmp))) {
  408. printf("error=exists, rlt=456\r\n");
  409. return;
  410. }
  411. char kv_buf[KV_ITEM_MAX_LEN] = {0};
  412. snprintf(kv_buf, sizeof(kv_buf), "%s=%s", key, value);
  413. uint8_t ok = SaveKvCache(db_id, kv_buf);
  414. if (ok) {
  415. printf("ok\r\n");
  416. } else {
  417. printf("error=insert fail, rlt=457\r\n");
  418. }
  419. }
  420. void DB_SaveUInt(uint8_t dbId, const char *key, uint32_t value){ // 将数值存入 KV 数据库
  421. char kv_str[KV_MAX_VAL_LEN]; // 足够存 "key=value"
  422. snprintf(kv_str, sizeof(kv_str), "%s=%" PRIu32, key, value);
  423. SaveKvCache(dbId, kv_str);
  424. }
  425. void DB_SaveChar(uint8_t db_id, const char *key, const char *value){
  426. char buf[128];
  427. snprintf(buf, sizeof(buf), "%s=%s", key, value);
  428. SaveKvCache(db_id, buf);
  429. }
  430. void DB_SaveInt(uint8_t dbId, const char *key, int32_t value){
  431. char kv_str[KV_MAX_VAL_LEN];
  432. snprintf(kv_str, sizeof(kv_str), "%s=%" PRId32, key, value);
  433. SaveKvCache(dbId, kv_str);
  434. }
  435. void DB_SaveFloat(uint8_t dbId, const char *key, float value){
  436. char kv_str[KV_MAX_VAL_LEN];
  437. snprintf(kv_str, sizeof(kv_str), "%s=%.6f", key, value); // 保留 6 位小数
  438. SaveKvCache(dbId, kv_str);
  439. }
  440. /**@END --- 键值对处理函数 */
  441. /**@BEGIN ---------- 提取数据库数据函数 ---------------------------*/
  442. static int extract_kv(const char *src, const char *key, char *val_out, uint8_t max_len) { // 查找"##"分隔符,在分隔符之间查找提取指定key对应的value
  443. const char *pos = strstr(src, "##");
  444. while (pos) {
  445. const char *kv = pos + 2;
  446. const char *eq = strchr(kv, '=');
  447. const char *end = strstr(kv, "##");
  448. KV_PRINTF("[extract_kv] Searching key '%s' from segment: %.32s\n", key, kv);
  449. if (eq && end && eq < end) {
  450. char key_buf[KV_MAX_KEY_LEN + 1] = {0};
  451. strncpy(key_buf, kv, eq - kv);
  452. key_buf[eq - kv] = '\0';
  453. KV_PRINTF("[extract_kv] Found key: '%s'\n", key_buf);
  454. if (strcmp(key_buf, key) == 0) {
  455. size_t val_len = end - eq - 1;
  456. if (val_len >= max_len) val_len = max_len - 1;
  457. strncpy(val_out, eq + 1, val_len);
  458. val_out[val_len] = '\0';
  459. KV_PRINTF("[extract_kv] Matched key! Value: '%s'\n", val_out);
  460. return 1;
  461. }
  462. }else {
  463. // printf("[extract_kv] No valid key=value between ##...\n");
  464. }
  465. pos = strstr(pos + 2, "##");
  466. }
  467. // printf("[extract_kv] Key '%s' not found.\n", key);
  468. return 0;
  469. }
  470. uint8_t pickup_string_from_key(uint8_t id, const char *key, char *val_out, uint8_t max_len) { // 从指定数据库ID的键值存储中提取字符串值
  471. if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
  472. printf("[pickup_string_from_key] Invalid input\n");
  473. return 0;
  474. }
  475. static char db_buf[DB_FLASH_SIZE];
  476. memset(db_buf, 0, sizeof(db_buf));
  477. uint16_t len = DB_Dump(id, db_buf, DB_FLASH_SIZE);
  478. KV_PRINTF("[pickup_string_from_key] Dumped len: %u\n", len);
  479. uint8_t res = extract_kv(db_buf, key, val_out, max_len);
  480. return res;
  481. }
  482. uint8_t pickup_int_from_key(uint8_t id, const char *key, int *val_out) { // 从指定数据库ID的键值存储中提取整数值
  483. if (!is_valid_db_id(id) || key == NULL || val_out == NULL)
  484. return 0;
  485. char val_str[KV_MAX_VAL_LEN] = {0};
  486. if (pickup_string_from_key(id, key, val_str, sizeof(val_str))) {
  487. *val_out = atoi(val_str);
  488. return 1;
  489. }
  490. return 0;
  491. }
  492. uint8_t pickup_uint32_from_key(uint8_t id, const char *key, uint32_t *val_out) { // 从数据库提取 uint32_t 类型
  493. if (!is_valid_db_id(id) || key == NULL || val_out == NULL)
  494. return 0;
  495. char val_str[KV_MAX_VAL_LEN] = {0};
  496. if (pickup_string_from_key(id, key, val_str, sizeof(val_str))) {
  497. *val_out = (uint32_t)strtoul(val_str, NULL, 10);
  498. return 1;
  499. }
  500. return 0;
  501. }
  502. uint8_t pickup_long_from_key(uint8_t id, const char *key, long *val_out) { // 从数据库提取 long 类型
  503. if (!is_valid_db_id(id) || key == NULL || val_out == NULL)
  504. return 0;
  505. char val_str[KV_MAX_VAL_LEN] = {0};
  506. if (pickup_string_from_key(id, key, val_str, sizeof(val_str))) {
  507. *val_out = strtol(val_str, NULL, 10);
  508. return 1;
  509. }
  510. return 0;
  511. }
  512. /**@END --- 提取数据库数据函数 */
  513. /**@BEGIN ------------ 控制台命令 一层入口函数 -------------------*/
  514. void handle_mac_set_cmd(const char *buf){ // 处理 ###::{"mac":"112233445566"} 设置设备SN号也就是MAC号
  515. uint8_t db_id = 0; // 固定数据库 0
  516. const char *prefix = "{\"mac\":\"";
  517. size_t prefix_len = strlen(prefix);
  518. // 必须匹配前缀
  519. if (strncmp(buf, prefix, prefix_len) != 0) {
  520. printf("error=invalid mac cmd\r\n");
  521. return;
  522. }
  523. // 找到结束的引号
  524. const char *end = strchr(buf + prefix_len, '"');
  525. if (!end) {
  526. printf("error=invalid mac format\r\n");
  527. return;
  528. }
  529. // 提取 value
  530. char value[128] = {0};
  531. size_t vlen = end - (buf + prefix_len);
  532. if (vlen >= sizeof(value)) {
  533. printf("error=mac too long\r\n");
  534. return;
  535. }
  536. strncpy(value, buf + prefix_len, vlen);
  537. value[vlen] = '\0';
  538. handle_cfg_set_common(db_id, "mac", value); // 更新数据库
  539. strncpy(JsRoot.mac, value, sizeof(JsRoot.mac) - 1); // 同步更新结构体
  540. JsRoot.mac[sizeof(JsRoot.mac) - 1] = '\0'; // 确保结尾有 '\0'
  541. }
  542. void handle_cfg_set_cmd(const char *buf){ // 处理 --cfg_set=${key1}=${val1} 系统配置-KV设置 专门处理数据库0的键值对
  543. uint8_t db_id = 0;
  544. char key[KV_MAX_VAL_LEN];
  545. char value[128];
  546. if (parse_cfg_set_cmd(buf, &db_id, key, sizeof(key), value, sizeof(value)) != 0) {
  547. printf("error=invalid cfg_set format, rlt=410\r\n");
  548. return;
  549. }
  550. handle_cfg_set_common(db_id, key, value); // 更新数据库
  551. sync_JsRoot_field(key, value); // 同步更新 JsRoot 对应字段
  552. }
  553. void handle_cfg_setx_cmd(const char *buf){ // 处理 --cfg_setx=X,${key}=${val} 通用配置-KV设置
  554. uint8_t db_id;
  555. char key[KV_MAX_VAL_LEN];
  556. char value[128];
  557. if (parse_cfg_setx_cmd(buf, &db_id, key, sizeof(key), value, sizeof(value)) != 0) {
  558. printf("error=invalid cfg_setx format, rlt=410\r\n");
  559. return;
  560. }
  561. handle_cfg_set_common(db_id, key, value); // 更新数据库
  562. sync_JsRoot_field(key, value); // 同步更新 JsRoot 对应字段
  563. }
  564. void handle_cfg_get_cmd(const char *buf){ // 处理 --cfg_get=${key} 读取键值内容
  565. const char *prefix = "--cfg_get=";
  566. size_t prefix_len = strlen(prefix);
  567. if (!buf || strncmp(buf, prefix, prefix_len) != 0) {
  568. printf("error=invalid cfg_get format, rlt=420\r\n");
  569. return;
  570. }
  571. const char *key = buf + prefix_len;
  572. if (strlen(key) == 0) {
  573. printf("error=empty key, rlt=421\r\n");
  574. return;
  575. }
  576. char val[128] = {0};
  577. for (uint8_t db_id = 0; db_id < DB_ID_MAX; db_id++) {
  578. if (pickup_string_from_key(db_id, key, val, sizeof(val))) {
  579. printf("fileid=%d, [%s=%s], ok\r\n", db_id, key, val);
  580. return; // 找到就退出
  581. }
  582. }
  583. // 遍历完还没找到
  584. printf("rlt=404, error=not exist.\r\n");
  585. }
  586. void handle_fileid_cmd(const char *buf){ // 处理 --fileid=X,0 读取库存信息
  587. const char *prefix = "--fileid=";
  588. size_t prefix_len = strlen(prefix);
  589. if (!buf || strncmp(buf, prefix, prefix_len) != 0) {
  590. printf("error=invalid fileid format, rlt=430\r\n");
  591. return;
  592. }
  593. // 解析 X,0
  594. uint8_t db_id = 0;
  595. int offset = 0;
  596. if (sscanf(buf + prefix_len, "%hhu,%d", &db_id, &offset) != 2) {
  597. printf("error=invalid fileid params, rlt=431\r\n");
  598. return;
  599. }
  600. if (!is_valid_db_id(db_id)) {
  601. printf("error=invalid db id, rlt=432\r\n");
  602. return;
  603. }
  604. // 读取数据库内容
  605. uint32_t addr = DB_ADDR[db_id];
  606. const uint8_t *db_addr = (const uint8_t *)addr;
  607. size_t db_len = get_db_used_len(db_id);
  608. if (!db_addr || db_len == 0) {
  609. printf("error=empty db, rlt=433\r\n");
  610. return;
  611. }
  612. // 打印头信息
  613. printf("DB_DUMP, fileid=%d, len=%u,\r\n", db_id, (unsigned)db_len);
  614. printf("addr=0x%08X+%d,content=\r\n", (unsigned int)db_addr, offset);
  615. // 数据库内容是以##key=value##key2=value2## 存储
  616. printf("[");
  617. for (size_t i = 0; i < db_len; i++) {
  618. char c = db_addr[i];
  619. if (c >= 32 && c <= 126) {
  620. putchar(c);
  621. }
  622. }
  623. printf("], ok\r\n");
  624. }
  625. void handle_clear_fileid_cmd(const char *buf){ // 处理 --clear_fileid=X 清除内部配置库
  626. const char *prefix = "--clear_fileid=";
  627. size_t prefix_len = strlen(prefix);
  628. if (!buf || strncmp(buf, prefix, prefix_len) != 0) {
  629. printf("error=invalid clear_fileid format, rlt=440\r\n");
  630. return;
  631. }
  632. uint8_t db_id = (uint8_t)atoi(buf + prefix_len);
  633. if (!is_valid_db_id(db_id)) {
  634. printf("error=invalid db id, rlt=441\r\n");
  635. return;
  636. }
  637. uint32_t addr = DB_ADDR[db_id];
  638. if (flash_erase_page(addr)) {
  639. printf("ok\r\n");
  640. } else {
  641. printf("error=erase fail, rlt=442\r\n");
  642. }
  643. }
  644. /**@END --- 控制台命令 一层入口函数 */
  645. /**@BEGIN ------------ 控制台命令 二层提取函数---------*/
  646. int parse_cfg_set_cmd(const char *buf, uint8_t *db_id, char *key, size_t key_sz, char *val, size_t val_sz){ // --cfg_set (固定 db=0) 提取 key、value---------
  647. if (!buf || !db_id || !key || !val) return -1;
  648. const char *prefix = "--cfg_set=";
  649. size_t prefix_len = strlen(prefix);
  650. if (strncmp(buf, prefix, prefix_len) != 0) return -1;
  651. *db_id = 0; // 固定 0 号库
  652. const char *kv_str = buf + prefix_len;
  653. char *eq = strchr(kv_str, '=');
  654. if (!eq) return -1;
  655. // 提取 key
  656. size_t klen = eq - kv_str;
  657. if (klen >= key_sz) return -1;
  658. strncpy(key, kv_str, klen);
  659. key[klen] = '\0';
  660. // 提取 value(可能为空串)
  661. const char *val_str = eq + 1;
  662. if (strlen(val_str) >= val_sz) return -1;
  663. strcpy(val, val_str);
  664. return 0;
  665. }
  666. int parse_cfg_setx_cmd(const char *buf, uint8_t *db_id, char *key, size_t key_sz, char *val, size_t val_sz){ // --cfg_setx 提取 db、key、value--------------
  667. if (!buf || !db_id || !key || !val) return -1;
  668. // 格式: --cfg_setx=X,key=value
  669. // 先找到 '=' 号
  670. const char *prefix = "--cfg_setx=";
  671. size_t prefix_len = strlen(prefix);
  672. if (strncmp(buf, prefix, prefix_len) != 0) return -1;
  673. // 解析 DB ID
  674. const char *p = buf + prefix_len;
  675. char *comma = strchr(p, ',');
  676. if (!comma) return -1;
  677. *comma = '\0';
  678. *db_id = (uint8_t)atoi(p);
  679. *comma = ','; // 还原
  680. // 解析 key 和 value
  681. const char *kv_str = comma + 1; // 指向 key=value
  682. char *eq = strchr(kv_str, '=');
  683. if (!eq) return -1;
  684. // 提取 key
  685. size_t klen = eq - kv_str;
  686. if (klen >= key_sz) return -1;
  687. strncpy(key, kv_str, klen);
  688. key[klen] = '\0';
  689. // 提取 value(可能为空串)
  690. const char *val_str = eq + 1;
  691. if (strlen(val_str) >= val_sz) return -1;
  692. strcpy(val, val_str);
  693. return 0;
  694. }
  695. /**@END --- 控制台命令 二层提取函数 */
  696. /**@BEGIN --------------- 初始化函数 ------------------*/
  697. void DB_InitAllConfigs(void) { // 初始化数据库
  698. size_t total = sizeof(init_table) / sizeof(init_table[0]);
  699. for (size_t i = 0; i < total; i++) {
  700. const char *kv_str = init_table[i].kv_str;
  701. char key_name[KV_MAX_KEY_LEN + 1] = {0};
  702. const char *eq = strchr(kv_str, '=');
  703. if (eq) {
  704. strncpy(key_name, kv_str, eq - kv_str);
  705. key_name[eq - kv_str] = '\0';
  706. char existing_val[KV_MAX_VAL_LEN] = {0};
  707. uint8_t exists = pickup_string_from_key(init_table[i].db_id,
  708. key_name,
  709. existing_val,
  710. sizeof(existing_val));
  711. if (!exists) {
  712. if (!SaveKvCache(init_table[i].db_id, kv_str)) {
  713. // printf("SaveKvCache fail: %s\n", kv_str);
  714. } else {
  715. // printf("SaveKvCache succ: %s\n", kv_str);
  716. }
  717. }
  718. }
  719. HAL_IWDG_Refresh(&hiwdg); // 喂狗,防止初始化太久被复位
  720. osDelay(1);
  721. }
  722. JsRoot.vers_id = CURRENT_FIRMWARE_VERSION;
  723. DB_SaveUInt(DB_ID_SYSTEM, KV_KEY_VERSION , JsRoot.vers_id ); // 存入 VERSION 到 SYSTEM 数据库
  724. printf("DB_InitAllConfigs complete.\n");
  725. osDelay(100);
  726. DB_Dump_to_JsRoot();
  727. printf("DB_Dump_to_JsRoot ok \r\n");
  728. }
  729. uint16_t DB_Dump(uint8_t id, char *buf, uint16_t buf_len) { // 从Flash读取区域内容到缓存
  730. if (!is_valid_db_id(id)) {
  731. printf("[DB_Dump] Invalid DB ID: %d\n", id);
  732. return 0;
  733. }
  734. if (buf == NULL) {
  735. printf("[DB_Dump] NULL buffer pointer\n");
  736. return 0;
  737. }
  738. if (buf_len < DB_FLASH_SIZE) {
  739. printf("[DB_Dump] Buffer too small: buf_len = %d, required = %d\n", buf_len, DB_FLASH_SIZE);
  740. return 0;
  741. }
  742. uint32_t addr = DB_ADDR[id];
  743. const uint8_t *flash_ptr = (const uint8_t *)addr;
  744. // printf("[DB_Dump] Start reading from address: 0x%08X (DB_ID: %d)\n", addr, id);
  745. uint16_t i;
  746. for (i = 0; i < DB_FLASH_SIZE && i < buf_len - 1; i++) {
  747. buf[i] = flash_ptr[i];
  748. if (flash_ptr[i] == 0xFF) {
  749. // printf("[DB_Dump] Reached 0xFF at offset %d\n", i);
  750. break;
  751. }
  752. }
  753. buf[i] = '\0'; // null终止
  754. return i;
  755. }
  756. uint8_t pickup_string_from_cache(uint8_t id, const char *key, char *val_out, uint8_t max_len) { // 从缓存提取字符串
  757. if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
  758. return 0;
  759. }
  760. return extract_kv(db_cache[id], key, val_out, max_len);
  761. }
  762. uint8_t pickup_int_from_cache(uint8_t id, const char *key, int *val_out) { // 从缓存提取 int
  763. if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
  764. return 0;
  765. }
  766. char val_str[KV_MAX_VAL_LEN] = {0};
  767. if (pickup_string_from_cache(id, key, val_str, sizeof(val_str))) {
  768. *val_out = atoi(val_str);
  769. return 1;
  770. }
  771. return 0;
  772. }
  773. uint8_t pickup_uint32_from_cache(uint8_t id, const char *key, uint32_t *val_out) { // 从缓存提取 uint32_t
  774. if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
  775. return 0;
  776. }
  777. char val_str[KV_MAX_VAL_LEN] = {0};
  778. if (pickup_string_from_cache(id, key, val_str, sizeof(val_str))) {
  779. *val_out = (uint32_t)strtoul(val_str, NULL, 10);
  780. return 1;
  781. }
  782. return 0;
  783. }
  784. uint8_t pickup_long_from_cache(uint8_t id, const char *key, long *val_out) { // 从缓存提取 long
  785. if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
  786. return 0;
  787. }
  788. char val_str[KV_MAX_VAL_LEN] = {0};
  789. if (pickup_string_from_cache(id, key, val_str, sizeof(val_str))) {
  790. *val_out = strtol(val_str, NULL, 10);
  791. return 1;
  792. }
  793. return 0;
  794. }
  795. void DB_Dump_to_JsRoot(void) { // 从数据库同步到 缓存结构体 JsRoot
  796. DB_Load_All(); // 一次性加载所有数据库到缓存里
  797. DB_SyncAllToJsRoot(); // 使用缓存解析,不再访问 Flash
  798. }
  799. void DB_Load_All(void) { // 一次性读取所有数据库到 RAM
  800. for (uint8_t id = 0; id < 3; id++) {
  801. db_cache_len[id] = DB_Dump(id, db_cache[id], DB_FLASH_SIZE); // db_cache用来缓存每个库的数据
  802. }
  803. }
  804. void DB_SyncAllToJsRoot(void) {
  805. char key[64]; char val[128];
  806. if(JsRoot.work == NULL) JsRoot.work = &JsWork; // 自动赋值
  807. for (uint8_t id = 0; id < 3; id++) {
  808. const char *p = db_cache[id];
  809. const char *pend = p + db_cache_len[id];
  810. // printf(">>> Start parsing DB[%d], len=%u\n", id, db_cache_len[id]);
  811. while (p < pend) { // 找到 "##" 开头
  812. const char *start = memmem_custom(p, pend - p, "##", 2);
  813. if (!start) break;
  814. start += 2; // 跳过 ##
  815. const char *end = memmem_custom(start, pend - start, "##", 2);
  816. if (!end) end = pend; // 没找到结束标记
  817. // 在 start 到 end 之间找 '='
  818. const char *sep = memchr(start, '=', end - start);
  819. if (!sep) {
  820. // printf("DB[%d] stop: no '=' found at offset=%ld\n", id, start - db_cache[id]);
  821. p = end;
  822. continue;
  823. }
  824. size_t klen = sep - start;
  825. size_t vlen = end - (sep + 1);
  826. if (klen >= sizeof(key)) klen = sizeof(key) - 1;
  827. if (vlen >= sizeof(val)) vlen = sizeof(val) - 1;
  828. strncpy(key, start, klen); key[klen] = '\0';
  829. strncpy(val, sep + 1, vlen); val[vlen] = '\0';
  830. // printf("DB[%d] key=%s, val=%s, p_offset=%ld\n", id, key, val, start - db_cache[id]);
  831. sync_JsRoot_field(key, val);
  832. p = end; // 移动到下一个 "##" 开头或结尾
  833. HAL_IWDG_Refresh(&hiwdg); // 刷新看门狗
  834. }
  835. // printf("<<< End of DB[%d]\n\n", id);
  836. }
  837. }
  838. void sync_JsRoot_field(const char *key, const char *value) { // 查询更新某个缓存结构体参数
  839. for (size_t i = 0; i < sizeof(kv_map) / sizeof(kv_map[0]); i++) {
  840. if (strcmp(key, kv_map[i].key) == 0) {
  841. // uint8_t *base = kv_map[i].in_work ? (uint8_t*)&JsRoot.work : (uint8_t*)&JsRoot;
  842. uint8_t *base = kv_map[i].in_work ? (uint8_t*)JsRoot.work : (uint8_t*)&JsRoot;
  843. void *field_ptr = base + kv_map[i].offset;
  844. switch (kv_map[i].type) {
  845. case FIELD_STRING:
  846. snprintf((char*)field_ptr, kv_map[i].size, "%s", value);
  847. break;
  848. case FIELD_INT:
  849. *(int*)field_ptr = strtol(value, NULL, 10);
  850. break;
  851. case FIELD_UINT8:
  852. *(uint8_t*)field_ptr = (uint8_t)strtoul(value, NULL, 10);
  853. break;
  854. case FIELD_UINT16:
  855. *(uint16_t*)field_ptr = (uint16_t)strtoul(value, NULL, 10);
  856. break;
  857. case FIELD_UINT32:
  858. *(uint32_t*)field_ptr = strtoul(value, NULL, 0);
  859. break;
  860. case FIELD_FLOAT:
  861. *(float*)field_ptr = str_to_float(value);
  862. break;
  863. }
  864. HAL_IWDG_Refresh(&hiwdg); // 刷新看门狗
  865. return; // 找到并更新完成
  866. }
  867. }
  868. printf("sync_JsRoot_field: unknown key = %s\n", key);
  869. }
  870. void DB__Dump_IAP_LOAD(void){
  871. int iapload =0;
  872. pickup_int_from_key(2,KV_KEY_IAP_LOAD,&iapload);
  873. JsRoot.iapLoadStatus = iapload;
  874. printf("iapLoadStatus %d\r\n",JsRoot.iapLoadStatus);
  875. }
  876. /**@END --- 初始化函数 */
  877. /* USER CODE BEGIN EFP */