#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include "utils.h"
#include "main.h"
#include <math.h>
#include "jsonct.h"
#include "../common.h"
#include "../cmdclient.h"
#include "../log_module/log_module.h"
#include "../multilanguage.h"
#include "../business_log/business_log.h"
#include "common.h"

int g_reflush_all_plan = 1;
all_plan g_all_plan[MAX_DAY_YEAR+1] = {0};//一年计划

usercfg_variant_t g_usercfg_variant = {
    .SOCmax = 100,
    .SOCmin = 0,
    .SOCmaxErr = 5,
    .SOCminErr = 5,
    .K2 = 100,
    .loadTrackingLimit = 1,
    .Pmlmax = 1000,
    .K1 = 100,
    .cab_power = DEFAULT_PCS_POWER,
    .StratagyType = STRATAGY_TYPE_PERIOD_CTRL,
    .WorkSamplePeriod = 10,
    .IdleSamplePeriod = 10,
    .ProtectReverseWin = 1,
    .K3 = 100,
    .SingleMaxVoltage = 4.200,
    .SingleMinVoltage = 2.500,
    .SingleMaxVoltageErr = 0.200,
    .SingleMinVoltageErr = 0.200,
    .EnCellVoltProtect = 0,
    .PcsPowerFactor = 0.95,
    .EnPcsRpComp = 0,
    .g100Enable=1,
    .g100UnlockTime=240,
    .g100LockMaxCount=3,
    .g100BackflowCount10Min=2,
    .g100BackflowCountDay=4,
    .g100BackflowLimit=0,
    .g100BackflowSec=5,
    .g100BackflowLockTime=62,
    .g100OverloadLimit=1000,
    .g100OverloadSec=5,
    .g100OverloadLockTime=62,
    .Installedcab_coeff = 100,
    .HeatCtrCycle = 10,
};

cabinet_info_t cabinet_info = {
    .cab_id                = "000000",
    .cnt_id                = "def",
    .name                  = "default",
    .cab_location          = "0.0,0.0",
    .cab_capacity          = 0,                 //KWH
    .cab_power             = DEFAULT_PCS_POWER, //额定功率KW   此功率将限制最大最小冲放电功率
    .cab_time              = "1960-1-1",
    .cab_local             = "default",
    .cab_station_name      = "default",
    .pack_temp_point       = 28,
    .en_ems_cloud          = 0, //使能EMS云
    .tenant_id             = "000000",
    .en_mqtt_dp            = 1,
    .en_mqtt_emms2         = 1,
    .en_mqtt_emms2_write   = 1,
    .en_mqtt_stactrl       = 0, // 使能站控mqtt
    .en_mqtt_stactrl_write = 0,
    .en_lc                 = 0,
    .en_lc_ctrl            = 0,
    .ctrl_mode             = 0, // 0:对等模式,1:主从模式
    .mode_microgrid        = 0, // 0:禁用微网 1:使能微网直流并/离网模式  2:使能微网交流并/离网模式
    .en_diesel_generator   = 0, // 0:禁用柴发  1:使能柴发
    .en_photovoltaic       = 0, // 0:禁用光伏  1:使能光伏
    .en_mains              = 0, // 0:禁用市电  1:使能市电
    .en_charging_pile      = 0, // 0:禁用充电桩  1:使能充电桩
    .en_esc                = 0, // 0:禁用储能柜  1:使能储能柜
    .en_north_mqtt         = 0, //使能标准北向
    .en_custom_mqtt        = 0, //使能定制北向
    .ms_mode               = MS_MODE_STD,
    .is_slave              = 0,
};
cabinet_info_t *get_cabinet_info()
{
    return &cabinet_info;
}

cabinet_info_tab_t cabinet_info_tab[] = 
{
    {"id",                          "Cabinet number",         offsetof(cabinet_info_t,cab_id           ),   "string"}, 
    {"cnt_id",                     "Connect Id",         offsetof(cabinet_info_t,cnt_id     ),   "string"},
    {"cnt_id_l",                     "Connect Id",         offsetof(cabinet_info_t,cnt_id_l     ),   "string"},
    {"name",                       "Cabinet name",         offsetof(cabinet_info_t,name     ),     "string"},
    {"location",                     "coordinate",         offsetof(cabinet_info_t,cab_location     ),   "string"}, 
    {"capacity",                    "Installed capacity(kWh)",         offsetof(cabinet_info_t,cab_capacity     ),   "int"}, 
    {"power",                      "Installed power(kW)",         offsetof(cabinet_info_t,cab_power        ),   "int"}, 
    {"time",                        "Time",         offsetof(cabinet_info_t,cab_time         ),   "string"}, 
    {"local",                       "Position",         offsetof(cabinet_info_t,cab_local        ),   "string"}, 
    {"station_name",                "Default site name",         offsetof(cabinet_info_t,cab_station_name ),   "string"}, 
    {"en_cloud",                    "Enable EMS cloud",     offsetof(cabinet_info_t,en_ems_cloud),    "int"},
    {"tenant_id",                  "EMMS tenant ID",      offsetof(cabinet_info_t,tenant_id),     "string"},
    {"en_mqtt_dp",               "Enable EMMS2",     offsetof(cabinet_info_t,en_mqtt_dp),    "int"},
    {"en_mqtt_emms2",               "Enable EMMS2",     offsetof(cabinet_info_t,en_mqtt_emms2),    "int"},
    {"en_mqtt_emms2_write",         "Enable EMMS2 write operation",offsetof(cabinet_info_t,en_mqtt_emms2_write),    "int"},
    {"en_mqtt_stactrl",               "Enable Station mqtt",     offsetof(cabinet_info_t,en_mqtt_stactrl),    "int"},
    {"en_mqtt_stactrl_write",         "Enable Station mqtt write operation",offsetof(cabinet_info_t,en_mqtt_stactrl_write),    "int"},
    {"en_lc",                      "Enable master-slave mode",      offsetof(cabinet_info_t,en_lc),    "int"},
    {"en_lc_ctrl",                  "Enable slave controller",      offsetof(cabinet_info_t,en_lc_ctrl),    "int"},
    {"mode",                  "Control mode",      offsetof(cabinet_info_t,ctrl_mode),    "int"},
    {"mode_microgrid",          "微网拓扑模式",      offsetof(cabinet_info_t,mode_microgrid),    "int"},
    {"en_diesel_generator",     "柴发使能",      offsetof(cabinet_info_t,en_diesel_generator),    "int"},
    {"en_photovoltaic",         "光伏使能",      offsetof(cabinet_info_t,en_photovoltaic),    "int"},
    {"en_mains",                "市电使能",      offsetof(cabinet_info_t,en_mains),    "int"},
    {"en_charging_pile",        "充电桩使能",      offsetof(cabinet_info_t,en_charging_pile),    "int"},
    {"en_esc",                  "使能储能柜",      offsetof(cabinet_info_t,en_esc),    "int"},
    {"en_sts",                  "使能STS",      offsetof(cabinet_info_t,en_sts),    "int"},
    {"en_icb",                  "使能并离网断路器",      offsetof(cabinet_info_t,en_icb),    "int"},
    {"en_lcb",                  "使能负载断路器",      offsetof(cabinet_info_t,en_lcb),    "int"},
    {"en_ats",                  "使能ATS",      offsetof(cabinet_info_t,en_ats),    "int"},
	 
	{"en_north_mqtt",               "Enable MQTT Standard North",offsetof(cabinet_info_t,en_north_mqtt),      "int"},
    {"en_custom_mqtt",               "Enable MQTT Custom North",offsetof(cabinet_info_t,en_custom_mqtt),      "int"},
    {"ms_mode",               "主从模式",offsetof(cabinet_info_t,ms_mode),      "int"},
    {"is_slave",               "是从机",offsetof(cabinet_info_t,is_slave),      "int"},
};
const int cabinet_info_tab_count_g = sizeof(cabinet_info_tab) / sizeof(cabinet_info_tab[0]);

