#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include "main.h"
#include "../multilanguage.h"
#include "jsonct.h"
#include "signal.h"
#include <poll.h>
#include "lnxall_list.h"
#include "../common.h"
#include "../opcua/ua_client.h"
#include <openssl/md5.h>
#include "../ems/slave_ems.h"

#include "emsctrlProc.h"
#include "collector-api.h"
#include "stratagy_period_ctrl.h"
#include "stratagy_demand_fixed.h"
#include "deviceLayer.h"
#include "../proto_forward.h"
#include "../adapter/pe_adapter.h"
#include "dy_utils/led.h"
#include "ems_ua.h"
#include "income.h"
#include "cloud_mqtt.h"
#include "lc_discovery.h"
#include "discover.h"
#include "../iec104_server/iec104_server.h"
#include "../mqtt_emms2/mqtt_emms2.h"
#include "../alarm_control/alarm_control.h"
#include "../cmdclient.h"
#include "../log_module/log_module.h"
#include "../snapshoot/snapshoot.h"
#include "lc.h"
#include "../north_mqtt/north_mqtt.h"
#include "microgridtopology.h"
#include "../hardware_monitor.h"
#include "log_upper_data.h"
#include "g100_protect.h"
#include "ems_activition.h"
#include "../opt_log.h"
#ifdef EN_TDENGINE
#include "../database/taos_database.h"
#include "../accident/accident.h"
#endif
#include "../mqtt_dp/mqtt_dp.h"
#include "../frpc_proxy/frpc_proxy.h"
#include "../bmser/bmser_alarm.h"
#include "../rate/rate.h"
#include "configuration_adaptation.h"
#include "../abnormal_log/abnormal_log.h"
#include "../business_log/business_log.h"
#include "ui_tag_cfg.h"
#include "../pcs_business_adapter/pcs_business_adapter.h"
static pcs_ctrl_var_t pcs_ctrl_var = {0};
extern double update_plan_cfg(pcs_ctrl_var_t *var);
extern void reflush_ems_data(usercfg_variant_t *pcfg);
pcs_ctrl_var_t *get_pcs_ctrl_var()

{
    return &pcs_ctrl_var;
}

int check_sync_date_time(void)
{
    static int day_t = 0;
    time_t now = time(NULL);
    struct tm *lc = localtime(&now);
    if ((lc->tm_hour == 1) && (day_t != lc->tm_mday))
    {
        dev_set_dev_tag_int(DEV_NO_PCS_MT, "SyncTime", now);
        day_t = lc->tm_mday;
    }
    return 0;
}


void *lc_power_update(void *arg)
{
    if (cabinet_info.ctrl_mode == EMS_MODE_ZC)
    {
        update_lc_info();
    }
    else
    {
        update_cab_info();
    }
    return NULL;
}


static int check_meter(void)
{
    if (dev_get_dev_tag_int(DEV_NO_GRID_MT, STATE_ONLINE) != 0)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, METER_ERROR, 0);
    }
    else
    {
        if (cabinet_info.mode_microgrid == 0 && (g_usercfg_variant.EnProtectTransf || g_usercfg_variant.EnProtectReverse || g_usercfg_variant.EnTraceLoad))
        // 微网，关口表离线问题在微网状态中处理，不进入异常状态
        {
            dev_set_dev_tag_int(DEV_NO_EMS, METER_ERROR, 1);
            return -1;
        }
        else
        {
            dev_set_dev_tag_int(DEV_NO_EMS, METER_ERROR, 0);
        }
    }

    return 0;
}

static int meter_error_clean()
{
    if(dev_get_dev_tag_int(DEV_NO_EMS, METER_ERROR) == 1)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, METER_ERROR, 0);
    }
    return 0;
}

// static int update_load_act_power(void)
// {
//     float pcs_value = dev_get_dev_tag_float(DEV_NO_PCS, ACTIVE_POWER);
//     double P1 = dev_get_dev_tag_float(DEV_NO_GRID_MT, METER_POWER);
// #if 0 /* removed for CC/LC */
//     extern float get_totle_pcs_val();
//     float P3 = P1 + pcs_value + get_totle_pcs_val();
// #else
//     float P3 = P1 + pcs_value;
// #endif
//     dev_set_dev_tag_float(DEV_NO_EMS, LOAD_ACT_POWER,  P3);
//     return 0;
// }

