/* USER CODE BEGIN Header */
/**@Doxygen风格注释
  ******************************************************************************
  * @file           : kv_flash.c
  * @brief          : 
  ******************************************************************************
  * @attention
  *
  * 
  * 
  *
  * 
  * 
  * 
  *
  ******************************************************************************
  */
	
/* USER CODE END Header */
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "kv_flash.h"
#include "uType.h"
#include "Printf.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "cmsis_os.h"
#include <inttypes.h> //  PRIu32 等宏
#include <ctype.h> // for isprint
#include <stddef.h>  // offsetof

/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
#define KV_ROOT(root, name, type) { #name, offsetof(root, name), type, sizeof(((root*)0)->name), 0 }
#define KV_WORK(root, name, type) { #name, offsetof(root, name), type, sizeof(((root*)0)->name), 1 }

extern IWDG_HandleTypeDef hiwdg;
size_t init_index = 0;
static char db_cache[3][DB_FLASH_SIZE]; // 每个数据库的缓存
static uint16_t db_cache_len[3]; // 每个数据库内的缓存长度
/**@BEGIN ------------ 映射表 -------------------*/
static const uint32_t DB_ADDR[DB_ID_MAX] = {
    [DB_ID_SYSTEM]   = 0x0803C000,
    [DB_ID_CALIB]    = 0x0803C800,
    [DB_ID_SNAPSHOT] = 0x0803D000,
};

static const InitEntry init_table[] = {
    // SystemConfig
    {DB_ID_SYSTEM, KV_KEY_MAC "=001800000000"},
    {DB_ID_SYSTEM, KV_KEY_ROLE "=1"},
    {DB_ID_SYSTEM, KV_KEY_CONSOLE "=1"},
    {DB_ID_SYSTEM, KV_KEY_ADDR "=255"},
    {DB_ID_SYSTEM, KV_KEY_BAUDRATE "=2"},
    {DB_ID_SYSTEM, KV_KEY_VERSION "=0007C128"},
    
    // CalibrationParams
    {DB_ID_CALIB, KV_KEY_ROD_VOL0 "=2.637"},
    {DB_ID_CALIB, KV_KEY_ROD_VOL1 "=2.905"},
    {DB_ID_CALIB, KV_KEY_ROD_VOL2 "=3.165"},
    {DB_ID_CALIB, KV_KEY_ROD_VOL3 "=3.504"},
    {DB_ID_CALIB, KV_KEY_ROD_VOL4 "=3.863"},
    {DB_ID_CALIB, KV_KEY_ROD_VOL5 "=4.125"},
    {DB_ID_CALIB, KV_KEY_ROD_VOL6 "=4.402"},
    
    {DB_ID_CALIB, KV_KEY_OIL_CAP0 "=1.86"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP1 "=2.01"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP2 "=2.14"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP3 "=2.35"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP4 "=3.05"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP5 "=3.12"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP6 "=3.22"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP7 "=3.39"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP8 "=3.65"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP9 "=3.84"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP10 "=3.98"},
    
    {DB_ID_CALIB, KV_KEY_RES_VOL0 "=2.713"},
    {DB_ID_CALIB, KV_KEY_RES_VOL1 "=2.739"},
    {DB_ID_CALIB, KV_KEY_RES_VOL2 "=2.766"},
    {DB_ID_CALIB, KV_KEY_RES_VOL3 "=2.788"},
    {DB_ID_CALIB, KV_KEY_RES_VOL4 "=2.821"},
    {DB_ID_CALIB, KV_KEY_RES_VOL5 "=2.855"},
    
		{DB_ID_CALIB, KV_KEY_RES_CAP0 "=2.678"},
    {DB_ID_CALIB, KV_KEY_RES_CAP1 "=2.356"},
    {DB_ID_CALIB, KV_KEY_RES_CAP2 "=1.863"},
    {DB_ID_CALIB, KV_KEY_RES_CAP3 "=1.545"},
    {DB_ID_CALIB, KV_KEY_RES_CAP4 "=9.87"},
    {DB_ID_CALIB, KV_KEY_RES_CAP5 "=3.98"},
		
    {DB_ID_CALIB, KV_KEY_ROD_VOL_OFFSET "=0.008"},
    {DB_ID_CALIB, KV_KEY_ROD_DIALOG "=10"},
    {DB_ID_CALIB, KV_KEY_ROD_COL_TIME "=10"},
		
    {DB_ID_CALIB, KV_KEY_OIL_PARA_INDU "=18"},
    {DB_ID_CALIB, KV_KEY_OIL_PARA_CAP "=33"},
    {DB_ID_CALIB, KV_KEY_OIL_CAP_OFFSET "=0"},
    {DB_ID_CALIB, KV_KEY_OIL_DIALOG "=10"},
    {DB_ID_CALIB, KV_KEY_OIL_COL_TIME "=10"},
		
    {DB_ID_CALIB, KV_KEY_RES_PARA_INDU "=18"},
    {DB_ID_CALIB, KV_KEY_RES_PARA_CAP "=33"},
    {DB_ID_CALIB, KV_KEY_RES_CAP_OFFSET "=0"},
    {DB_ID_CALIB, KV_KEY_RES_VOL_OFFSET "=0.008"},
    {DB_ID_CALIB, KV_KEY_RES_CAP_DIALOG "=10"},
    {DB_ID_CALIB, KV_KEY_RES_VOL_DIALOG "=10"},
    {DB_ID_CALIB, KV_KEY_RES_CAP_COL_TIME "=10"},
		{DB_ID_CALIB, KV_KEY_RES_VOL_COL_TIME "=10"},
		
    {DB_ID_CALIB, KV_KEY_TEMP_VOL_OFFSET "=0.008"},
    {DB_ID_CALIB, KV_KEY_TEMP_DIALOG "=10"},
    {DB_ID_CALIB, KV_KEY_TEMP_COL_TIME "=10"},
		
    // SnapshotInfo
    {DB_ID_SNAPSHOT, KV_KEY_IAP_LOAD "=0"},
    {DB_ID_SNAPSHOT, KV_KEY_IAP_SIZE "=40960"},
    {DB_ID_SNAPSHOT, KV_KEY_IAP_MD5 "=00000000"},
    {DB_ID_SNAPSHOT, KV_KEY_UPDATE_OK "=2"},
    {DB_ID_SNAPSHOT, KV_KEY_UPDATE_ERR "=2"},
    {DB_ID_SNAPSHOT, KV_KEY_UBOOTBACK "=400"}
};
typedef enum {
    FIELD_INT,
    FIELD_UINT8,
    FIELD_UINT16,
    FIELD_UINT32,
    FIELD_FLOAT,
    FIELD_STRING
} FieldType;