int paser_cabinet_info_file(cabinet_info_t *pcfg, const char *pdata)
{
    cJSON *root = NULL;
    root = json_parse_string_with_comments(pdata);
    if (!root)
    {
        printf("parse cfg file error");
        goto out;
    }
    for(int i =0;i<cabinet_info_tab_count_g;i++)
    {
        char *key=NULL;
        key=cabinet_info_tab[i].cab_key;
        if(key!=NULL)printf("key:%s",key);
        else continue;
        if(strcmp(cabinet_info_tab[i].cab_type,"string")==0)
        {
            {GET_JSON_STRING(root, ((char *)pcfg + cabinet_info_tab[i].cab_member_ofs), key);
            printf("get:%s :%s",key,((char *)pcfg + cabinet_info_tab[i].cab_member_ofs));}
        }
        else if(strcmp(cabinet_info_tab[i].cab_type,"int")==0)
        {
            {GET_JSON_INT(root, *((int *)((char *)pcfg + cabinet_info_tab[i].cab_member_ofs)),key);
            printf("get:%s :%d",key,*((int *)((char *)pcfg + cabinet_info_tab[i].cab_member_ofs)));}
        }
    }
out:
    cJSON_Delete(root);
    return 0;
}

int cabinet_info_cmp_by_name(char *name, void *value, int *ok)
{
    if (name == NULL || value == NULL)
    {
        return -1;
    }

    cabinet_info_t *pcfg = &cabinet_info;
    *ok = 0;
    for (int i = 0; i < cabinet_info_tab_count_g; i++)
    {
        if (strcmp(name, cabinet_info_tab[i].cab_key) != 0)
        {
            continue;
        }
        *ok = 1;
        if (strcmp(cabinet_info_tab[i].cab_type, "string") == 0)
        {
            return strcmp((char *)pcfg + cabinet_info_tab[i].cab_member_ofs, (char *)value);
        }
        else if (strcmp(cabinet_info_tab[i].cab_type, "int") == 0)
        {
            return (*((int *)((char *)pcfg + cabinet_info_tab[i].cab_member_ofs)) - *(int *)value);
        }
        break;
    }
    get_pcs_ctrl_var()->cabinet_save = true;
    return -2;
}

void write_cabinet_info_by_name(char *name ,void *value)
{
    if (name == NULL || value == NULL)
        return;

    cabinet_info_t  *pcfg= &cabinet_info;
    for(int i =0;i<cabinet_info_tab_count_g;i++)
    {
        if(strcmp(name,cabinet_info_tab[i].cab_key)!=0)continue;
        if(strcmp(cabinet_info_tab[i].cab_type,"string")==0)
        {
            strcpy((char *)pcfg + cabinet_info_tab[i].cab_member_ofs,(char *)value);
        }
        else if(strcmp(cabinet_info_tab[i].cab_type,"int")==0)
        {
            *((int *)((char *)pcfg + cabinet_info_tab[i].cab_member_ofs)) = *(int *)value;
        }
        break;
    }

    if (cabinet_info.ctrl_mode != EMS_MODE_ZC) //如果关闭了LC控制器，本地LC应该也关闭
    {
        cabinet_info.en_lc = 0;
    }
    get_pcs_ctrl_var()->cabinet_save= true;
}

void read_cabinet_info_by_name(char *name ,void *value)
{
    cabinet_info_t  *pcfg= &cabinet_info;
    for(int i = 0;i<cabinet_info_tab_count_g;i++)
    {
        if(strcmp(name,cabinet_info_tab[i].cab_key)!=0) continue;
        if(strcmp(cabinet_info_tab[i].cab_type,"string")==0)
        {
            strcpy((char *)value,(char *)pcfg + cabinet_info_tab[i].cab_member_ofs);
        }
        else if(strcmp(cabinet_info_tab[i].cab_type,"int")==0)
        {
            *(int *)value = *((int *)((char *)pcfg + cabinet_info_tab[i].cab_member_ofs));
        }
        break;
    }
}



void reflush_other_data_to_usercfg(usercfg_variant_t *pcfg)
{
    pcfg->cab_power=cabinet_info.cab_power;
    pcfg->cab_capacity=cabinet_info.cab_capacity;
}
int write_cabinet_info_file(cabinet_info_t *pcfg, const char *filepwd)
{
    cJSON *root =NULL;
    char *string = NULL;
    root = cJSON_CreateObject();
    if (root == NULL)
    {
        printf("cJSON_CreateObject root NULL\n");
        goto end;
    }

    for(int i =0;i<cabinet_info_tab_count_g;i++)
    {
        char *key=NULL;
        key=cabinet_info_tab[i].cab_key;
        if(key!=NULL)printf("key:%s\n",key);
        else continue;
        if(strcmp(cabinet_info_tab[i].cab_type,"string")==0)
        {
            {cJSON_AddStringToObject(root,key,((char *)pcfg + cabinet_info_tab[i].cab_member_ofs));
            printf("set:%s :%s\n",key,((char *)pcfg + cabinet_info_tab[i].cab_member_ofs));}
        }
        else if(strcmp(cabinet_info_tab[i].cab_type,"int")==0)
        {
            {cJSON_AddNumberToObject(root,key,*((int *)((char *)pcfg + cabinet_info_tab[i].cab_member_ofs)));
            printf("set:%s :%d\n",key,*((int *)((char *)pcfg + cabinet_info_tab[i].cab_member_ofs)));}
        }
    }
    string = cJSON_Print(root);
    if (string != NULL)
    {
        write_file_data(filepwd, string, strlen(string));
        free(string);
        string=NULL;
    }
end:
    if (root != NULL)
    {
        cJSON_Delete(root);
    }
    return 0;
}