void print_TOU_info(void)
{
    for (size_t i = 0; i < get_tou_max_num(); i++)
    {
        ems_syslog(LOG_WARNING, "current TOU index :%d userdef num:%d",i,g_usercfg_variant.TOU_info[i].userdefNum);
        for (size_t j = 0; j < g_usercfg_variant.TOU_info[i].userdefNum; j++)
        {
            ems_syslog(LOG_WARNING, "current TOU info: %s %s %s %d",
            g_usercfg_variant.TOU_info[i].userdef[j].id,
            g_usercfg_variant.TOU_info[i].userdef[j].name,
            g_usercfg_variant.TOU_info[i].userdef[j].tag,
            g_usercfg_variant.TOU_info[i].userdef[j].value);
        }
    }
    
}
void ems_loop(pcs_ctrl_var_t *var)
{
    time_t last_check_lc = 0;
    time_t last_storage = 0;
    time_t last_flush_dev_group = 0;
    time_t last_domain_check = 0;
    time_t last_check_discha_cha = 0;
    time_t now;

    if (!var->enable_ems) // ems未启动停在这里
    {
        while (1)
            sleep(1);
    }

    reflush_ems_data(&g_usercfg_variant);
    all_plan_init();
    emsctrl_proc_init(var);
    ems_syslog(LOG_WARNING, "wait ems channel idle......");
    sleep(1);
    extern int ems_channel_is_idle(void);
    while (!ems_channel_is_idle())
    {
        usleep(1000 * 50);
    }
    ems_syslog(LOG_WARNING, "ems channel idle......");
    callBaseEvent(SYSTEM_STATUS, 1000, "系统初始化完成!!");
    
    p1_filter_task_init();
    if(get_cabinet_info()->mode_microgrid)
    {
        MicrogridtopologyIni(var);
    }
    else {
        stratagy_period_ctrl_init(var);
    }
    heat_management_main(get_cabinet_info()->en_lc);// 
    init_update_ems_hardware_status_task();
    meter_error_clean();
#ifdef EN_TDENGINE
    accident_Init();
#endif
    while (1)
    {
        sleep(1);
        now = time(NULL);
        // print_TOU_info();
        // sem_wait_time(&var->datachange_sem, 0, 500);
        check_master_ems_offline();
        set_ChDischStatus(DEV_NO_EMS);
        update_total_power_sync();
        g100_protect_task(&g100_protect, now);
        if(cabinet_info.ctrl_mode == EMS_MODE_DD)
        {
            check_voltage_time_std();
            check_DD_mode_pcs_autp_turn_off();
            update_phase_power(EMS_MODE_DD);
        }
        if (check_timeout_second(now, &last_check_lc, 3)) // 计算周期
        {
            set_alarm_cnt(get_alarm_cnt());
            set_hisalarm_cnt(get_alarm_history_count());
            set_sample_period_by_run_state();
            check_sync_date_time();
            // if (0 == check_meter())
            // {
            //     check_peflux_power();
            //     // update_load_act_power();
            // }
            // check_limit_power();
            update_total_energy();
            check_lc_lang();
            get_slave_frpcaddr_by_ua();
            check_lc_ms_mode();
            if (get_cabinet_info()->is_slave == 0) // 占时只考虑主机会更新，从机不用
            {
                check_meter();
                if (check_timeout_second(now, &last_flush_dev_group, 10))
                {
                    flush_dev_group_info_file();
                }
            }
            if (check_timeout_second(now, &last_domain_check, 30) && (get_cabinet_info()->is_slave == 0))
            {
                check_domain_connectivity(&var->domain_list);
            }
            if( check_timeout_second(now, &last_check_discha_cha, 1) && (cabinet_info.ctrl_mode == EMS_MODE_ZC))
            {
                check_ems_and_fault_word_tag_status();
                update_phase_power(EMS_MODE_ZC);
            }
            check_lc_group_info();
            check_and_sync_lc_template();
        }

        if (check_timeout_second(now, &last_storage, 30) || (var->restart_code) || (var->reboot_code == REBOOT_VALUE) || (var->rx_sig != 0)) // 存储
        {
            if (var->config_save)
            {
                save_ems_user_cfg(&g_usercfg_variant);
                var->config_save = false;
                server_add_new_log(LOG_CODE_SAVE_FILE,"EMS",LOG_MODE_TRIG,1,EMS_USER_CFG_FILE);
            }
            if (var->cabinet_save)
            {
                write_cabinet_info_file(&cabinet_info, CONFIG_PATH "/" PROPERTY);
                var->cabinet_save = false;
                server_add_new_log(LOG_CODE_SAVE_FILE,"EMS",LOG_MODE_TRIG,1,CONFIG_PATH "/" PROPERTY);
            }
            if (var->restart_code)
            {
                sleep(1);
                const char *board_name = get_board_name();
                ems_syslog(LOG_NOTICE, "%s restart ...", board_name);
                if (strstr(board_name,"WLOSANY_ECU1170")||strstr(board_name,"WLOSANY_EM1000")||(strstr(board_name,"WLOSANY_RK3568"))||strstr(board_name, "BAU-B30-003")||strstr(board_name, "WLOSANY_BAU-B30-003")||strstr(board_name, "WLOSANY_LC100"))
                {
                    ems_syslog(LOG_NOTICE, "systemctl restart ...");
                    cmd_call("systemctl restart emsd.service",1,NULL,0);
                }
                else
                {
                    ems_syslog(LOG_NOTICE, "[arm 32]restart ...");
                    cmd_call("/etc/init.d/emsd restart",1,NULL,0);

                }
                
            }
            if (var->reboot_code == REBOOT_VALUE)
            {
                cmd_call("reboot -d 20 &",1,NULL,0);
            }
            //
            if (var->rx_sig != 0)
            {
                var->rx_sig = 0;
                ems_syslog(LOG_WARNING, "========================================================= exit(0)");
                exit(0);
            }
        }
    }
}

int data_collect_init(void *parm, proto_forward_param_t *attr_p)
{
    pcs_ctrl_var_t *var = parm;
    proto_forward_param_t *attr = attr_p;
    proto_forward_param_t pf_param = {0};
    if (!parm)
    {
        return -1;
    }
    if (!attr)
    {
        // 设置数采模块参数
        //  pf_param.log_lv = LOG_NOTICE;
        pf_param.enable_open_ua = true;
        pf_param.ua_base_data.data_persistence = false;
        pf_param.ua_base_data.open_history = false;
        pf_param.modbus_ser_data.config_file_pwd = "/app/config/north/modbustcp_north_map.json";
        pf_param.enable_modbus_server = true;
        attr = &pf_param;
    }
    set_default_sample_rare(var->sample_rate); // 需要先在proto_forward_creat之前设置，因为proto_forward_creat内部需要这个默认值
    proto_forward_t *pf_p = proto_forward_creat(attr);
    var->device_layer_ptr = pf_p;
    return 0;
}


static char *template_to_opcua(const char *t_name)
{
    char *f_data = read_file_data(t_name);
    if (f_data == NULL)
    {
        proto_syslog(LOG_ERR, "load file error, file:%s", t_name);
        return NULL;
    }

    cJSON *root = json_parse_string_with_comments(f_data);
    free(f_data);
    f_data = NULL;
    if (!root)
    {
        proto_syslog(LOG_ERR, "parse file to json obj error, file:%s", t_name);
        return NULL;
    }
    //
    cJSON *info = cJSON_GetObjectItemCaseSensitive(root, "info");
    cJSON_DeleteItemFromObjectCaseSensitive(info, "protocol");
    const char *protocol[] = {"opcua"};
    cJSON_AddItemToObject(info, "protocol", cJSON_CreateStringArray(protocol, sizeof(protocol) / sizeof(protocol[0])));
    //
    cJSON_DeleteItemFromObjectCaseSensitive(root, "poll_list");
    //
    cJSON *reg = cJSON_GetObjectItemCaseSensitive(root, "read_reg");
    if (reg != NULL)
    {
        int reg_cnt = cJSON_GetArraySize(reg);
        cJSON *it = NULL;
        for (int i = 0; i < reg_cnt; i++)
        {
            it = cJSON_GetArrayItem(reg, i);
            cJSON_DeleteItemFromObjectCaseSensitive(it, "prot");
            cJSON_AddItemToObject(it, "prot", cJSON_CreateString("")); // 模板里面必须保证有prot
        }
    }

    reg = cJSON_GetObjectItemCaseSensitive(root, "write_reg");
    if (reg != NULL)
    {
        int reg_cnt = cJSON_GetArraySize(reg);
        cJSON *it = NULL;
        for (int i = 0; i < reg_cnt; i++)
        {
            it = cJSON_GetArrayItem(reg, i);
            cJSON_DeleteItemFromObjectCaseSensitive(it, "prot");
            cJSON_DeleteItemFromObjectCaseSensitive(it, "no_fetch");
            cJSON_AddItemToObject(it, "prot", cJSON_CreateString("")); // 模板里面必须保证有prot
        }
    }

    f_data = cJSON_Print(root);
    cJSON_Delete(root);
    return f_data;
}


static int dev_add_vendor_and_model(cJSON *dev)
{
    int ret = 0;
    char f_path[256] = {0};
    cJSON *tp = cJSON_GetObjectItemCaseSensitive(dev, "template");
    if (tp == NULL)
    {
        return -1;
    }
    snprintf(f_path, sizeof(f_path), "%s/%s", TEMPLATE_DIR, tp->valuestring);
    char *pd = read_file_data(f_path);
    if (pd == NULL)
    {
        return -2;
    }
    tp = cJSON_Parse(pd);
    free(pd);
    if (tp == NULL)
    {
        return -3;
    }
    cJSON *info = cJSON_GetObjectItemCaseSensitive(tp, "info");
    cJSON *vm = NULL;
    if (info != NULL)
    {
        vm = cJSON_GetObjectItemCaseSensitive(info, "vendor");
        if (vm != NULL)
        {
            cJSON_AddItemToObject(dev, "vendor", cJSON_CreateString(vm->valuestring));
        }
        else
        {
            ret = -4;
        }
        vm = cJSON_GetObjectItemCaseSensitive(info, "model");
        if (vm != NULL)
        {
            cJSON_AddItemToObject(dev, "model", cJSON_CreateString(vm->valuestring));
        }
        else
        {
            ret -= 100;
        }
    }
    else
    {
        ret = -5;
    }
    cJSON_Delete(tp);
    return ret;
}