typedef struct {
    const char *key;   // KV 名
    size_t offset;     // 在 JsRoot/J sRoot.work 中的偏移
    FieldType type;    // 字段类型
    size_t size;       // 字段大小（字符串用）
    uint8_t in_work;   // 0 表示在 JsRoot，1 表示在 JsRoot.work
} KvFieldMap;

const KvFieldMap kv_map[] = {
    // ===== JsRoot =====
    {"mac",       offsetof(JsonDat_Root, mac),       FIELD_STRING, sizeof(JsRoot.mac), 0},
    {"role",      offsetof(JsonDat_Root, role),      FIELD_INT,    sizeof(JsRoot.role), 0},
    {"console",   offsetof(JsonDat_Root, console),   FIELD_INT,    sizeof(JsRoot.console), 0},
    {"addr",      offsetof(JsonDat_Root, addr),      FIELD_UINT8,  sizeof(JsRoot.addr), 0},
    {"baudrate",  offsetof(JsonDat_Root, baudrate),  FIELD_INT,    sizeof(JsRoot.baudrate), 0},
    {"version",   offsetof(JsonDat_Root, vers_id),   FIELD_UINT32, sizeof(JsRoot.vers_id), 0},
    {"iapLoadStatus", offsetof(JsonDat_Root, iapLoadStatus), FIELD_UINT32, sizeof(JsRoot.iapLoadStatus), 0},
    {"iapsize",   offsetof(JsonDat_Root, iapSize),   FIELD_INT,    sizeof(JsRoot.iapSize), 0},
    {"iapmd5",    offsetof(JsonDat_Root, iapMd5),    FIELD_STRING, sizeof(JsRoot.iapMd5), 0},
    {"subcode",   offsetof(JsonDat_Root, subcode),   FIELD_UINT16, sizeof(JsRoot.subcode), 0},
    {"update_ok", offsetof(JsonDat_Root, update_ok), FIELD_INT,    sizeof(JsRoot.update_ok), 0},
    {"update_err",offsetof(JsonDat_Root, update_err),FIELD_INT,    sizeof(JsRoot.update_err), 0},
    {"ubootback", offsetof(JsonDat_Root, ubootback), FIELD_INT,    sizeof(JsRoot.ubootback), 0},

    // ===== JsWork =====
    {"rod_vol0", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[0]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[0]), 1},
    {"rod_vol1", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[1]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[1]), 1},
    {"rod_vol2", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[2]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[2]), 1},
    {"rod_vol3", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[3]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[3]), 1},
    {"rod_vol4", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[4]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[4]), 1},
    {"rod_vol5", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[5]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[5]), 1},
    {"rod_vol6", offsetof(JsonDat_Work, calib_rod) + offsetof(Calib_rod_t, vol_values[6]), FIELD_FLOAT, sizeof(JsWork.calib_rod.vol_values[6]), 1},

    {"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},
    {"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},
    {"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},
    {"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},
    {"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},
    {"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},
    {"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},
    {"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},
    {"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},
    {"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},
    {"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},

    {"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},
    {"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},
    {"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},
    {"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},
    {"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},
    {"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},

    {"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},
    {"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},
    {"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},
    {"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},
    {"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},
    {"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},

    {"rod_vol_offset", offsetof(JsonDat_Work, rod_vol_offset), FIELD_FLOAT, sizeof(JsWork.rod_vol_offset), 1},
    {"rod_dialog",     offsetof(JsonDat_Work, rod_dialog),     FIELD_FLOAT, sizeof(JsWork.rod_dialog), 1},
		{"rod_coltime",    offsetof(JsonDat_Work, rod_coltime),   FIELD_UINT16, sizeof(JsWork.rod_coltime), 1},

    {"oil_para_indu",  offsetof(JsonDat_Work, oil_para_indu),  FIELD_FLOAT, sizeof(JsWork.oil_para_indu), 1},
    {"oil_para_cap",   offsetof(JsonDat_Work, oil_para_cap),   FIELD_FLOAT, sizeof(JsWork.oil_para_cap), 1},
    {"oil_cap_offset", offsetof(JsonDat_Work, oil_cap_offset), FIELD_FLOAT, sizeof(JsWork.oil_cap_offset), 1},
    {"oil_dialog",     offsetof(JsonDat_Work, oil_dialog),     FIELD_FLOAT, sizeof(JsWork.oil_dialog), 1},
		{"rod_oil_coltime", offsetof(JsonDat_Work, rod_oil_coltime),   FIELD_UINT16, sizeof(JsWork.rod_oil_coltime), 1},

    {"res_para_indu",  offsetof(JsonDat_Work, res_para_indu),  FIELD_FLOAT, sizeof(JsWork.res_para_indu), 1},
    {"res_para_cap",   offsetof(JsonDat_Work, res_para_cap),   FIELD_FLOAT, sizeof(JsWork.res_para_cap), 1},
    {"res_cap_offset", offsetof(JsonDat_Work, res_cap_offset), FIELD_FLOAT, sizeof(JsWork.res_cap_offset), 1},
    {"res_vol_offset", offsetof(JsonDat_Work, res_vol_offset), FIELD_FLOAT, sizeof(JsWork.res_vol_offset), 1},
    {"res_cap_dialog", offsetof(JsonDat_Work, res_cap_dialog), FIELD_FLOAT, sizeof(JsWork.res_cap_dialog), 1},
    {"res_vol_dialog", offsetof(JsonDat_Work, res_vol_dialog), FIELD_FLOAT, sizeof(JsWork.res_vol_dialog), 1},
		{"res_oil_cap_coltime", offsetof(JsonDat_Work, res_oil_cap_coltime),   FIELD_UINT16, sizeof(JsWork.res_oil_cap_coltime), 1},
		{"res_oil_vol_coltime",    offsetof(JsonDat_Work, res_oil_vol_coltime),   FIELD_UINT16, sizeof(JsWork.res_oil_vol_coltime), 1},

    {"temp_vol_offset",offsetof(JsonDat_Work, temp_vol_offset), FIELD_FLOAT, sizeof(JsWork.temp_vol_offset), 1},
    {"temp_dialog",    offsetof(JsonDat_Work, temp_dialog),    FIELD_FLOAT, sizeof(JsWork.temp_dialog), 1},
		{"temp_coltime",   offsetof(JsonDat_Work, temp_coltime),   FIELD_UINT16, sizeof(JsWork.temp_coltime), 1},
};

/**@END ---映射表 */
/* USER CODE END ET */
/* Exported functions prototypes ---------------------------------------------*/

/**@BEGIN ------------ 小工具函数 -------------------*/
static uint8_t is_valid_db_id(uint8_t id) { // 数据库编号是否过界
    return id < DB_ID_MAX;
}
size_t get_db_used_len(uint8_t db_id){ // 倒着查找该数据库里写入了多少数据
    uint32_t addr = DB_ADDR[db_id];
    const uint8_t *p = (const uint8_t *)addr;
    for (int i = 2047; i >= 0; i--) {
        if (p[i] != 0xFF) {
            return i + 1;
        }
    }
    return 0; // 全空
}

static inline float str_to_float(const char *s) { // char转float
    return (float)strtof(s, NULL);
}

const char* memmem_custom(const char *haystack, size_t haylen,const char *needle, size_t neelen) { 
    if (!haystack || !needle || neelen == 0 || haylen < neelen) return NULL;
    for (size_t i = 0; i <= haylen - neelen; i++) {
        if (memcmp(haystack + i, needle, neelen) == 0)
            return haystack + i;
    }
    return NULL;
}

/**@END --- 小工具函数 */

/**@BEGIN ------------ FLASH擦除写入函数 -------------------*/
static uint8_t flash_erase_page(uint32_t addr) { // 擦除指定数据库区域（2KB） 20~40ms
		__disable_irq(); // 关闭全局中断
		HAL_IWDG_Refresh(&hiwdg);	
    HAL_FLASH_Unlock();

    FLASH_EraseInitTypeDef erase;
    uint32_t PageError = 0;

    erase.TypeErase   = FLASH_TYPEERASE_PAGES;
    erase.Page        = (addr - FLASH_BASE) / DB_FLASH_PAGE_SIZE;
    erase.NbPages     = 1;
    erase.Banks       = FLASH_BANK_1;

    if (HAL_FLASHEx_Erase(&erase, &PageError) != HAL_OK) {
        HAL_FLASH_Lock();
        return 0;
    }

    HAL_FLASH_Lock();
		HAL_IWDG_Refresh(&hiwdg);	
		__enable_irq(); // 开启全局中断
    return 1;
}
uint8_t DB_Save(uint8_t id, const char *buf, uint16_t len) { // 写入Flash（整个数据库内容）
    if (!is_valid_db_id(id) || buf == NULL || len > DB_FLASH_SIZE) {
				printf("DB_Save par error\n");
        return 0;
    }

    uint32_t addr = DB_ADDR[id];

    // Step1: 擦除整页
    if (!flash_erase_page(addr)) {
				printf("DB_Save: flash_erase fail 0x%08X\n", addr);
        return 0;
    }

    // Step2: 写入字符串数据（按64位字节写入）
    HAL_FLASH_Unlock();

    for (uint32_t i = 0; i < len; i += 8) {
        uint64_t word64 = 0xFFFFFFFFFFFFFFFFULL;
        memcpy(&word64, &buf[i], (len - i >= 8) ? 8 : (len - i));

        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr + i, word64) != HAL_OK) {
						KV_PRINTF("DB_Save: flash_write fail 0x%08X\n", addr + i);
            HAL_FLASH_Lock();
            return 0;
        }
    }

    HAL_FLASH_Lock();
		KV_PRINTF("DB_Save: flash_write succ id=%d, len=%d\n", id, len);
    return 1;
}


/**@END --- FLASH擦除写入函数 */

/**@BEGIN ------------ 键值对处理函数  -------------------*/
uint8_t SaveKvCache(uint8_t id, const char *kv_str){ // 支持有则更新，无则新增，值为空则删除
    static char db_buf[DB_FLASH_SIZE];
    static char new_buf[DB_FLASH_SIZE];

    if (!is_valid_db_id(id) || kv_str == NULL || strlen(kv_str) >= KV_ITEM_MAX_LEN) {
        printf("[KV] Invalid DB ID or kv_str too long/null\n");
        return 0;
    }

    char key[KV_MAX_KEY_LEN + 1] = {0};
    const char *eq = strchr(kv_str, '=');
    if (!eq || eq - kv_str > KV_MAX_KEY_LEN) {
        printf("[KV] '=' not found or key too long in '%s'\n", kv_str);
        return 0;
    }
    strncpy(key, kv_str, eq - kv_str);
    key[eq - kv_str] = '\0';
    KV_PRINTF("[KV] Processing key='%s'\n", key);

    memset(db_buf, 0, DB_FLASH_SIZE);
    uint16_t old_len = DB_Dump(id, db_buf, DB_FLASH_SIZE); // 从 flash 对应 DB 区域读取数据到 db_buf
    KV_PRINTF("[KV] Existing DB length: %u bytes\n", old_len);

    memset(new_buf, 0, DB_FLASH_SIZE);

    int found = 0; // 是否找到相同 key

    const char *pos = db_buf;
    while ((pos = strstr(pos, "##")) != NULL) {
        const char *kv = pos + 2;
        const char *next = strstr(kv, "##");
        size_t len = next ? (size_t)(next - kv) : strlen(kv);

        char temp[KV_ITEM_MAX_LEN] = {0};
        strncpy(temp, kv, len);
        temp[len] = '\0';

        const char *eq_in = strchr(temp, '=');
        if (eq_in) {
            char key_in[KV_MAX_KEY_LEN + 1] = {0};
            strncpy(key_in, temp, eq_in - temp);
            key_in[eq_in - temp] = '\0';

            if (strcmp(key_in, key) == 0) {
                // 找到相同 key
                found = 1;
                if (strlen(eq + 1) > 0) { // 找到相同 key , 更新值或删除
                    // 更新：保持位置不变
                    strcat(new_buf, "##");
                    strcat(new_buf, kv_str);
                    KV_PRINTF("[KV] Updating key in place: %s\n", kv_str);
                } else {
                    // 删除：跳过这个 key
                    KV_PRINTF("[KV] Deleting key: %s\n", key_in);
                }
            } else {
                // 不是目标 key，原样拷贝
                strcat(new_buf, "##");
                strcat(new_buf, temp); // 找不到相同 key , 复制到 new_buf
            }
        }

        if (!next) break;
        pos = next;
    }

    // 如果没找到，且新值不为空 → 追加到末尾
    if (!found && strlen(eq + 1) > 0) {
        strcat(new_buf, "##");
        strcat(new_buf, kv_str);
        KV_PRINTF("[KV] Adding new kv at end: %s\n", kv_str);
    }

    strcat(new_buf, "##"); // 结尾
    KV_PRINTF("[KV] Final DB content (len=%u):\n%s\n", (unsigned int)strlen(new_buf), new_buf);

    uint8_t result = DB_Save(id, new_buf, strlen(new_buf)); // 把更新后的数据存回 flash
    if (result) {
//        KV_PRINTF("[KV] Flash save success\n");
    } else {
//        printf("[KV] Flash save failed\n");
    }

    return result;
}

void handle_cfg_set_common(uint8_t db_id, const char *key, const char *value){ // 通用处理函数 删除 更新 新增
    if (!is_valid_db_id(db_id)) {
        printf("error=invalid db id, rlt=411\r\n");
        return;
    }

    if (strlen(value) == 0) {
        // value 为空，删除 key
        KV_HandleDelete(db_id, key);
        return;
    }

    char tmp[64] = {0};
    if (pickup_string_from_key(db_id, key, tmp, sizeof(tmp))) {
        // key 存在 → 更新
        KV_HandleUpdate(db_id, key, value);
    } else {
        // key 不存在 → 新建
        KV_HandleInsert(db_id, key, value);
    }

}

void KV_HandleQuery(uint8_t db_id, const char* key){ // 读取
    if (!is_valid_db_id(db_id) || key == NULL) {
        printf("error=invalid db id or null key, rlt=450\r\n");
        return;
    }

    char val[KV_MAX_VAL_LEN] = {0}; 
    if (pickup_string_from_key(db_id, key, val, sizeof(val))) {
        printf("fileid=%d, [%s=%s], ok\r\n", db_id, key, val);
    } else {
        printf("rlt=404, error=not exist\r\n");
    }
}


void KV_HandleUpdate(uint8_t db_id, const char* key, const char* value){ // 更新
		if (!is_valid_db_id(db_id) || key == NULL || value == NULL) {
        printf("error=invalid params, rlt=451\r\n");
        return;
    }

    char kv_buf[KV_ITEM_MAX_LEN] = {0};
    snprintf(kv_buf, sizeof(kv_buf), "%s=%s", key, value);

    uint8_t ok = SaveKvCache(db_id, kv_buf);
    if (ok) {				
        printf("ok\r\n");
    } else {
        printf("error=update fail, rlt=452\r\n");
    }
}

void KV_HandleDelete(uint8_t db_id, const char* key){ // 删除
		if (!is_valid_db_id(db_id) || key == NULL) {
        printf("error=invalid params, rlt=453\r\n");
        return;
    }

    char kv_buf[KV_ITEM_MAX_LEN] = {0};
    char value[1] = {0}; // 空串表示删除
    snprintf(kv_buf, sizeof(kv_buf), "%s=%s", key, value);

    uint8_t ok = SaveKvCache(db_id, kv_buf);
    if (ok) {				
        printf("ok\r\n");
    } else {
        printf("error=delete fail, rlt=454\r\n");
    }
}

void KV_HandleInsert(uint8_t db_id, const char* key, const char* value){ // 新增
		if (!is_valid_db_id(db_id) || key == NULL || value == NULL) {
        printf("error=invalid params, rlt=455\r\n");
        return;
    }

    char tmp[KV_MAX_VAL_LEN] = {0};
    if (pickup_string_from_key(db_id, key, tmp, sizeof(tmp))) {
        printf("error=exists, rlt=456\r\n");
        return;
    }

    char kv_buf[KV_ITEM_MAX_LEN] = {0};
    snprintf(kv_buf, sizeof(kv_buf), "%s=%s", key, value);

    uint8_t ok = SaveKvCache(db_id, kv_buf);
    if (ok) {
        printf("ok\r\n");
    } else {
        printf("error=insert fail, rlt=457\r\n");
    }
}
void DB_SaveUInt(uint8_t dbId, const char *key, uint32_t value){ // 将数值存入 KV 数据库
    char kv_str[KV_MAX_VAL_LEN]; // 足够存 "key=value"
    snprintf(kv_str, sizeof(kv_str), "%s=%" PRIu32, key, value);
    SaveKvCache(dbId, kv_str);
}
void DB_SaveChar(uint8_t db_id, const char *key, const char *value){
    char buf[128];
    snprintf(buf, sizeof(buf), "%s=%s", key, value);
    SaveKvCache(db_id, buf);
}
void DB_SaveInt(uint8_t dbId, const char *key, int32_t value){
    char kv_str[KV_MAX_VAL_LEN];
    snprintf(kv_str, sizeof(kv_str), "%s=%" PRId32, key, value);
    SaveKvCache(dbId, kv_str);
}

void DB_SaveFloat(uint8_t dbId, const char *key, float value){
    char kv_str[KV_MAX_VAL_LEN];
    snprintf(kv_str, sizeof(kv_str), "%s=%.6f", key, value); // 保留 6 位小数
    SaveKvCache(dbId, kv_str);
}
/**@END --- 键值对处理函数 */

/**@BEGIN ---------- 提取数据库数据函数 ---------------------------*/
static int extract_kv(const char *src, const char *key, char *val_out, uint8_t max_len) { // 查找"##"分隔符,在分隔符之间查找提取指定key对应的value
    const char *pos = strstr(src, "##");
    while (pos) {
        const char *kv = pos + 2;
        const char *eq = strchr(kv, '=');
        const char *end = strstr(kv, "##");
				KV_PRINTF("[extract_kv] Searching key '%s' from segment: %.32s\n", key, kv);
			
        if (eq && end && eq < end) {
            char key_buf[KV_MAX_KEY_LEN + 1] = {0};
            strncpy(key_buf, kv, eq - kv);
						key_buf[eq - kv] = '\0';
						KV_PRINTF("[extract_kv] Found key: '%s'\n", key_buf);
						
            if (strcmp(key_buf, key) == 0) {
                size_t val_len = end - eq - 1;
                if (val_len >= max_len) val_len = max_len - 1;
                strncpy(val_out, eq + 1, val_len);
                val_out[val_len] = '\0';
								KV_PRINTF("[extract_kv] Matched key! Value: '%s'\n", val_out);
                return 1;
            }
        }else {
//            printf("[extract_kv] No valid key=value between ##...\n");
        }
        pos = strstr(pos + 2, "##");
    }
//		printf("[extract_kv] Key '%s' not found.\n", key);
    return 0;
}
uint8_t pickup_string_from_key(uint8_t id, const char *key, char *val_out, uint8_t max_len) { // 从指定数据库ID的键值存储中提取字符串值
    if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
        printf("[pickup_string_from_key] Invalid input\n");
        return 0;
    }
		static char db_buf[DB_FLASH_SIZE]; 
    memset(db_buf, 0, sizeof(db_buf));

    uint16_t len = DB_Dump(id, db_buf, DB_FLASH_SIZE);
		KV_PRINTF("[pickup_string_from_key] Dumped len: %u\n", len);
    uint8_t res = extract_kv(db_buf, key, val_out, max_len);

    return res;
}
uint8_t pickup_int_from_key(uint8_t id, const char *key, int *val_out) { // 从指定数据库ID的键值存储中提取整数值
		if (!is_valid_db_id(id) || key == NULL || val_out == NULL) 
        return 0;

    char val_str[KV_MAX_VAL_LEN] = {0};
    if (pickup_string_from_key(id, key, val_str, sizeof(val_str))) {
        *val_out = atoi(val_str);
        return 1;
    }
    return 0;
}
uint8_t pickup_uint32_from_key(uint8_t id, const char *key, uint32_t *val_out) { // 从数据库提取 uint32_t 类型
		if (!is_valid_db_id(id) || key == NULL || val_out == NULL) 
        return 0;
    char val_str[KV_MAX_VAL_LEN] = {0};
    if (pickup_string_from_key(id, key, val_str, sizeof(val_str))) {
        *val_out = (uint32_t)strtoul(val_str, NULL, 10);
        return 1;
    }
    return 0;
}
uint8_t pickup_long_from_key(uint8_t id, const char *key, long *val_out) { // 从数据库提取 long 类型
		if (!is_valid_db_id(id) || key == NULL || val_out == NULL) 
        return 0;
	
    char val_str[KV_MAX_VAL_LEN] = {0};
    if (pickup_string_from_key(id, key, val_str, sizeof(val_str))) {
        *val_out = strtol(val_str, NULL, 10);
        return 1;
    }
    return 0;
}
/**@END --- 提取数据库数据函数 */

/**@BEGIN ------------ 控制台命令 一层入口函数 -------------------*/
void handle_mac_set_cmd(const char *buf){ // 处理 ###::{"mac":"112233445566"} 设置设备SN号也就是MAC号
    uint8_t db_id = 0; // 固定数据库 0
    const char *prefix = "{\"mac\":\"";
    size_t prefix_len = strlen(prefix);

    // 必须匹配前缀
    if (strncmp(buf, prefix, prefix_len) != 0) {
        printf("error=invalid mac cmd\r\n");
        return;
    }

    // 找到结束的引号
    const char *end = strchr(buf + prefix_len, '"');
    if (!end) {
        printf("error=invalid mac format\r\n");
        return;
    }

    // 提取 value
    char value[128] = {0};
    size_t vlen = end - (buf + prefix_len);
    if (vlen >= sizeof(value)) {
        printf("error=mac too long\r\n");
        return;
    }
    strncpy(value, buf + prefix_len, vlen);
    value[vlen] = '\0';

    handle_cfg_set_common(db_id, "mac", value); // 更新数据库
		
    strncpy(JsRoot.mac, value, sizeof(JsRoot.mac) - 1); // 同步更新结构体
    JsRoot.mac[sizeof(JsRoot.mac) - 1] = '\0'; // 确保结尾有 '\0'
}

void handle_cfg_set_cmd(const char *buf){ // 处理 --cfg_set=${key1}=${val1} 系统配置-KV设置 专门处理数据库0的键值对
    uint8_t db_id = 0;
    char key[KV_MAX_VAL_LEN];
    char value[128];
			
    if (parse_cfg_set_cmd(buf, &db_id, key, sizeof(key), value, sizeof(value)) != 0) {
        printf("error=invalid cfg_set format, rlt=410\r\n");
        return;
    }
    handle_cfg_set_common(db_id, key, value); // 更新数据库
		sync_JsRoot_field(key, value); // 同步更新 JsRoot 对应字段
}

void handle_cfg_setx_cmd(const char *buf){ // 处理 --cfg_setx=X,${key}=${val} 通用配置-KV设置
    uint8_t db_id;
    char key[KV_MAX_VAL_LEN];
    char value[128];
    
		if (parse_cfg_setx_cmd(buf, &db_id, key, sizeof(key), value, sizeof(value)) != 0) {
        printf("error=invalid cfg_setx format, rlt=410\r\n");
        return;
    }
		handle_cfg_set_common(db_id, key, value); // 更新数据库
		sync_JsRoot_field(key, value); // 同步更新 JsRoot 对应字段
}

void handle_cfg_get_cmd(const char *buf){ // 处理 --cfg_get=${key} 读取键值内容
    const char *prefix = "--cfg_get=";
    size_t prefix_len = strlen(prefix);

    if (!buf || strncmp(buf, prefix, prefix_len) != 0) {
        printf("error=invalid cfg_get format, rlt=420\r\n");
        return;
    }

    const char *key = buf + prefix_len;
    if (strlen(key) == 0) {
        printf("error=empty key, rlt=421\r\n");
        return;
    }

    char val[128] = {0};
    for (uint8_t db_id = 0; db_id < DB_ID_MAX; db_id++) {
        if (pickup_string_from_key(db_id, key, val, sizeof(val))) {
            printf("fileid=%d, [%s=%s], ok\r\n", db_id, key, val);
            return; // 找到就退出
        }
    }

    // 遍历完还没找到
    printf("rlt=404, error=not exist.\r\n");
}

void handle_fileid_cmd(const char *buf){ // 处理 --fileid=X,0 读取库存信息
    const char *prefix = "--fileid=";
    size_t prefix_len = strlen(prefix);

    if (!buf || strncmp(buf, prefix, prefix_len) != 0) {
        printf("error=invalid fileid format, rlt=430\r\n");
        return;
    }

    // 解析 X,0
    uint8_t db_id = 0;
    int offset = 0;
    if (sscanf(buf + prefix_len, "%hhu,%d", &db_id, &offset) != 2) {
        printf("error=invalid fileid params, rlt=431\r\n");
        return;
    }

    if (!is_valid_db_id(db_id)) {
        printf("error=invalid db id, rlt=432\r\n");
        return;
    }

    // 读取数据库内容
		uint32_t addr = DB_ADDR[db_id];
    const uint8_t *db_addr = (const uint8_t *)addr; 
    size_t db_len = get_db_used_len(db_id);       

    if (!db_addr || db_len == 0) {
        printf("error=empty db, rlt=433\r\n");
        return;
    }

    // 打印头信息
    printf("DB_DUMP, fileid=%d, len=%u,\r\n", db_id, (unsigned)db_len);
    printf("addr=0x%08X+%d,content=\r\n", (unsigned int)db_addr, offset);

    // 数据库内容是以##key=value##key2=value2## 存储
    printf("[");
    for (size_t i = 0; i < db_len; i++) {
        char c = db_addr[i];
        if (c >= 32 && c <= 126) {
            putchar(c);
        }
    }
    printf("], ok\r\n");
}
void handle_clear_fileid_cmd(const char *buf){ // 处理 --clear_fileid=X 清除内部配置库
    const char *prefix = "--clear_fileid=";
    size_t prefix_len = strlen(prefix);

    if (!buf || strncmp(buf, prefix, prefix_len) != 0) {
        printf("error=invalid clear_fileid format, rlt=440\r\n");
        return;
    }

    uint8_t db_id = (uint8_t)atoi(buf + prefix_len);
    if (!is_valid_db_id(db_id)) {
        printf("error=invalid db id, rlt=441\r\n");
        return;
    }

    uint32_t addr = DB_ADDR[db_id];
    if (flash_erase_page(addr)) {
        printf("ok\r\n");
    } else {
        printf("error=erase fail, rlt=442\r\n");
    }
}

/**@END --- 控制台命令 一层入口函数 */

/**@BEGIN ------------ 控制台命令 二层提取函数---------*/
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---------
    if (!buf || !db_id || !key || !val) return -1;
    const char *prefix = "--cfg_set=";
    size_t prefix_len = strlen(prefix);

    if (strncmp(buf, prefix, prefix_len) != 0) return -1;

    *db_id = 0; // 固定 0 号库

    const char *kv_str = buf + prefix_len;
    char *eq = strchr(kv_str, '=');
    if (!eq) return -1;

    // 提取 key
    size_t klen = eq - kv_str;
    if (klen >= key_sz) return -1;
    strncpy(key, kv_str, klen);
    key[klen] = '\0';

    // 提取 value（可能为空串）
    const char *val_str = eq + 1;
    if (strlen(val_str) >= val_sz) return -1;
    strcpy(val, val_str);

    return 0;
}
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--------------
    if (!buf || !db_id || !key || !val) return -1;
    // 格式: --cfg_setx=X,key=value
    // 先找到 '=' 号
    const char *prefix = "--cfg_setx=";
    size_t prefix_len = strlen(prefix);

    if (strncmp(buf, prefix, prefix_len) != 0) return -1;

    // 解析 DB ID
    const char *p = buf + prefix_len;
    char *comma = strchr(p, ',');
    if (!comma) return -1;
    *comma = '\0';
    *db_id = (uint8_t)atoi(p);
    *comma = ','; // 还原

    // 解析 key 和 value
    const char *kv_str = comma + 1; // 指向 key=value
    char *eq = strchr(kv_str, '=');
    if (!eq) return -1;

    // 提取 key
    size_t klen = eq - kv_str;
    if (klen >= key_sz) return -1;
    strncpy(key, kv_str, klen);
    key[klen] = '\0';

    // 提取 value（可能为空串）
    const char *val_str = eq + 1;
    if (strlen(val_str) >= val_sz) return -1;
    strcpy(val, val_str);

    return 0;
}

/**@END --- 控制台命令 二层提取函数 */

/**@BEGIN --------------- 初始化函数 ------------------*/
void DB_InitAllConfigs(void) { // 初始化数据库
    size_t total = sizeof(init_table) / sizeof(init_table[0]);

    for (size_t i = 0; i < total; i++) {
        const char *kv_str = init_table[i].kv_str;
        char key_name[KV_MAX_KEY_LEN + 1] = {0};
        const char *eq = strchr(kv_str, '=');

        if (eq) {
            strncpy(key_name, kv_str, eq - kv_str);
            key_name[eq - kv_str] = '\0';

            char existing_val[KV_MAX_VAL_LEN] = {0};
            uint8_t exists = pickup_string_from_key(init_table[i].db_id,
                                                    key_name,
                                                    existing_val,
                                                    sizeof(existing_val));

            if (!exists) {
                if (!SaveKvCache(init_table[i].db_id, kv_str)) {
//                    printf("SaveKvCache fail: %s\n", kv_str);
                } else {
//                    printf("SaveKvCache succ: %s\n", kv_str);
                }
            }
        }

        HAL_IWDG_Refresh(&hiwdg);  // 喂狗，防止初始化太久被复位
        osDelay(1);   
    }
		
		JsRoot.vers_id = CURRENT_FIRMWARE_VERSION;
		DB_SaveUInt(DB_ID_SYSTEM, KV_KEY_VERSION , JsRoot.vers_id ); // 存入 VERSION 到 SYSTEM 数据库
		
    printf("DB_InitAllConfigs complete.\n");

    osDelay(100); 
    DB_Dump_to_JsRoot();
    printf("DB_Dump_to_JsRoot ok \r\n");
}

uint16_t DB_Dump(uint8_t id, char *buf, uint16_t buf_len) { // 从Flash读取区域内容到缓存
    if (!is_valid_db_id(id)) {
        printf("[DB_Dump] Invalid DB ID: %d\n", id);
        return 0;
    }
    if (buf == NULL) {
        printf("[DB_Dump] NULL buffer pointer\n");
        return 0;
    }
    if (buf_len < DB_FLASH_SIZE) {
        printf("[DB_Dump] Buffer too small: buf_len = %d, required = %d\n", buf_len, DB_FLASH_SIZE);
        return 0;
    }

    uint32_t addr = DB_ADDR[id];
    const uint8_t *flash_ptr = (const uint8_t *)addr;
//		printf("[DB_Dump] Start reading from address: 0x%08X (DB_ID: %d)\n", addr, id);
		
    uint16_t i;
    for (i = 0; i < DB_FLASH_SIZE && i < buf_len - 1; i++) {
        buf[i] = flash_ptr[i];
        if (flash_ptr[i] == 0xFF) {
//						printf("[DB_Dump] Reached 0xFF at offset %d\n", i);
            break;
        }
    }

    buf[i] = '\0';  // null终止
    return i;
}


uint8_t pickup_string_from_cache(uint8_t id, const char *key, char *val_out, uint8_t max_len) { // 从缓存提取字符串
    if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
        return 0;
    }
    return extract_kv(db_cache[id], key, val_out, max_len);
}
uint8_t pickup_int_from_cache(uint8_t id, const char *key, int *val_out) { // 从缓存提取 int
    if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
        return 0;
    }
    char val_str[KV_MAX_VAL_LEN] = {0};
    if (pickup_string_from_cache(id, key, val_str, sizeof(val_str))) {
        *val_out = atoi(val_str);
        return 1;
    }
    return 0;
}
uint8_t pickup_uint32_from_cache(uint8_t id, const char *key, uint32_t *val_out) { // 从缓存提取 uint32_t
    if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
        return 0;
    }
    char val_str[KV_MAX_VAL_LEN] = {0};
    if (pickup_string_from_cache(id, key, val_str, sizeof(val_str))) {
        *val_out = (uint32_t)strtoul(val_str, NULL, 10);
        return 1;
    }
    return 0;
}
uint8_t pickup_long_from_cache(uint8_t id, const char *key, long *val_out) { // 从缓存提取 long
    if (!is_valid_db_id(id) || key == NULL || val_out == NULL) {
        return 0;
    }
    char val_str[KV_MAX_VAL_LEN] = {0};
    if (pickup_string_from_cache(id, key, val_str, sizeof(val_str))) {
        *val_out = strtol(val_str, NULL, 10);
        return 1;
    }
    return 0;
}
void DB_Dump_to_JsRoot(void) { // 从数据库同步到 缓存结构体 JsRoot   
    DB_Load_All(); // 一次性加载所有数据库到缓存里 
    DB_SyncAllToJsRoot(); // 使用缓存解析，不再访问 Flash	
}
void DB_Load_All(void) { // 一次性读取所有数据库到 RAM
		
    for (uint8_t id = 0; id < 3; id++) {
        db_cache_len[id] = DB_Dump(id, db_cache[id], DB_FLASH_SIZE); // db_cache用来缓存每个库的数据
    }
}
void DB_SyncAllToJsRoot(void) { 
	char key[64]; char val[128]; 
	
	if(JsRoot.work == NULL) JsRoot.work = &JsWork; // 自动赋值 
	
	for (uint8_t id = 0; id < 3; id++) { 
		
		const char *p = db_cache[id]; 
		const char *pend = p + db_cache_len[id]; 
//		printf(">>> Start parsing DB[%d], len=%u\n", id, db_cache_len[id]); 
		
		while (p < pend) { // 找到 "##" 开头 
			const char *start = memmem_custom(p, pend - p, "##", 2); 
			
			if (!start) break; 
			start += 2; // 跳过 ## 
			
			const char *end = memmem_custom(start, pend - start, "##", 2); 
			if (!end) end = pend; // 没找到结束标记 
			
			// 在 start 到 end 之间找 '=' 
			const char *sep = memchr(start, '=', end - start); 
			if (!sep) { 
//				printf("DB[%d] stop: no '=' found at offset=%ld\n", id, start - db_cache[id]); 
				p = end;
				continue; 
			} 
			
			size_t klen = sep - start; 
			size_t vlen = end - (sep + 1); 
			
			if (klen >= sizeof(key)) klen = sizeof(key) - 1; 
			if (vlen >= sizeof(val)) vlen = sizeof(val) - 1; 
			
			strncpy(key, start, klen); key[klen] = '\0'; 
			strncpy(val, sep + 1, vlen); val[vlen] = '\0'; 
			
//			printf("DB[%d] key=%s, val=%s, p_offset=%ld\n", id, key, val, start - db_cache[id]); 
			
			sync_JsRoot_field(key, val); 
			p = end; // 移动到下一个 "##" 开头或结尾			
			HAL_IWDG_Refresh(&hiwdg); // 刷新看门狗 
			
			} 
//			printf("<<< End of DB[%d]\n\n", id); 
	}
	
}

void sync_JsRoot_field(const char *key, const char *value) { // 查询更新某个缓存结构体参数
    for (size_t i = 0; i < sizeof(kv_map) / sizeof(kv_map[0]); i++) {
        if (strcmp(key, kv_map[i].key) == 0) {
//            uint8_t *base = kv_map[i].in_work ? (uint8_t*)&JsRoot.work : (uint8_t*)&JsRoot;
						uint8_t *base = kv_map[i].in_work ? (uint8_t*)JsRoot.work : (uint8_t*)&JsRoot;

            void *field_ptr = base + kv_map[i].offset;

						switch (kv_map[i].type) {
								case FIELD_STRING:
										snprintf((char*)field_ptr, kv_map[i].size, "%s", value);
										break;
								case FIELD_INT:
										*(int*)field_ptr = strtol(value, NULL, 10);
										break;
								case FIELD_UINT8:
										*(uint8_t*)field_ptr = (uint8_t)strtoul(value, NULL, 10);
										break;
								case FIELD_UINT16:
										*(uint16_t*)field_ptr = (uint16_t)strtoul(value, NULL, 10);
										break;
								case FIELD_UINT32:
										*(uint32_t*)field_ptr = strtoul(value, NULL, 0);
										break;
								case FIELD_FLOAT:
										*(float*)field_ptr = str_to_float(value);
										break;
						}
            HAL_IWDG_Refresh(&hiwdg); // 刷新看门狗
            return; // 找到并更新完成
        }
    }
    printf("sync_JsRoot_field: unknown key = %s\n", key);
}


void DB__Dump_IAP_LOAD(void){
	int iapload =0;
	pickup_int_from_key(2,KV_KEY_IAP_LOAD,&iapload);
	JsRoot.iapLoadStatus = iapload;
	printf("iapLoadStatus %d\r\n",JsRoot.iapLoadStatus);
}

/**@END --- 初始化函数 */

/* USER CODE BEGIN EFP */