int creat_cabinet_on_ua(UA_SERVER_PARAM *ua_param,cabinet_info_t *pcfg)
{
    creat_a_base_file(ua_param,"PropertyInfo",_("Asset information"),&pcfg->cabinet_id);
    UA_NodeId node_id;

    
    for(int i =0;i<cabinet_info_tab_count_g;i++)
    {
        char *key=NULL;
        key=cabinet_info_tab[i].cab_key;
        if(key!=NULL)ems_syslog(LOG_INFO, "key:%s",key);
        else continue;
        if(strcmp(cabinet_info_tab[i].cab_type,"string")==0)
        {
            creat_a_base_string(ua_param,cabinet_info_tab[i].cab_key,cabinet_info_tab[i].cab_show_name,&node_id,pcfg->cabinet_id,((char *)pcfg + cabinet_info_tab[i].cab_member_ofs));
        }
    }
    return 0;
}

int load_cabinet_info_cfg(cabinet_info_t *pcfg)
{
    char *pdata = NULL;
    printf("read cfg file %s ...\n", CONFIG_PATH"/"PROPERTY);
    pdata = read_file_data(CONFIG_PATH"/"PROPERTY);
    if (pdata == NULL)
    {
        printf( "read cfg file %s error\n", CONFIG_PATH"/"PROPERTY);
        write_cabinet_info_file(pcfg,CONFIG_PATH"/"PROPERTY);
        return -1;
    }
    paser_cabinet_info_file(pcfg, pdata);
    if (pdata != NULL)
    {
        free(pdata);
    }
    return 0;
}


int paser_cfg_file(usercfg_variant_t *pcfg, const char *pdata);

int load_ems_user_cfg(usercfg_variant_t *pcfg)
{
    char *pdata = NULL;

    pdata = read_file_data(EMS_USER_CFG_FILE);

    if (pdata == NULL)
    {
        ems_syslog(LOG_ERR, "read cfg file %s error", EMS_USER_CFG_FILE);
        return -1;
    }
    paser_cfg_file(pcfg, pdata);
    if (pdata != NULL)
    {
        free(pdata);
    }
    return 0;
}

int load_ems_sys_cfg(usercfg_variant_t *pcfg)
{
    char *pdata = NULL;
    pdata = read_file_data(EMS_CFG_FILE);

    if (pdata == NULL)
    {
        ems_syslog(LOG_ERR, "read cfg file %s error", EMS_CFG_FILE);
        return -1;
    }
    paser_cfg_file(pcfg, pdata);
    if (pdata != NULL)
    {
        free(pdata);
    }
    return 0;
}
extern int ems_config_tab_count_g;
extern ems_cfg_info_t ems_config_tab[];
int paser_cfg_file(usercfg_variant_t *pcfg, const char *pdata)
{
    cJSON *root = NULL;
    cJSON *nodes = NULL;
    root = json_parse_string_with_comments(pdata);
    if (!root)
    {
        ems_syslog(LOG_ERR, "parse cfg file error");
        goto out;
    }
    for(int i =0;i<ems_config_tab_count_g;i++)
    {
        char *key=NULL;
        key=ems_config_tab[i].nod+strlen(DEV_NO_EMS".");
        if(key!=NULL)ems_syslog(LOG_INFO, "key:%s",key);
        switch(ems_config_tab[i].data_type)
        {
            case EMS_POINT_INT:
            {
                if(key!=NULL)
                    {GET_JSON_INT(root, *((int *)((char *)pcfg + ems_config_tab[i].member_ofs)),key);
                    ems_syslog(LOG_NOTICE, "get:%s :%d",key,*((int *)((char *)pcfg + ems_config_tab[i].member_ofs)));}
            };
            break;
            case EMS_POINT_FLOAT:
            {
                if(key!=NULL)
                   {GET_JSON_FLOAT(root, *((float *)((char *)pcfg + ems_config_tab[i].member_ofs)),key);
                    ems_syslog(LOG_NOTICE, "get:%s :%f",key,*((float *)((char *)pcfg + ems_config_tab[i].member_ofs)));}
            };
            break;
        }
    }

    nodes = cJSON_GetObjectItemCaseSensitive(root, "tou");
    if (!nodes)
    {
        ems_syslog(LOG_ERR, "get nodes_cfg failed");
        goto out;
    }

    int node_cnt = cJSON_GetArraySize(nodes);
	if (node_cnt > NUM_TOU_SEG_MAX) {
		ems_syslog(LOG_WARNING, "Too many tou segment: %d, max: %d", node_cnt, NUM_TOU_SEG_MAX);
		node_cnt = NUM_TOU_SEG_MAX;
	}

    for(int i = 0;i<node_cnt;i++)//最大6段
    {
        cJSON *node = cJSON_GetArrayItem(nodes, i);
		if (node == NULL)
			continue;

        tou_seg_t *tou = &pcfg->tou[i];
        GET_JSON_INT(node, tou->StHour, "StHour");
        GET_JSON_INT(node, tou->StMin, "StMin");
        GET_JSON_INT(node, tou->EnHour, "EnHour");
        GET_JSON_INT(node, tou->EnMin, "EnMin");
        GET_JSON_INT(node, tou->Power, "Power");
        GET_JSON_INT(node, tou->RePower, "RePower");
        GET_JSON_INT(node, tou->SocUp, "SOCUpperLimit");
        GET_JSON_INT(node, tou->SocLower, "SOCLowerLimit");
        ems_syslog(LOG_NOTICE, "Load TOU Segment[%d] [%d:%d] -> [%d:%d] = %d",i+1,tou->StHour,tou->StMin,tou->EnHour,tou->EnMin,tou->Power);
    }
out:
    cJSON_Delete(root);
    return 0;
}

unsigned int get_seconds_of_today(void) 
{
    time_t t = time(NULL);
    struct tm target_time = {};  
    localtime_r(&t, &target_time);
    struct tm *local_time = &target_time;
    unsigned int today_second = 0;
    today_second = local_time->tm_hour * 3600 + local_time->tm_min * 60 + local_time->tm_sec;

    return today_second;
}