int load_lc_device(const lc_it *lc, const char *channel, cJSON **out_dev_cfg)
{
    int ret = 0;
    char buff[TAG_NAME_LEN * 3] = {0};
    char cfg_f[128] = {0};
    snprintf(cfg_f, sizeof(cfg_f), LC_DEVICE_CFG_DIR "/%s/%s", lc->no, COLLECTOR_DEVICE);
    char *f_data = read_file_data(cfg_f);
    if (f_data == NULL)
    {
        proto_syslog(LOG_ERR, "load file error, file:%s", cfg_f);
        return -1;
    }

    cJSON *root = json_parse_string_with_comments(f_data);
    cJSON *ret_root = json_parse_string_with_comments(f_data);
    free(f_data);
    if ((root == NULL) || (ret_root == NULL))
    {
        proto_syslog(LOG_ERR, "parse file to json obj error, file:%s", cfg_f);
        return -2;
    }
    //
    cJSON *dev_class = cJSON_GetObjectItemCaseSensitive(ret_root, "dev_class");
    if (!dev_class)
    {
        proto_syslog(LOG_ERR, "get object dev_class error");
        ret = -3;
        goto load_lc_device_exit;
    }
    int dev_class_cnt = cJSON_GetArraySize(dev_class);
    for (int i = 0; i < dev_class_cnt; i++)
    {
        cJSON *devs = cJSON_GetObjectItemCaseSensitive(cJSON_GetArrayItem(dev_class, i), "devs");
        if (devs == NULL)
        {
            continue;
        }
        int devs_info_cnt = cJSON_GetArraySize(devs);
        for (int j = 0; j < devs_info_cnt; j++)
        {
            cJSON *devs_info = cJSON_GetArrayItem(devs, j);
            dev_add_vendor_and_model(devs_info);
            //
            cJSON *tmp = cJSON_GetObjectItemCaseSensitive(devs_info, "no");
            snprintf(buff, sizeof(buff), "%s.%s", lc->no, tmp->valuestring);
            free(tmp->valuestring);
            tmp->valuestring = strdup(buff);
            //
            tmp = cJSON_GetObjectItemCaseSensitive(devs_info, "name");
            snprintf(buff, sizeof(buff), "%s.%s", lc->name, tmp->valuestring);
            free(tmp->valuestring);
            tmp->valuestring = strdup(buff);
            //
            cJSON_AddItemToObject(devs_info, "group", cJSON_CreateString(lc->no));
        }
    }
    // 下面操作主要是模板的转换和通道转换
    dev_class = cJSON_GetObjectItemCaseSensitive(root, "dev_class");
    if (!dev_class)
    {
        proto_syslog(LOG_ERR, "get object dev_class error");
        ret = -3;
        goto load_lc_device_exit;
    }
    dev_class_cnt = cJSON_GetArraySize(dev_class);
    for (int i = 0; i < dev_class_cnt; i++)
    {
        cJSON *devs = cJSON_GetObjectItemCaseSensitive(cJSON_GetArrayItem(dev_class, i), "devs");
        if (devs == NULL)
        {
            continue;
        }
        int devs_info_cnt = cJSON_GetArraySize(devs);
        for (int j = 0; j < devs_info_cnt; j++)
        {
            cJSON *devs_info = cJSON_GetArrayItem(devs, j);
            //
            cJSON *tmp = cJSON_GetObjectItemCaseSensitive(devs_info, "no");
            snprintf(buff, sizeof(buff), "%s.%s", lc->no, tmp->valuestring);
            free(tmp->valuestring);
            tmp->valuestring = strdup(buff);
            //
            tmp = cJSON_GetObjectItemCaseSensitive(devs_info, "name");
            snprintf(buff, sizeof(buff), "%s.%s", lc->name, tmp->valuestring);
            free(tmp->valuestring);
            tmp->valuestring = strdup(buff);
            //
            if (lc->type == eLC_TYPE_VLC) // 虚拟LC设备不需要执行下面操作
            {
                continue;
            }
            tmp = cJSON_GetObjectItemCaseSensitive(devs_info, "channel");
            snprintf(buff, sizeof(buff), "%s", channel);
            free(tmp->valuestring);
            tmp->valuestring = strdup(buff);
            //
            tmp = cJSON_GetObjectItemCaseSensitive(devs_info, "protocol");
            free(tmp->valuestring);
            tmp->valuestring = strdup("opcua");
            //
            tmp = cJSON_GetObjectItemCaseSensitive(devs_info, "template");
            snprintf(buff, sizeof(buff), "%s/%s", TEMPLATE_DIR, tmp->valuestring);
            if((strcmp(tmp->valuestring, DIDO_TEMPLATE_CONFIG) == 0) && (strcmp(lc->no, "LC") != 0))
            {
                snprintf(buff, sizeof(buff), "/app/config/lc_devs/%s/%s", lc->no, DIDO_TEMPLATE_CONFIG);
            }
            cJSON *user_def = cJSON_GetObjectItemCaseSensitive(devs_info, "user_def");
            check_load_template_config(buff, sizeof(buff), user_def ? user_def->valuestring : NULL);
            char *new_template = template_to_opcua(buff);
            if (new_template == NULL)
            {
                ems_syslog(LOG_ERR, "creat template error:%s", buff);
            }
            else
            {
                char *bname = strdup(basename(buff));
                snprintf(buff, sizeof(buff), "/tmp/%s", bname);
                write_file_data(buff, new_template, strlen(new_template));
                free(new_template);
                free(bname);
            }
            free(tmp->valuestring);
            tmp->valuestring = strdup(buff);
            //
            cJSON_AddItemToObject(devs_info, "group", cJSON_CreateString(lc->no));
            cJSON_AddItemToObject(devs_info, "period", cJSON_CreateNumber(10));
        }
    }
    char *later_str = cJSON_Print(root);
    if (later_str != NULL)
    {
        if (load_device_cfg(pcs_ctrl_var.device_layer_ptr, later_str, 0) < 0)
        {
            proto_syslog(LOG_ERR, "load device cfg error: %s", later_str);
        }
        free(later_str);
    }
    else
    {
        ret = -4;
    }
    
load_lc_device_exit:
    cJSON_Delete(root);
    if (out_dev_cfg != NULL)
    {
        *out_dev_cfg = ret_root;
    }
    else
    {
        cJSON_Delete(ret_root);
    }

    return ret;
}