int get_current_plan(usercfg_variant_t *pcfg, int *power, int *repower, int *socup, int *soclower)
{
    int i;
    unsigned int today_second = 0;
    today_second = get_seconds_of_today();
    double socmax = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMAX);
    double socmin = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMIN);
    if (socmax == socmin){ // 点位还未赋值
        socmax = 100; socmin = 0;
    }

    for(i = 0; i < NUM_TOU_SEG_MAX; i++)//最大96段
    {
        tou_seg_t *tou = &pcfg->tou[i];
        uint32_t start_time =   tou->StHour * 3600 + tou->StMin * 60;
        uint32_t end_time =     tou->EnHour * 3600 + tou->EnMin * 60;
        if (end_time > start_time) // 不跨天的情况
        {
            if (today_second > start_time && today_second <= end_time)
            {
                *repower = tou->RePower;
                *power = tou->Power;
                if (tou->SocUp == tou->SocLower){ // 策略中未设置
                    *socup = socmax; 
                    *soclower = socmin;
                }
                else{
                    *socup = (tou->SocUp > socmax) ? socmax : tou->SocUp;
                    *soclower = (tou->SocLower < socmin) ? socmin : tou->SocLower;
                }
                break;
            }
        }
        else // 跨天的情况
        {
            if (start_time == 0 && end_time == 0)
            {
                // 在无效策略时间设置soc默认值，无功为0
                *socup = (int)socmax;
                *soclower = (int)socmin;
                *repower = 0;
                *power = 0;
                continue;
            }

            if (today_second > start_time || today_second <= end_time)
            {
                *repower = tou->RePower;
                *power = tou->Power;
                if (tou->SocUp == tou->SocLower){
                    *socup = socmax; 
                    *soclower = socmin;
                }
                else{                
                    *socup = (tou->SocUp > socmax) ? socmax : tou->SocUp;
                    *soclower = (tou->SocLower < socmin) ? socmin : tou->SocLower;
                }
                break;
            }
        }
    }

    if (i >= NUM_TOU_SEG_MAX) {
        *power = 0;
        *repower = 0;
        *socup = (int)socmax;
        *soclower = (int)socmin;
        return -1;
    }
    return i;
}

void check_plan_soc_suitable(usercfg_variant_t *pcfg)
{
    int i;
    int result = 0;
    double socmax = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMAX);
    double socmin = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMIN);
    if(socmax == socmin)  // 点位还未赋值
    {
        socmax = 100; socmin = 0;
    }

    for(i = 0; i < NUM_TOU_SEG_MAX; i++)//最大96段
    {
        tou_seg_t *tou = &pcfg->tou[i];
        if(tou->SocUp == tou->SocLower)
        {
            continue;
        }
        else if(tou->SocUp > socmax || tou->SocLower < socmin)
        {
            result = 1;
            break;
        }
    }

    int alarm_cur = dev_get_dev_tag_int(DEV_NO_EMS, SOC_LIMIT_ALARM);
    if(result != alarm_cur)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, SOC_LIMIT_ALARM,  result);
    }
}

int check_to_plan_time(usercfg_variant_t *pcfg,int time,float powernow)//time 秒 //返回 0待机 1预工作 2工作
{
    int i;
    ems_syslog(LOG_NOTICE, "heat_management 333333333333[%f]!",powernow);
    int today_second = 0;
    today_second = get_seconds_of_today();
    if (!IS_IDLE(powernow))
    {
        ems_syslog(LOG_DEBUG, "work: %f", powernow);
        return 2;//有功率 工作模式
    }
    ems_syslog(LOG_DEBUG, "no work: %f", powernow);
    for(i = 0;i<NUM_TOU_SEG_MAX;i++)
    {
        tou_seg_t *tou = &pcfg->tou[i];
        int start_time =   tou->StHour * 3600 + tou->StMin * 60;
        int end_time =     tou->EnHour * 3600 + tou->EnMin * 60;

        if (((start_time!=0)||(end_time!=0)))
        {
            // if ((today_second > start_time && today_second <=end_time)) {
            //     return 1;//工作段 功率为0 设置为
            // }
            // else 
            if ((((start_time - today_second)<time)&&(start_time - today_second)>=0)||((today_second - start_time)>(86400-time)))
            {
                return 1;
            }
        }
    }
    return 0;
}

int get_demand_plan(usercfg_variant_t *pcfg)
{
    int i;
    unsigned int today_second = 0;
    today_second = get_seconds_of_today();
    for(i = 0;i<NUM_DEMAND_MAX;i++)
    {
        demand_seg_t *tou = &pcfg->demand_param.tou_seg[i];
        uint32_t start_time =   tou->StHour * 3600 + tou->StMin * 60;
        uint32_t end_time =     tou->EnHour * 3600 + tou->EnMin * 60;
        if (today_second > start_time && today_second <=end_time) {
            break;
        }
    }
    if (i>=NUM_DEMAND_MAX) {
        return -1;
    }
    return i;
}

void save_all_plan_to_file(cJSON *plan)
{
    char *string = cJSON_Print(plan);
    ems_syslog(LOG_INFO,"%s\n",string);
    if (string != NULL)
    {
        write_file_data(ALL_PLAN_FILE, string, strlen(string));
        free(string);
        string=NULL;
        server_add_new_log(LOG_CODE_SAVE_FILE,"EMS",LOG_MODE_TRIG,1,ALL_PLAN_FILE);
    }
}
void get_all_plan_from_file()
{
    char *pdata = NULL;
    printf("read cfg file %s ...\n", ALL_PLAN_FILE);
    pdata = read_file_data(ALL_PLAN_FILE);
    if (pdata == NULL)
    {
        printf( "read cfg file %s error\n", ALL_PLAN_FILE);
        BUSINESS_LOG(BLOG_ERR, NORTH_ID_0045, NULL, "[北向]:读取云平台策略计划文件失败，文件名：[%s]", ALL_PLAN_FILE);
        return ;
    }
    else
    {
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0045, NULL, "[北向]:读取云平台策略计划文件成功，文件名：[%s]", ALL_PLAN_FILE);
    }

    cJSON *root = cJSON_Parse(pdata);
    if (!root)
    {
        ems_syslog(LOG_ERR, "json parse error");
        BUSINESS_LOG(BLOG_ERR, NORTH_ID_0046, NULL, "[北向]:解析云平台策略计划文件失败，文件名：[%s]", ALL_PLAN_FILE);
        goto END;
    }
    else
    {
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0046, NULL, "[北向]:解析云平台策略计划文件成功，文件名：[%s]", ALL_PLAN_FILE);
    }
    parse_all_plan(root);
END:  
    if (root != NULL)
    {
        cJSON_Delete(root);
    }
    if (pdata != NULL)
    {
        free(pdata);
    }
}

int parse_all_plan(cJSON *messages)
{
    int message_num = cJSON_GetArraySize(messages);
    message_num = message_num>MAX_DAY_YEAR?MAX_DAY_YEAR:message_num;
    for (int i = 0;i<MAX_DAY_YEAR;i++)
    {
        g_all_plan[i].year = 0;
        g_all_plan[i].mounth = 0;
        g_all_plan[i].date = 0;
        for (int total_time=0;total_time<NUM_TOU_SEG_MAX;total_time++)
        {
            memset(&g_all_plan[i].schedule[total_time],0,sizeof(g_all_plan[i].schedule[total_time]));
            g_all_plan[i].schedule[total_time].SocUp = -1;
            g_all_plan[i].schedule[total_time].SocLower = -1;
        }
    }

    for (size_t i = 0; i < message_num; i++)
    {
        int tmp_1,tmp_2,tmp_3;
        cJSON *message = cJSON_GetArrayItem(messages,i);
        cJSON *date = cJSON_GetObjectItem(message, "Date");
        if (date!=NULL)
        {
            if(sscanf(date->valuestring,"%d-%d-%d",&tmp_1,&tmp_2,&tmp_3)!=3)
            {
                return -1;
            }
            g_all_plan[i].year = tmp_1;
            g_all_plan[i].mounth = tmp_2;
            g_all_plan[i].date = tmp_3;

            //for (int total_time=0;total_time<NUM_TOU_SEG_MAX;total_time++)
            //{
            //    memset(&g_all_plan[i].schedule[total_time],0,sizeof(g_all_plan[i].schedule[total_time]));
            //}

            cJSON *Schedules = cJSON_GetObjectItem(message, "Schedule");
            int Schedule_num = cJSON_GetArraySize(Schedules);
            for (int j = 0; j < Schedule_num; j++)
            {
                cJSON *Schedule = cJSON_GetArrayItem(Schedules,j);
                if (Schedule!=NULL)
                {   
                    cJSON *StartTime = cJSON_GetObjectItem(Schedule, "StartTime");
                    cJSON *EndTime = cJSON_GetObjectItem(Schedule, "EndTime");
                    cJSON *Power = cJSON_GetObjectItem(Schedule, "Power");
                    cJSON *RePower = cJSON_GetObjectItem(Schedule, "RePower");
                    cJSON *Soc_up = cJSON_GetObjectItem(Schedule, "SOCUpperLimit");
                    cJSON *Soc_lower = cJSON_GetObjectItem(Schedule, "SOCLowerLimit");

                    if ((!StartTime)||(!EndTime)||(!Power)) return -1;
                    
                    int start_hour,start_min,end_hour,end_min,power,repower,soc_up,soc_lower;
                    sscanf(StartTime->valuestring,"%d:%d",&start_hour,&start_min);
                    sscanf(EndTime->valuestring,"%d:%d",&end_hour,&end_min);
                    power = Power->valueint;
                    repower = (RePower) ? RePower->valueint : 0;
                    soc_up = (Soc_up) ? Soc_up->valueint : (-1);
                    soc_lower = (Soc_lower) ? Soc_lower->valueint : (-1);

                    ems_syslog(LOG_INFO, "schedule %d, %d.%d -> %d.%d [%d] [%d] [%d] [%d]",j,start_hour,start_min,end_hour,end_min,power,repower, soc_up, soc_lower);    
                    g_all_plan[i].schedule[j].StHour = start_hour;
                    g_all_plan[i].schedule[j].StMin = start_min;
                    g_all_plan[i].schedule[j].EnHour = end_hour;
                    g_all_plan[i].schedule[j].EnMin = end_min;
                    g_all_plan[i].schedule[j].Power = power;
                    g_all_plan[i].schedule[j].RePower = repower;
                    g_all_plan[i].schedule[j].SocUp = soc_up;
                    g_all_plan[i].schedule[j].SocLower = soc_lower;
                }
            };
        }
    }
    return 0;
}

int save_ems_user_cfg(usercfg_variant_t *pcfg)
{
    cJSON *root =NULL;
    cJSON *sub =NULL;
    char *string = NULL;
    root = cJSON_CreateObject();
    if (root == NULL)
    {
        ems_syslog(LOG_NOTICE, "cJSON_CreateObject root NULL");
        goto end;
    }

    for(int i =0;i<ems_config_tab_count_g;i++)
    {
        char *key=NULL;
        key=ems_config_tab[i].nod+strlen(DEV_NO_EMS".");
        if(key!=NULL)ems_syslog(LOG_INFO, "key:%s",key);
        switch(ems_config_tab[i].data_type)
        {
            case EMS_POINT_INT:
            {
                if(key!=NULL)
                    {cJSON_AddNumberToObject(root,key,*((int *)((char *)pcfg + ems_config_tab[i].member_ofs)));
                    ems_syslog(LOG_NOTICE, "set:%s :%d",key,*((int *)((char *)pcfg + ems_config_tab[i].member_ofs)));}
            };
            break;
            case EMS_POINT_FLOAT:
            {
                if(key!=NULL)
                    {cJSON_AddNumberToObject(root,key,*((float *)((char *)pcfg + ems_config_tab[i].member_ofs)));
                    ems_syslog(LOG_NOTICE, "set:%s :%f",key,*((float *)((char *)pcfg + ems_config_tab[i].member_ofs)));}
            };
            break;
            
        }
    }
    sub = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "tou", sub);
    for(int i = 0;i<NUM_TOU_SEG_MAX;i++)//最大96段
    {
        cJSON *node = cJSON_CreateObject();
        tou_seg_t *tou = &pcfg->tou[i];
        cJSON_AddNumberToObject(node, "StHour", tou->StHour);
        cJSON_AddNumberToObject(node, "StMin", tou->StMin);
        cJSON_AddNumberToObject(node, "EnHour", tou->EnHour);
        cJSON_AddNumberToObject(node, "EnMin", tou->EnMin);
        cJSON_AddNumberToObject(node, "Power", tou->Power);
        cJSON_AddNumberToObject(node, "RePower", tou->RePower);
        cJSON_AddNumberToObject(node, "SOCUpperLimit", tou->SocUp);
        cJSON_AddNumberToObject(node, "SOCLowerLimit", tou->SocLower);
        ems_syslog(LOG_NOTICE, "Save TOU Segment[%d] [%d:%d] -> [%d:%d] = %d",i+1,tou->StHour,tou->StMin,tou->EnHour,tou->EnMin,tou->Power);
        cJSON_AddItemToArray(sub, node);
    }

    string = cJSON_Print(root);
    if (string != NULL)
    {
        write_file_data(EMS_USER_CFG_FILE, string, strlen(string));
        free(string);
        string=NULL;
    }

end:
    if (root != NULL)
    {
        cJSON_Delete(root);
    }
    return 0;
}

char* create_ems_device(char *tem_file_name)
{
    char device_tmp[2048]={0};
    sprintf(device_tmp,"{\"dev_class\":[{\"type\":\"EMS\",\"devs\":[{\"no\":\""DEV_NO_EMS"\",\"name\":\"EMS\",\"channel\":\"EMS\",\"protocol\":\"ems\",\"template\":\"%s\"}]}]}",tem_file_name);
    return (char*)strdup(device_tmp);
}