int load_lc_and_device(void)
{
    char cfg_buff[1024] = {0};
    char channel[64] = {0};
    char template_path[128];
    const char *tmp = "{\"dev_class\":[{\"type\":\"LC\",\"period\":1,\"devs\":[{\"no\":\"%s\",\"type\":\"LC\",\"name\":\"%s\",\"channel\":\"%s\",\"protocol\":\"opcua\",\"user_def\":\"is_lc=1;\",\"template\":\"%s\"}]}]}";
    const char *cfg = "{\"dev_class\":[{\"type\":\"LC\",\"devs\":[{\"no\":\"%s\",\"type\":\"VLC\",\"name\":\"%s\",\"channel\":\"LC\",\"protocol\":\"lc\",\"user_def\":\"is_vlc=1;\",\"template\":\"vlc_template.json\"}]}]}"; // 虚拟LC
    const char *ems = "{\"dev_class\":[{\"type\":\"LC\",\"period\":1,\"devs\":[{\"no\":\"%s\",\"type\":\"EMS\",\"name\":\"%s\",\"channel\":\"%s\",\"protocol\":\"opcua\",\"user_def\":\"is_ems=1;\",\"template\":\"vlc_template.json\"}]}]}"; // EMS
    pcs_ctrl_var_t *var = get_pcs_ctrl_var();
    int lc_list_len = var->lc_list_len;
    lc_it *it_arr = var->lc_list;
    //
    cJSON *dev_cfg = NULL;
    cJSON *ret_cfg = cJSON_Parse("{\"dev_class\":[]}");
    // cJSON *dev_class = cJSON_GetObjectItemCaseSensitive(ret_cfg, "dev_class");
    int ok = 0;
    for (int i = 0; i < lc_list_len; i++)
    {
        switch (it_arr[i].type)
        {
        case eLC_TYPE_EMS:
            snprintf(channel, sizeof(channel), "%s:4840", it_arr[i].ipaddr);
            snprintf(cfg_buff, sizeof(cfg_buff), ems, it_arr[i].no, it_arr[i].name, channel);
            break;
        case eLC_TYPE_VLC:
            snprintf(channel, sizeof(channel), "%s:4841", it_arr[i].ipaddr);
            snprintf(cfg_buff, sizeof(cfg_buff), cfg, it_arr[i].no, it_arr[i].name, channel);
            break;
        case eLC_TYPE_LC:
            snprintf(channel, sizeof(channel), "%s:4841", it_arr[i].ipaddr);
            snprintf(template_path, sizeof(template_path), LOCAL_TEMPLATE_FILE_PATH_FMT, it_arr[i].no);
            if (access(template_path, F_OK) != 0)
            {
                char *template_file = read_file_data("/app/template/lc_template.json");
                if (template_file != NULL)
                {
                    write_file_data(template_path, template_file, strlen(template_file));
                }
            }
            else
            {
                snprintf(cfg_buff, sizeof(cfg_buff), tmp, it_arr[i].no, it_arr[i].name, channel, template_path);
            }
            break;
        default:
            proto_syslog(LOG_ERR, "lc type error: %d", it_arr[i].type);
            break;
        }
        int ret = load_device_cfg(pcs_ctrl_var.device_layer_ptr, cfg_buff, 0);
        if (ret < 0)
        {
            proto_syslog(LOG_ERR, "load device cfg error: %s", tmp);
        }
        if (it_arr[i].type == eLC_TYPE_EMS) // EMS不需要加载设备
        {
            continue;
        }
        ok = load_lc_device(&it_arr[i], channel, &dev_cfg);
        if (0 == ok)
        {
            dev_cfg_merge(ret_cfg, dev_cfg, 0);
            cJSON_Delete(dev_cfg);
        }
        else
        {
            proto_syslog(LOG_CRIT, "加载从机[%s]的设备出错，错误码:%d", it_arr[i].no, ok);
        }
    }
    //
    //
    char *pp = cJSON_Print(ret_cfg);
    write_file_data(EMS_LC_ALL_DEVICE_CFG, pp, strlen(pp));
    cJSON_Delete(ret_cfg);
    free(pp);
    return 0;
}


static void remove_directory(const char *path) {
    DIR *dir = opendir(path);
    if (dir) {
        struct dirent *entry;
        while ((entry = readdir(dir)) != NULL) {
            if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                continue;
            char subpath[396];
            snprintf(subpath, sizeof(subpath), "%s/%s", path, entry->d_name);
            if (entry->d_type == DT_DIR) {
                remove_directory(subpath);
            } else {
                unlink(subpath);
            }
        }
        closedir(dir);
        rmdir(path);
    }
}