char *create_md_server_tag_addr(ems_base_variant_t **tags, size_t tags_size)
{
    char tmp_str[64] = {0};
    char tmp_str2[24] = {0};
    cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        ems_syslog(LOG_NOTICE, "cJSON_CreateObject root NULL");
        return NULL;
    }
    cJSON_AddNumberToObject(root, "server_port", 502);

    struct
    {
        uint8_t f_code;
        uint32_t start_addr;
        uint32_t end_addr;
        char *start_name;
        char *len_name;
    } support[] = {
        {.f_code = 0x01, .start_addr = INVALID_REG_ADDR, .end_addr = 0x00, .start_name = "start_bits", .len_name = "nb_bits"},
        {.f_code = 0x02, .start_addr = INVALID_REG_ADDR, .end_addr = 0x00, .start_name = "start_input_bits", .len_name = "nb_input_bits"},
        {.f_code = 0x03, .start_addr = INVALID_REG_ADDR, .end_addr = 0x00, .start_name = "start_registers", .len_name = "nb_registers"},
        {.f_code = 0x04, .start_addr = INVALID_REG_ADDR, .end_addr = 0x00, .start_name = "start_input_registers", .len_name = "nb_input_registers"},
    };
    // 遍历几遍查找地址空间
    for (int i = 0; i < sizeof(support) / sizeof(support[0]); i++)
    {
        for (int k = 0; k < tags_size; k++)
        {
            if (tags[k]->fun_code == support[i].f_code)
            {
                    if (tags[k]->addr < support[i].start_addr)
                        support[i].start_addr = tags[k]->addr;

                    if ((tags[k]->addr + get_data_order_size(tags[k]->order)) > support[i].end_addr)
                        support[i].end_addr = tags[k]->addr + get_data_order_size(tags[k]->order);
            }
        }

        ems_syslog(LOG_NOTICE, "modbus server addr map:fun:0x%02x, start:0x%x, end:0x%x", support[i].f_code, support[i].start_addr, support[i].end_addr);

        if (support[i].start_addr != INVALID_REG_ADDR)
        {
            snprintf(tmp_str2, sizeof(tmp_str2), "%s", support[i].start_name);
            snprintf(tmp_str, sizeof(tmp_str), "0x%08x", support[i].start_addr);
            cJSON_AddStringToObject(root, tmp_str2, tmp_str);
            //
            snprintf(tmp_str2, sizeof(tmp_str2), "%s", support[i].len_name);
            cJSON_AddNumberToObject(root, tmp_str2, support[i].end_addr - support[i].start_addr);
        }
    }
    // 遍历一遍添加点位
    cJSON *pointInfo = cJSON_CreateArray();
    for (int i = 0; i < tags_size; i++)
    {
        if ((tags[i]->fun_code < 1) || (tags[i]->fun_code > 4))
        {
            continue;
        }
        cJSON *it = cJSON_CreateArray();
        //
        snprintf(tmp_str, sizeof(tmp_str), "%s.%s", tags[i]->dev, tags[i]->variant);
        cJSON_AddItemToArray(it, cJSON_CreateString(tmp_str));
        //
        cJSON_AddItemToArray(it, cJSON_CreateNumber(tags[i]->fun_code));
        //
        snprintf(tmp_str, sizeof(tmp_str), "0x%08x", tags[i]->addr);
        cJSON_AddItemToArray(it, cJSON_CreateString(tmp_str));

        if (get_type_string(tags[i]->type) != NULL)
        {
            cJSON_AddItemToArray(it, cJSON_CreateString(get_type_string(tags[i]->type)));
        }
        else
        {
            ems_syslog(LOG_ERR, "%s get type string is null, type = %d", tags[i]->variant, tags[i]->type);
        }

        if (get_order_string(tags[i]->order) != NULL)
        {
            cJSON_AddItemToArray(it, cJSON_CreateString(get_order_string(tags[i]->order)));
        }
        else
        {
            ems_syslog(LOG_ERR, "%s get order string is null, order = %d", tags[i]->variant, tags[i]->order);
        }
        cJSON_AddItemToArray(it, cJSON_CreateNumber(tags[i]->coefficient));

        cJSON_AddItemToArray(pointInfo, it);
    }
    cJSON_AddItemToObject(root, "pointInfo", pointInfo);

    char *str = cJSON_Print(root);

    cJSON_Delete(root);
    return str;
}

int ems_base_variant_t_cmp(const void *cmp1, const void *cmp2)
{
    const ems_base_variant_t **p1 = (const ems_base_variant_t **)cmp1;
    const ems_base_variant_t **p2 = (const ems_base_variant_t **)cmp2;
    if (p1[0]->fun_code == p2[0]->fun_code)
    {
        return (p1[0]->addr - p2[0]->addr);
    }
    else
    {
        return (p1[0]->fun_code - p2[0]->fun_code);
    }
}