static void remove_extra_dirs(const char *base_dir, lc_it *it_arr, int lc_list_len) {
    DIR *main_dir = opendir(base_dir);
    if (!main_dir) return;
    struct dirent *entry;
    while ((entry = readdir(main_dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 ||
            strcmp(entry->d_name, "..") == 0)
            continue;

        char full_path[396];
        snprintf(full_path, sizeof(full_path), "%s/%s", base_dir, entry->d_name);

        struct stat entry_stat;
        if (stat(full_path, &entry_stat) == 0 && S_ISDIR(entry_stat.st_mode)) {
            int found = 0;
            for (int i = 0; i < lc_list_len; i++) {
                if (strcmp(entry->d_name, it_arr[i].no) == 0) {
                    found = 1;
                    break;
                }
            }
            if (!found) {
                remove_directory(full_path);
            }
        }
    }
    closedir(main_dir);
}

int creat_lc_devs_dir(void)
{
    pcs_ctrl_var_t *var = get_pcs_ctrl_var();
    int lc_list_len = var->lc_list_len;
    lc_it *it_arr = var->lc_list;
    char pat[256] = {0};
    struct stat st = {0};
    if (!(stat(LC_DEVICE_CFG_DIR, &st) == 0 && S_ISDIR(st.st_mode)))
    {
        if (mkdir(LC_DEVICE_CFG_DIR, 0755) != 0)
        {
            return -1;
        }
    }
    for (int i = 0; i < lc_list_len; i++)
    {
        snprintf(pat, sizeof(pat), LC_DEVICE_CFG_DIR "/%s", it_arr[i].no);
        memset(&st, 0, sizeof(st));
        if (!(stat(pat, &st) == 0 && S_ISDIR(st.st_mode)))
        {
            if (mkdir(pat, 0755) != 0)
            {
                return (-2 - i);
            }
        }
    }
    remove_extra_dirs(LC_DEVICE_CFG_DIR, it_arr, lc_list_len);
    return 0;
}

int update_device_list(int status)
{
    if (status == -1) //对等模式
    {
        const char *default_content = "{\n    \"devices\": [\n    ]\n}";
        write_file_data(CONFIG_PATH "/" EMS_LC_LIST_FILE, default_content, strlen(default_content));
        return 0;
    }
    char *filename = CONFIG_PATH "/" EMS_LC_LIST_FILE;
    FILE *file = fopen(filename, "r");
    if (!file)
    {
        ems_syslog(LOG_CRIT, "文件不存在: %s", filename);
        return -1;
    }
    fseek(file, 0, SEEK_END);
    long length = ftell(file);
    fseek(file, 0, SEEK_SET);
    char *content = (char *)calloc(1, length + 1);
    if (content)
    {
        fread(content, 1, length, file);
        content[length] = '\0';
    }
    fclose(file);

    char *file_content = content;
    if (!file_content)
    {
        ems_syslog(LOG_ERR, "update_device_list:<error>:Memory space acquisition failed!");
        return -2;
    }

    cJSON *json = cJSON_Parse(file_content);
    free(file_content);

    if (!json)
    {
        ems_syslog(LOG_ERR, "update_device_list:<error>:Error parsing JSON!");
        return -3;
    }

    cJSON *devices = cJSON_GetObjectItem(json, "devices");
    if (!devices || !cJSON_IsArray(devices))
    {
        ems_syslog(LOG_ERR, "update_device_list:<error>:Devices array not found!");
        cJSON_Delete(json);
        return -4;
    }

    cJSON *device_to_check = NULL;
    cJSON *item = NULL;
    int has_device = 0, index = 0;

    cJSON_ArrayForEach(item, devices)
    {

        cJSON *no_item = cJSON_GetObjectItem(item, "no");
        if (no_item && no_item->valuestring && strcmp(no_item->valuestring, "LC") == 0)
        {
            device_to_check = item;
            has_device = 1;
            break;
        }
        index++;
    }
    char ip_addr[20] = "";
    get_localip(pcs_ctrl_var.net_node, ip_addr);
    if (has_device)
    {
        if (device_to_check)
        {
            if (status == 0)
            {
                cJSON_DeleteItemFromArray(devices, index);
                char *updated_json = cJSON_Print(json);
                write_file_data(filename, updated_json, strlen(updated_json));
                free(updated_json);
            }
            else
            {
                status = 0; // 作为文件配置有变动的标记
                if (strlen(ip_addr) < 5)
                {
                    snprintf(ip_addr, sizeof(ip_addr), "127.0.0.1");
                }
                ems_syslog(LOG_WARNING, "device ip: %s", ip_addr);
                item = cJSON_GetObjectItem(device_to_check, "ipaddr");
                if ((item != NULL) && strcmp(item->valuestring, ip_addr) != 0)
                {
                    free(item->valuestring);
                    item->valuestring = strdup(ip_addr);
                    status = 1;
                }
                //
                item = cJSON_GetObjectItem(device_to_check, "sn");
                const char *sn = get_pcs_ctrl_var()->sn_str;
                ems_syslog(LOG_WARNING, "device sn: %s", sn);
                if ((item != NULL) && strcmp(item->valuestring, sn) != 0)
                {
                    free(item->valuestring);
                    item->valuestring = strdup(sn);
                    status = 1;
                }
                //
                item = cJSON_GetObjectItem(device_to_check, "name");
                const char *name = cabinet_info.name;
                ems_syslog(LOG_WARNING, "device name: %s", name);
                if ((item != NULL) && strcmp(item->valuestring, name) != 0)
                {
                    free(item->valuestring);
                    item->valuestring = strdup(name);
                    status = 1;
                }
                //
                if (status != 0)
                {
                    char *updated_json = cJSON_Print(json);
                    ems_syslog(LOG_WARNING, "ems_lc_list: %s", updated_json);
                    write_file_data(filename, updated_json, strlen(updated_json));
                    free(updated_json);
                }
            }
        }
    }
    else
    {
        if (status == 1)
        {
            get_localip(pcs_ctrl_var.net_node, ip_addr);
            ems_syslog(LOG_WARNING, "device ip: %s", ip_addr);
            char lc_sn[50] = "";
            get_board_sn(lc_sn);
            cJSON *new_device = cJSON_CreateObject();
            cJSON_AddStringToObject(new_device, "disabled", "0");
            cJSON_AddStringToObject(new_device, "ipaddr", strlen(ip_addr) > 5 ? ip_addr : "127.0.0.1");
            cJSON_AddStringToObject(new_device, "name", cabinet_info.name);
            cJSON_AddStringToObject(new_device, "no", "LC");
            cJSON_AddStringToObject(new_device, "sn", lc_sn);
            cJSON_AddStringToObject(new_device, "type", "vlc");
            cJSON_InsertItemInArray(devices, 0, new_device);

            char *updated_json = cJSON_Print(json);
            write_file_data(filename, updated_json, strlen(updated_json));
            free(updated_json);
        }
    }
    //
    status = 0;
    cJSON_ArrayForEach(item, devices)
    {
        cJSON *type = cJSON_GetObjectItem(item, "type");
        cJSON *ip = cJSON_GetObjectItem(item, "ipaddr");
        if (type && type->valuestring && strcmp(type->valuestring, "vlc") == 0)
        {
            if (ip && ip->valuestring && strcmp(ip->valuestring, ip_addr) != 0)
            {
                    free(ip->valuestring);
                    ip->valuestring = strdup(ip_addr);
                    status = 1;
            }
        }
        index++;
    }
    if (status != 0)
    {
        char *updated_json = cJSON_Print(json);
        write_file_data(filename, updated_json, strlen(updated_json));
        free(updated_json);
    }
    cJSON_Delete(json);
    return 0;
}

int judge_ssd_exist()
{
    char tmp_c[256] = {0};
    int ret = 0;
    const char *disk_path = get_disk_path();
    snprintf(tmp_c, sizeof(tmp_c), "df -h |grep %s", disk_path);
    ret = cmd_call(tmp_c,10,NULL,1);
    if (ret==0)
    {
        dev_set_dev_tag_int(DEV_NO_EMS,SSD_NOT_EXIST,0);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS,SSD_NOT_EXIST,1);
    }
    return ret;
}

/*
    由于北向版本变更及中间某段时间配置文件内容被删除，导致新老配置文件不兼容、部分网关内配置文件内容为空。
    因此，在程序启动时，需要检查当前北向配置文件版本，并处理。
 */
int check_and_handle_mqtt_config() {
    const char *config_path = "/app/config/north/mqtt_north_cfg.json";
    char *f_data = read_file_data(config_path);  
    if (f_data == NULL) {
        ems_syslog(LOG_ERR, "读取文件失败: %s", config_path);
        return -1;
    }

    cJSON *root = cJSON_Parse(f_data);
    free(f_data);
    if (root == NULL) {
        ems_syslog(LOG_ERR, "解析 JSON 文件失败: %s", config_path);
        goto mv_config;
    }

    cJSON *standard = cJSON_GetObjectItem(root, "standard");
    if (standard == NULL || !cJSON_IsArray(standard)) {
        cJSON_Delete(root);
        goto mv_config;
    }

    int array_size = cJSON_GetArraySize(standard);
    cJSON_Delete(root);

    if (array_size <= 1) {
        goto mv_config;
    } else {
        system("rm -f /app/config/north/mqtt_north_cfg_copy.json");
        return 0;
    }

mv_config:
    system("mv /app/config/north/mqtt_north_cfg_copy.json /app/config/north/mqtt_north_cfg.json");
    return 0;
}