char *create_modbus_server_template(ems_base_variant_t **tags, size_t tags_size)
{
    char *init_json_str = "{\"info\":{\"protocol\":[\"modbus_tcp\"],\"model\":\"V3\",\"compatible\":\"5.312.0000\",\"vendor\":\"LNXALL\",\"adapter\":\"lnxall_lc\"}}";

    cJSON *root = json_parse_string_with_comments(init_json_str);
    if (root == NULL)
    {
        ems_syslog(LOG_NOTICE, "cJSON Parse root NULL");
        return NULL;
    }

    struct
    {
        uint8_t f_code;
        uint32_t start_addr;
        uint32_t end_addr;
    } support[] = {
        {.f_code = 0x01, .start_addr = 0xffff, .end_addr = 0x00},
    };

    qsort(tags, tags_size, sizeof(*tags), ems_base_variant_t_cmp); // 必须递增排序，下面逻辑按照排序后处理
    for (int k = 0; k < tags_size; k++)
    {
        ems_syslog(LOG_NOTICE, "fun:%d, addr:%d", tags[k]->fun_code, tags[k]->addr);
    }

    // 遍历几遍查找地址空间
    char tmp_str[32];
    cJSON *json_arr = cJSON_CreateArray();
    int k = 0;
    for( ;k < tags_size; k++)
    {
        if ((tags[k]->fun_code < 1) || (tags[k]->fun_code > 4))
        {
            continue;
        }
        support[0].f_code = tags[k]->fun_code;
        support[0].start_addr = tags[k]->addr;
        support[0].end_addr = tags[k]->addr;
        for (; k < tags_size; k++)
        {
            if (tags[k]->fun_code != support[0].f_code)
            {
                k--;
                break;
            }

            if (tags[k]->addr < support[0].end_addr)
            {
                //该tag的modbus_server地址有冲突
                ems_syslog(LOG_ERR, "tag[%s] and tag[%s] modbus addr clash", tags[k]->variant, tags[k-1]->variant);
                exit(-1);
            }

            if ((tags[k]->addr - support[0].end_addr) > 20)
            {
                k--;
                break;
            }

            support[0].end_addr = tags[k]->addr + get_data_order_size(tags[k]->order);
            if ((support[0].end_addr - support[0].start_addr) >= 100)
            {
                break;
            }
        }

        cJSON *json_obj = cJSON_CreateObject();
        cJSON *json_sub_arr = cJSON_CreateArray();
        cJSON_AddNumberToObject(json_obj, "scan_rate", 2000);
        cJSON_AddItemToArray(json_sub_arr, cJSON_CreateNumber(support[0].f_code));
        snprintf(tmp_str, sizeof(tmp_str), "0x%08x", support[0].start_addr);
        cJSON_AddItemToArray(json_sub_arr, cJSON_CreateString(tmp_str));
        cJSON_AddItemToArray(json_sub_arr, cJSON_CreateNumber(support[0].end_addr - support[0].start_addr));
        cJSON_AddItemToArray(json_arr, json_obj);
        cJSON_AddItemToObject(json_obj, "prot", json_sub_arr);
        
    }
    cJSON_AddItemToObject(root, "poll_list", json_arr);
#if 1
    //
    cJSON *read_arr = cJSON_CreateArray();
    cJSON *write_arr = cJSON_CreateArray();
    for (int i = 0; i < tags_size; i++)
    {
        if (tags[i]->fun_code > 4)
        {
            ems_syslog(LOG_ERR, "%s fun code err, fun code = %d", tags[i]->variant, tags[i]->fun_code);
            continue;
        }

        cJSON *json_obj = cJSON_CreateObject();
        cJSON *json_sub_arr = cJSON_CreateArray();

        cJSON_AddStringToObject(json_obj, "tag", tags[i]->variant);
        cJSON_AddStringToObject(json_obj, "name", tags[i]->name);
        cJSON_AddStringToObject(json_obj, "property", get_string_property(tags[i]->point_type));
        cJSON_AddStringToObject(json_obj, "history", tags[i]->history_type);
        if (tags[i]->data_type == 0)
        {
            cJSON_AddStringToObject(json_obj, "type", "int");
        }
        else
        {
            cJSON_AddStringToObject(json_obj, "type", "float");
        }
        cJSON_AddNumberToObject(json_obj, "offset", 0);

        double coefficient = (fabsf(tags[i]->coefficient) > 0.00001) ? 1.0 / tags[i]->coefficient : 1;
        cJSON_AddNumberToObject(json_obj, "scale", coefficient);

        cJSON_AddItemToArray(json_sub_arr, cJSON_CreateNumber(tags[i]->fun_code));
        snprintf(tmp_str, sizeof(tmp_str), "0x%08x", tags[i]->addr);
        cJSON_AddItemToArray(json_sub_arr, cJSON_CreateString(tmp_str));
        if (get_type_string(tags[i]->type) != NULL)
        {
            cJSON_AddItemToArray(json_sub_arr, cJSON_CreateString(get_type_string(tags[i]->type)));
        }
        else
        {
            ems_syslog(LOG_ERR, "%s get type string is null, type = %d", tags[i]->variant, tags[i]->type);
        }

        if (get_order_string(tags[i]->order) != NULL)
        {
            cJSON_AddItemToArray(json_sub_arr, cJSON_CreateString(get_order_string(tags[i]->order)));
        }
        else
        {
            ems_syslog(LOG_ERR, "%s get order string is null, order = %d", tags[i]->variant, tags[i]->order);
        }

        switch (tags[i]->fun_code)
        {
        case 0x01:
            cJSON_AddNumberToObject(json_obj, "no_fetch", 0);
            cJSON_AddItemToArray(write_arr, json_obj);
            break;

        case 0x02:
            cJSON_AddItemToArray(read_arr, json_obj);
            cJSON_AddItemToArray(json_sub_arr, cJSON_CreateNumber(0));
            cJSON_AddItemToArray(json_sub_arr, cJSON_CreateNumber(0));
            cJSON_AddItemToArray(json_sub_arr, cJSON_CreateNumber(0));
            break;

        case 0x03:
            if (tags[i]->flag & O_WRONLY)
            {
                cJSON_AddNumberToObject(json_obj, "no_fetch", 0);
                cJSON_AddItemToArray(write_arr, json_obj);
                break;
            }
			/* fall through */

        case 0x04:
            cJSON_AddItemToArray(read_arr, json_obj);
            cJSON_AddItemToArray(json_sub_arr, cJSON_CreateNumber(0));
            cJSON_AddItemToArray(json_sub_arr, cJSON_CreateNumber(0));
            cJSON_AddItemToArray(json_sub_arr, cJSON_CreateNumber(0));
            break;

        default:
            break;
        }

        cJSON_AddItemToObject(json_obj, "prot", json_sub_arr);
        if (tags[i]->desc != NULL)
        {
            cJSON_AddStringToObject(json_obj, "desc", tags[i]->desc);
        }
    }

    cJSON_AddItemToObject(root, "read_reg", read_arr);
    cJSON_AddItemToObject(root, "write_reg", write_arr);
#endif
    char *str = cJSON_Print(root);
    cJSON_Delete(root);

    return str;
}

/*-----------------------导出历史数据--------------------------------*/