static int ems_init(pcs_ctrl_var_t *var)
{
    // char info[4096] = {0};
    // while(1)
    // {
    //     get_all_temp_info(TEMPLATE_DIR,info);
    //     sleep(1);
    // }

    // 设置外部网络端口
    // set_net_par();
    check_and_update_emsd_version("/app/upstart/verlist/emsd_ver.json");
    //主机北向连通性检查初始化，从机不需要
    if(get_cabinet_info()->is_slave == 0)
    {
        sync_north_cfg_domain(&var->domain_list, N_MQTT_NORTH_CFG_FILE);
        update_sys_cfg_domain(&var->domain_list,MODEFOR4G_PATH);
    }
    update_board_config();
    get_board_sn(var->sn_str);
    // strcpy(var->mqttparam.topic_sn,var->sn_str);
    strcpy(var->mqttparam.topic_sn, "E4B853F333"); // 测试设备 正式的用上面的

    get_process_name(var->proc_name);
    // system_upgrade_ck();
    led_init();
    net_led_check();
    // lc_broadcast_listen_start();
    //检查设备配置文件是否需要更新
    check_device_cfg_update();
    // 加载南向采集点位
    data_collect_init(var, &var->param);
    // 加载南向采集配置
    load_device_cfg(var->device_layer_ptr, CONFIG_PATH "/" COLLECTOR_DEVICE, 1);

    get_rate_cfg();
    
    if (var->enable_ems)
    {
        check_and_creat_vlc_template();
        ems_syslog(LOG_NOTICE, "ems config loading...");
        // 优先系统配置
        // 其次系统配置
        load_TOU_info(&g_usercfg_variant);
        load_ems_sys_cfg(&g_usercfg_variant);
        load_ems_user_cfg(&g_usercfg_variant);
        // TODO 加载完user cfg 同步最新配置
        update_plan_cfg(var);
        if (cabinet_info.ctrl_mode == EMS_MODE_ZC)
        {
            update_device_list(cabinet_info.en_lc);
            int ret = load_lc_list();
            if (ret != 0)
            {
                ems_syslog(LOG_ERR, "load lc list error:%d", ret);
            }
            var->cabinet_cnt = var->lc_list_len; //如果使能LC，柜子数就等于LC数
            int ret_err = creat_lc_devs_dir();
            if (ret_err != 0)
            {
                ems_syslog(LOG_CRIT, "给从机创建目录失败，错误码:%d", ret_err);
            }
            load_lc_and_device();
            //
            for (int i = 0; i < var->lc_list_len; i++)
            {
                if ((var->lc_list[i].type == eLC_TYPE_EMS)  && ((get_cabinet_info()->ms_mode == MS_MODE_DC) || (var->lc_list[i].disabled == 0)))
                {
                    var->lc_list[i].slave = new_slave_ems(var->lc_list[i].ipaddr, var->lc_list[i].no, var->lc_list[i].name, g_usercfg_variant.EnDataCollect);
                    if (var->lc_list[i].slave == NULL)
                    {
                        ems_syslog(LOG_CRIT, "给EMS类型的从机创建客户端失败: %s", var->lc_list[i].no);
                    }
                    else
                    {
                        slave_ems_start(var->lc_list[i].slave);
                    }
                }
            }
        }
        else
        {
            update_device_list(-1); //清空lc_list
            require_cabinet_count(var);
        }
        int en_lc = get_en_lc_to_lc_list("/app/config/ems_lc_list.json");
        lc_discover_init(var->sn_str, DISCOVERY_TYPE_EMS, en_lc);
#ifdef EN_EMS
        if(cabinet_info.en_custom_mqtt)
        {
            ems_syslog(LOG_ERR, "north_cloud_load_nodemap loaded");
            north_cloud_load_nodemap(NORTH_NODEMAP);
        }
#endif
        // 初始化EMS总控点位
        device_layer_init(var->device_layer_ptr);
        ems_syslog(LOG_NOTICE, "ems config loaded");
        // 加载EMS总控
        load_device_cfg(var->device_layer_ptr, CONFIG_PATH "/" EMS_DEVICE_MASTER_FILE, 1);
        UA_SERVER_PARAM *ua_ser_para = ((proto_forward_t *)var->device_layer_ptr)->ua_server_param;
        judge_ssd_exist();
        post_run_led(LED_TWINKLE);
    }
    // 广播数据初始化
    if ((get_cabinet_info()->is_slave == 0)) // 主机才需要这个广播
    {
        discover_init_config init_message;
        init_message.sn       = var->sn_str;
        init_message.capacity = cabinet_info.cab_capacity;
        init_message.power    = cabinet_info.cab_power;
        init_message.K3       = g_usercfg_variant.K3;
        discover_init(&init_message);
    }
    init_alarm_control_task();
    g100_protect_init(&g100_protect);
    init_activition_record();
    init_nondisplay_tags();
#if 0
    {
        int calloc_len = strlen(LC_DEV_JSON_STR_TEMPLATE) + 50;
        char *str_cfg = calloc(1, calloc_len);
        if (str_cfg != NULL)
        {
            snprintf(str_cfg, calloc_len, LC_DEV_JSON_STR_TEMPLATE,"LC", "LC控制器", "127.0.0.1:502");
            load_device_cfg(var->device_layer_ptr, str_cfg, 0);
            free(str_cfg);
        }
        else
        {
            ems_syslog(LOG_ERR, "calloc error, failed to create LC dev");;
        }
    }
#endif
    // ua_del_history_percent(((proto_forward_t *)var->device_layer_ptr)->ua_server_param,20);
    if (var->enable_ems)
    {
        ((proto_forward_t *)var->device_layer_ptr)->iec104_param = iec104_server_init();
        // 注册ems一些点的回调函数
        if (var->enable_group_ctrl)
        {
            // variant_cb_register_mul(var);
        }
        else
        {
            variant_cb_register(var);
        }
    }

    UA_Event_Init(((proto_forward_t *)var->device_layer_ptr)->ua_server_param);
#ifdef EN_TDENGINE
    init_taos_database_tables(var->device_layer_ptr);
#endif
    if (var->north_mqtt_enable)
        var->north_mqtt_var = north_mqtt_client_init(&var->mqttparam);

    if (var->param.ua_base_data.open_history)                                                                                                                                                            // 如果开启历史数据定时监测历史数据
    {
#ifdef EN_SQLITE
        pthread_t tid;
        if (0 == pthread_create(&tid,NULL, HistoryCycleTask, ((proto_forward_t *)var->device_layer_ptr)->ua_server_param))
        {
            pthread_setname_np(tid, "HistoryCycle");
        }
        else
        {
            ems_syslog(LOG_ERR, "HistoryCycleTask create error");
            exit(0);
        }
#endif
    }    
    add_systrm_info(((proto_forward_t *)var->device_layer_ptr)->ua_server_param);
    // creat_cabinet_on_ua(((proto_forward_t *)var->device_layer_ptr)->ua_server_param,&cabinet_info); //创建资产信息目录
    snapshoot_param_s snapshoot_param = {0};
    snapshoot_param.snapshoot_interval = pcs_ctrl_var.snapshoot_interval;
    snapshoot_init(&snapshoot_param);
    proto_forward_later_handle();
    set_write_tag_write_type(var->device_layer_ptr);
    if (((proto_forward_t*)var->device_layer_ptr)->gv != NULL)
    {
        data_presistence_start(((proto_forward_t*)var->device_layer_ptr)->gv, "DataPresistence");
    }
    pe_adapter_probe();
    pcs_business_init();
    
    ((proto_forward_t*)var->device_layer_ptr)->opt_log = (opt_log_s*)new_opt_log("opt_log");
    if(((proto_forward_t*)var->device_layer_ptr)->opt_log != NULL)
    {
        opt_log_start(((proto_forward_t*)var->device_layer_ptr)->opt_log);
    }
    proto_forward_schedule(var->device_layer_ptr);
    if(((proto_forward_t *)var->device_layer_ptr)->iec104_enable == 1)
    {
        proto_syslog(LOG_ERR,"iec104 thread start");
        iec104_server_start(((proto_forward_t *)var->device_layer_ptr)->iec104_param);
    }
    if (cabinet_info.en_mqtt_dp)
    {
        mqtt_dp_start();
    }
    if (cabinet_info.en_mqtt_emms2)
    { //emms2.0
        mqtt_emms2_var_t* mqtt_emms2_var = mqtt_emms2_client_init(MQTT_EMMS2_STATION_ID);
        mqtt_emms2_start(mqtt_emms2_var);
    }
    if (cabinet_info.en_mqtt_stactrl)
    {
        mqtt_emms2_var_t* mqtt_stactrl_var = mqtt_emms2_client_init(MQTT_STACTRL_STATION_ID);
        mqtt_emms2_start(mqtt_stactrl_var);
    }

    parameterSynchronizationIni();
    if (var->north_mqtt_enable) // 启动mqtt北向
        mqtt_north_start(var->north_mqtt_var);
    if (cabinet_info.en_north_mqtt)
        common_north_mqtt_Init();
    // UA_SERVER_PARAM *ua_param = var->ua_server_param;
    //  while(1)
    //  {
    //      sleep(10);
    //      //callEvent(ua_param->ua_server);
    //      // creat_event(ua_param->ua_server);
    //      // trigger_event(ua_param->user_data);
    //  }
    create_monitor_north_connect_status_task();
    var->second_period_timer = my_timer_create();
    if (var->second_period_timer > 0)
    {
        my_timer_set(var->second_period_timer, 1, 1000);
    }
    UD_log_init();
    return 0;
}

void delete_history(void) // 删除历史数据
{
#ifdef EN_OPCUA
 // 路径需要改成配置的路径
    // cmd_call("rm " DBFILE,1,NULL,0);
    if (0 == remove(DBFILE))
    {
        ems_syslog(LOG_NOTICE, "remove %s ok", DBFILE);
    }
    else
    {
        ems_syslog(LOG_ERR, "remove %s error", DBFILE);
    }
#endif
}

int get_snapshoot_interval_by_board_name()
{
    const char *board_name = get_board_name();
    if (strcmp(board_name, BOARD_NAME_6ULL) == 0)
    {
        ems_syslog(LOG_NOTICE, "snapshoot_interval : %d", 30);
        return 30;
    }
    ems_syslog(LOG_NOTICE, "snapshoot_interval : %d", 15);
    return 15;
}

int load_sys_cfg(void)
{
    int re = 0;
    char *cfg_f = PROTO_SYS_CFG_FILE;
    char *f_data = read_file_data(cfg_f);

    if (f_data == NULL)
    {
        printf("load sys config file error, file:%s\n", cfg_f);
        return -1;
    }

    cJSON *root = json_parse_string_with_comments(f_data);
    if (!root)
    {
        printf("parse file to json obj error, file:%s\n", cfg_f);
        re = -2;
        goto load_sys_cfg_exit;
    }

    cJSON *tmp_json = cJSON_GetObjectItemCaseSensitive(root, "sample_rate");
    if ((tmp_json != NULL) && cJSON_IsNumber(tmp_json))
    {
        pcs_ctrl_var.sample_rate = tmp_json->valueint;
        printf("set sample rate %d\n", pcs_ctrl_var.sample_rate);
    }

    tmp_json = cJSON_GetObjectItemCaseSensitive(root, "history");
    if ((tmp_json != NULL) && cJSON_IsNumber(tmp_json))
    {
        pcs_ctrl_var.param.ua_base_data.open_history = (tmp_json->valueint == 0) ? false : true;
    }

    tmp_json = cJSON_GetObjectItemCaseSensitive(root, "data_persistence");
    if ((tmp_json != NULL) && cJSON_IsNumber(tmp_json))
    {
        pcs_ctrl_var.param.ua_base_data.data_persistence = (tmp_json->valueint == 0) ? false : true;
    }
    tmp_json = cJSON_GetObjectItemCaseSensitive(root, "snapshoot_interval");
    if ((tmp_json != NULL) && cJSON_IsNumber(tmp_json))
    {
        pcs_ctrl_var.snapshoot_interval = tmp_json->valueint;
    }
    else
    {
        pcs_ctrl_var.snapshoot_interval = get_snapshoot_interval_by_board_name();
    }
    tmp_json = cJSON_GetObjectItemCaseSensitive(root, "tou_max_num");//可配置 最大96段最小10段 不填写默认按照96段兼容以前的
    if ((tmp_json != NULL) && cJSON_IsNumber(tmp_json))
    {
        pcs_ctrl_var.tou_max_num = tmp_json->valueint>10?tmp_json->valueint:10;
        pcs_ctrl_var.tou_max_num = tmp_json->valueint<96?tmp_json->valueint:96;
    }
    else
    {
        pcs_ctrl_var.tou_max_num = 96;
    }

    tmp_json = cJSON_GetObjectItemCaseSensitive(root, "ch_broadcast_timeout");
    if ((tmp_json != NULL) && cJSON_IsNumber(tmp_json))
    {
        pcs_ctrl_var.ch_broadcast_timeout = tmp_json->valueint;
    }
    else
    {
        pcs_ctrl_var.ch_broadcast_timeout = 10;
    }
#ifdef EN_EMS
    int deg_history_time = 7 * 2; // 默认7天
#endif

#ifdef EN_LC
    int deg_history_time = 2; // 默认2小时
#endif

    tmp_json = cJSON_GetObjectItemCaseSensitive(root, "history_rolling");
    if ((tmp_json != NULL) && cJSON_IsNumber(tmp_json))
    {
        pcs_ctrl_var.history_data_rolling = tmp_json->valueint;
        if (pcs_ctrl_var.history_data_rolling <= 0)
        {
            pcs_ctrl_var.history_data_rolling = deg_history_time;
        }
    }
    else
    {
        pcs_ctrl_var.history_data_rolling = deg_history_time;
    }
    // 数据持久化更新间隔
    tmp_json = cJSON_GetObjectItemCaseSensitive(root, "db_update_interval");
    if ((tmp_json != NULL) && cJSON_IsNumber(tmp_json) && (tmp_json->valueint > 0))
    {
        pcs_ctrl_var.param.db_update_interval = tmp_json->valueint;
    }
    proto_syslog(LOG_NOTICE, "db_update_interval=%d", pcs_ctrl_var.param.db_update_interval);


load_sys_cfg_exit:
    free(f_data);
    if (root != NULL)
    {
        cJSON_Delete(root);
    }

    return re;
}