void check_and_export_history(cabinet_info_t *pcfg) //每天24.00左右导出一次
{
    time_t t = time(NULL)-86400;//前一天时间
    struct tm target_time = { };  
    struct tm *local_time = &target_time;
    static int saved = 0;
    localtime_r(&t, local_time);
    if ((local_time->tm_hour == 0)&&(local_time->tm_min <= 10))
    {
	    if (saved == 0)
        {
            char cmd[256] = {0};
            const char *disk_path = get_disk_path();
            snprintf(cmd, sizeof(cmd), "df -h |grep %s", disk_path);
            if (is_dir(disk_path)&&(cmd_call(cmd,10,NULL,1)==0))
            {
                saved = 1;
                {
                    char docName[512] = {0};
                    char docPwd[sizeof(docName) + 256] = {0};
                    char tmp1[1024] = {0};
                    char tmp2[sizeof(tmp1) + sizeof(docName) + 128] = {0};
                    sprintf(docName,"history-%s-%04d-%02d-%02d/",pcfg->cab_id,local_time->tm_year+1900,local_time->tm_mon+1,local_time->tm_mday);
                    sprintf(docPwd,"%s"HISTORY_DOC"%s",disk_path,docName);

                    sprintf(tmp1,"mkdir %s -p",docPwd);
                    cmd_call(tmp1,5,NULL,0);
                    sprintf(tmp1,"sqlite3 -header -csv "DBFILE"  \"SELECT name FROM sqlite_master WHERE type='table';\" > %s"INDEX_LIST_NAME,docPwd);
                    cmd_call(tmp1,5,NULL,0);
                    ems_syslog(LOG_INFO, "system:%s",tmp1);
                    FILE *fp; 
                    sprintf(tmp1,"%s"INDEX_LIST_NAME,docPwd);
                    fp = fopen(tmp1, "r");  
                    if (fp == NULL) {  
                        printf("文件不存在\n");
                        return;
                    } else {  
                        while (fgets(tmp1, sizeof(tmp1), fp) != NULL)
                        {
                            ems_syslog(LOG_INFO, "system:%s",tmp1);
                            if (strstr(tmp1,"name")==NULL) {  
                                snprintf(tmp2, sizeof(tmp2), "sqlite3 -header -csv "DBFILE"  \"select datatype,result,(time/1000/1000/10-11644473600) from '%.*s' where  time >= %" PRId64" and time <=%" PRId64" order by time;\" > %s%.*s.csv",(int)(strlen(tmp1)-1),tmp1,ENCODE_TIME_UA(t-60), ENCODE_TIME_UA(t+ONE_DAY),docPwd,(int)(strlen(tmp1)-1),tmp1);
                                cmd_call(tmp2,5,NULL,0);
                                ems_syslog(LOG_INFO, "system:%s",tmp2);
                            }
                        }
                        snprintf(tmp1, sizeof(tmp1), "cd %s"HISTORY_DOC";tar -zcf %.*s.tar.gz  %.*s;rm %.*s -rf",disk_path,(int)strlen(docName)-1,docName,(int)strlen(docName)-1,docName,(int)strlen(docName)-1,docName);
                        cmd_call(tmp1,5,NULL,0);
                        ems_syslog(LOG_INFO, "system:%s",tmp1);
                        fclose(fp); 
                    } 

                    {
                        double mem_used_percent = 0.0f;
                        get_disk_usage(disk_path,&mem_used_percent,NULL,NULL);
                        if (mem_used_percent>USE_PERCENT)
                        {
                            for (size_t i = 0; i < DELETENUM; i++)
                            {
                                tmp1[0] = '\0';
                                snprintf(docName, sizeof(docName), "%s"HISTORY_DOC, disk_path);
                                get_dir_size(docName,tmp1); //能够返回目录大小
                                if (strlen(tmp1)>0)
                                {
                                    sprintf(tmp2,"rm %s%s -f",docName,tmp1);
                                    cmd_call(tmp2,5,NULL,0);
                                }
                                else
                                {
                                    break;
                                }
                            } 
                        }
                    }
                }
            }
        }
    }
    else
    {
        saved = 0;
    }    
}
void *export_history_task(void *args)
{
    while (1)
    {
        sleep(60);
        check_and_export_history(args);
    }
    
}
void export_history_init(cabinet_info_t *pcfg)
{
    pthread_t tid;
    if (0 == pthread_create(&tid,NULL,export_history_task,pcfg))
    {
        pthread_setname_np(tid, "exHistoryTask");
    }
}

int load_lc_list(void)
{
    int re = 0;
    const char *cfg_f = CONFIG_PATH "/" EMS_LC_LIST_FILE;
    pcs_ctrl_var_t *var = get_pcs_ctrl_var();
    var->lc_list_len = 0;
    char *f_data = read_file_data(cfg_f);
    if (f_data == NULL)
    {
        ems_syslog(LOG_CRIT, "读文件错误，请检查文件[%s]是否存在", cfg_f);
        return -1;
    }
    cJSON *root = json_parse_string_with_comments(f_data);
    free(f_data);
    if (!root)
    {
        ems_syslog(LOG_CRIT, "文件[%s]不是json格式", cfg_f);
        re = -2;
        goto load_lc_list_exit;   // 修复：避免后续使用空 root
    }
    cJSON *devices = cJSON_GetObjectItem(root, "devices"); //使用版本的cjson库cJSON_GetObjectItemCaseSensitive这个函数有点坑如果不存在的字段会直接段错误
    if (devices == NULL)
    {
        re = -3;
        goto load_lc_list_exit;
    }
    int devices_cnt = cJSON_GetArraySize(devices);
    if (devices_cnt < 1)
    {
        re = -4;
        goto load_lc_list_exit;
    }

    var->lc_list = calloc(devices_cnt, sizeof(lc_it));
    if (var->lc_list == NULL)
    {
        re = -5;
        goto load_lc_list_exit;
    }
    var->lc_list_len = devices_cnt;
    for (int i = 0; i < devices_cnt; i++)
    {
        cJSON *dev = cJSON_GetArrayItem(devices, i);
        cJSON *sn = cJSON_GetObjectItemCaseSensitive(dev, "sn");

        // 当前仅对IP地址字段，做严格检查，其他字段暂时先不做最严格的检查
        cJSON *ip = cJSON_GetObjectItemCaseSensitive(dev, "ipaddr");

        const char *ipstr =  get_cJSON_valid_ip_str(ip);
        local_strlcpy(var->lc_list[i].ipaddr, ipstr, sizeof(var->lc_list[i].ipaddr));
        
        cJSON *no = cJSON_GetObjectItemCaseSensitive(dev, "no");
        cJSON *name = cJSON_GetObjectItemCaseSensitive(dev, "name");
        cJSON *disable = cJSON_GetObjectItemCaseSensitive(dev, "disabled");
        cJSON *type = cJSON_GetObjectItemCaseSensitive(dev, "type");
        if ((type != NULL) && (disable != NULL) && (no != NULL) && (name != NULL))
        {
            local_strlcpy(var->lc_list[i].no, no->valuestring, sizeof(var->lc_list[i].no));
            local_strlcpy(var->lc_list[i].name, name->valuestring, sizeof(var->lc_list[i].name));
            var->lc_list[i].disabled = disable->valueint;
            //
            if (strcmp(type->valuestring, "lc") == 0) // 实体lc
            {
                var->lc_list[i].type = eLC_TYPE_LC;
            }
            else if (strcmp(type->valuestring, "vlc") == 0) // 虚拟lc
            {
                var->lc_list[i].type = eLC_TYPE_VLC;
            }
            else if (strcmp(type->valuestring, "ems") == 0) // lc
            {
                var->lc_list[i].type = eLC_TYPE_EMS;
                if (get_cabinet_info()->ms_mode == MS_MODE_DC) // 直流柜模式ems不分配功率
                {
                    var->lc_list[i].disabled = 1;
                }
            }
            local_strlcpy(var->lc_list[i].sn, sn->valuestring, sizeof(var->lc_list[i].sn));            
            //
            ems_syslog(LOG_ERR, "dev[%d]:no->%s, sn->%s, ip->%s, disable->%d, type->%d[%s]", 
            i, var->lc_list[i].no, var->lc_list[i].sn, var->lc_list[i].ipaddr, var->lc_list[i].disabled, var->lc_list[i].type, type->valuestring);
        }
        else
        {
            ems_syslog(LOG_CRIT, "从机[%d]配置有错误，配置文件为:%s", i, cfg_f);
            re++;
        }
    }

load_lc_list_exit:
    cJSON_Delete(root);
    return re;
}