static void print_help(void)
{
    printf("\n--------------------------------------------------------------------------------\n");
    printf("Usage: proto_forward [OPTIONS]\n");
    printf("\n");
    printf("-v, --version         show this app version\n");
    printf("-m, --mqttclient[url]:[username]:[password]       open north mqtt with url :127.0.0.1:1883:username:password\n");
    printf("-n, --network interface[eth0]  interface ip used by opc, default use hostname\n");
    printf("-d, --delete          delete opc history data\n");
    printf("-e, --ems             enable ems logic\n");
    printf("-u, --disable security        disable ua security mbdeTLS\n");
    printf("-p, --persistence     rollback data from the last history\n");
    printf("-c, --console         default run with deamon witch not log print;if set print to the termal\n");
    printf("-g, --group control    default run with one PCS and BMS;if set to support dispatching and writing to mutil PCS\n");
    printf("-L, --prolevel [level]    proto_forward log level default:4\n");
    printf("-l, --emslevel [level]    ems log level default:4\n");
    printf("                                0:LOG_EMERG      system is unusable                 \n");
    printf("                                1:LOG_ALERT      action must be taken immediately   \n");
    printf("                                2:LOG_CRIT       critical conditions                \n");
    printf("                                3:LOG_ERR        error conditions                   \n");
    printf("                                4:LOG_WARNING    warning conditions                 \n");
    printf("                                5:LOG_NOTICE     normal, but significant, condition \n");
    printf("                                6:LOG_INFO       informational message              \n");
    printf("                                7:LOG_DEBUG      debug-level message                \n");
    printf("-h, --help            show this help\n");
    printf("\n--------------------------------------------------------------------------------\n");
}

int get_tou_max_num(void)
{
    return  pcs_ctrl_var.tou_max_num;
}

int main(int argc, char *argv[])
{
    sig_init();
    lnxall_loglevel_set(LOG_DEBUG,0);   //调用的接口日志等级调为最高
    check_and_handle_mqtt_config();
    log_module_server_init();
    if (abnormal_log_init() != 0) {
        ems_syslog(LOG_ERR, "abnormal_log_init failed");
    }
    load_cabinet_info_cfg(&cabinet_info); // 需要在set_point_desc之前
    set_point_desc();
    init_multilanguage();
    server_add_new_log(LOG_CODE_EMS_START,"EMS",LOG_MODE_TRIG,1,NULL);
    int c;
    bool set_console = false;
    bool getversion = false;
    bool del_history = false;
    int proto_log_level = LOG_WARNING;
    int ems_log_level = LOG_WARNING;
    pcs_ctrl_var.enable_ems = false;
    pcs_ctrl_var.param.enable_open_ua = true;
    pcs_ctrl_var.param.db_update_interval = 60;
    pcs_ctrl_var.param.ua_base_data.data_persistence = false;
    pcs_ctrl_var.param.ua_base_data.open_history = true;
    pcs_ctrl_var.param.ua_base_data.history_num = 60000;
    pcs_ctrl_var.param.ua_base_data.en_encryption = true;
    pcs_ctrl_var.param.modbus_ser_data.config_file_pwd = "/app/config/tag_addr_map.json";
    pcs_ctrl_var.param.enable_modbus_server = true;
    pcs_ctrl_var.north_mqtt_enable = false;
    pcs_ctrl_var.enable_group_ctrl = false;
    pcs_ctrl_var.sample_rate = DEFAULT_SAMPLE_RATE;
    proto_set_log_level(LOG_WARNING);
    ems_set_log_level(LOG_WARNING);
    load_sys_cfg();
    
    pcs_ctrl_var.param.ua_base_data.someExInfo[0] = cabinet_info.cab_id;
    // pcs_ctrl_var.param.ua_base_data.someExInfo[1] = cabinet_info.cab_type;
    // pcs_ctrl_var.param.ua_base_data.someExInfo[2] = cabinet_info.cab_num;
    get_board_sn(pcs_ctrl_var.sn_str);
    pcs_ctrl_var.param.ua_base_data.someExInfo[4] = strdup(pcs_ctrl_var.sn_str);

    printf("g_usercfg_variant.cab_power:%d", g_usercfg_variant.cab_power);
    while ((c = getopt(argc, argv, "n:m:vhtcguepdl:L:")) != -1)
    {
        switch (c)
        {
        case 'v':
            getversion = true;
            break;
        case 'c':
            set_console = true;
            break;
        case 'e':
            pcs_ctrl_var.enable_ems = true;
            break;
        case 'g':
            pcs_ctrl_var.enable_group_ctrl = true;
            break;            
        case 'u':
            pcs_ctrl_var.param.ua_base_data.en_encryption = false;
            break;
        case 'p':
            pcs_ctrl_var.param.ua_base_data.data_persistence = true;
            break;
        case 'd':
            del_history = true;
            break;
        case 't':
        {
            const char *board_name = get_board_name();
            printf("board_name:%s", board_name);
        }
            // #if defined(__arm__)
            // printf("\n---------[arm 32]---------\n");
            // #elif defined(__aarch64__)
            // printf("\n---------[aarch64]---------\n");
            // #endif
            exit(0);
            break;
        case 'L':
            proto_log_level = atoi(optarg);
            break;
        case 'l':
            ems_log_level = atoi(optarg);
            break;
        case 'n':
        {
            pcs_ctrl_var.param.ua_base_data.someExInfo[3] = strdup(optarg);
            pcs_ctrl_var.net_node = strdup(optarg);
            // if(strlen(pcs_ctrl_var.param.ua_base_data.someExInfo[3])>0)
            //     pcs_ctrl_var.param.ua_base_data.ExInfoNum++;
        }
        break;
        case 'm':
            pcs_ctrl_var.north_mqtt_enable = true;
            sscanf(optarg, "%[^:]:%d:%[^:]:%s", pcs_ctrl_var.mqttparam.addr, &pcs_ctrl_var.mqttparam.port, pcs_ctrl_var.mqttparam.user, pcs_ctrl_var.mqttparam.pass);
            printf("\nmqtt_info:%s:%d,%s,%s\n", pcs_ctrl_var.mqttparam.addr, pcs_ctrl_var.mqttparam.port, pcs_ctrl_var.mqttparam.user, pcs_ctrl_var.mqttparam.pass);
            break;
        case 'h':
        case '?':
        default:
            print_help();
            exit(0);
        }
    }
    if (pcs_ctrl_var.net_node == NULL)
    {
        pcs_ctrl_var.net_node = strdup("eth0");
    }

    if (set_console)
    {
        printf("set log out to the console\n");
        openlog(argv[0], LOG_PERROR | LOG_PID, 0);
    }
    else
    {
        openlog(argv[0], LOG_PID, LOG_DAEMON);
    }
    if (proto_log_level <= LOG_DEBUG && proto_log_level >= LOG_EMERG)
    {
        // pcs_ctrl_var.log_lv = proto_log_level;
        proto_set_log_level(proto_log_level);
        printf("set log level:%d\n", proto_log_level);
    }
    if (ems_log_level <= LOG_DEBUG && ems_log_level >= LOG_EMERG)
    {
        ems_set_log_level(ems_log_level);
        printf("set log level:%d\n", ems_log_level);
    }

    if (getversion)
    {
        printf("ver: %s, build at %s %s\n",DISPLAY_VERSION,__DATE__, __TIME__);
        exit(0);
    }

    if (del_history)
    {
        delete_history();
    }
    cmd_client_init("cmdclient");
    ems_syslog(LOG_NOTICE, "%s starting !!!", argv[0]);
    ui_tag_cfg_init();
    ems_init(&pcs_ctrl_var);
    ems_loop(&pcs_ctrl_var);
    return 0;
}
