#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/syslog.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 "../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"
#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"
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 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 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(1)
    {
        MicrogridtopologyIni(var);
    }
    else {
        stratagy_period_ctrl_init(var);
    }
    // heat_management_main(get_cabinet_info()->en_lc);// 
    // init_update_ems_hardware_status_task();

    while (1)
    {
        sleep(1);
        now = time(NULL);
        // sem_wait_time(&var->datachange_sem, 0, 500);
        // check_master_ems_offline();
        // update_total_power_sync();
        // g100_protect_task(&g100_protect, now);

        // 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_cha_discharge_status();
        //     }
        //     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")||strstr(board_name, "WLOSANY_X86_V3"))
                // {
                ems_syslog(LOG_ERR, "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\",\"name\":\"%s\",\"channel\":\"%s\",\"protocol\":\"opcua\",\"user_def\":\"is_lc=1;\",\"template\":\"%s\"}]}]}";
    const char *cfg = "{\"dev_class\":[{\"type\":\"LC\",\"devs\":[{\"no\":\"%s\",\"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\",\"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)
{
    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 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;
}

void channel_thread_start(proto_forward_t *proto_forward_p);

static void wait_to_start_protocol(pcs_ctrl_var_t* var)
{
    proto_forward_t* proto_forward_p = get_proto_forward_var();

    proto_forward_later_handle();

    for (size_t i = 0; i < proto_forward_p->channels_size; i++)
    {
        if (proto_forward_p->channels[i]->proto_type != PROTOCOL_TYPE_EMS) continue;

        pthread_mutex_init(&proto_forward_p->channels[i]->channel_lock, NULL);
        int ret = pthread_create(&proto_forward_p->channels[i]->thread, NULL, prot_transf_loop, proto_forward_p->channels[i]);
        if (ret != 0)
        {
            proto_syslog(LOG_WARNING, "failed to created pthread for data process %s", strerror(errno));
        }
        else
        {
            char name_buff[16] = {0};
            int  len           = strlen(proto_forward_p->channels[i]->channel);
            if (len < sizeof(name_buff))
            {
                memcpy(name_buff, proto_forward_p->channels[i]->channel, len);
            }
            else
            {
                memcpy(name_buff, proto_forward_p->channels[i]->channel, 7);
                memcpy(&name_buff[7], "...", 3);
                memcpy(&name_buff[7 + 3], &proto_forward_p->channels[i]->channel[len - 5], 5);
            }
            pthread_setname_np(proto_forward_p->channels[i]->thread, name_buff);
        }
    }

    ua_server_thread_start((UA_SERVER_PARAM*)proto_forward_p->ua_server_param);


    ems_syslog(LOG_ERR,"Waiting for EnProto to be set to 1");

    while (dev_get_dev_tag_int(DEV_NO_EMS, "EnProto") == 0)
    {
        sleep(1);
    }

    channel_thread_start(proto_forward_p);
    proto_forward_schedule(var->device_layer_ptr);
}

static int ems_init(pcs_ctrl_var_t *var)
{
    sig_init();

    // check_and_update_emsd_version("/app/upstart/verlist/emsd_ver.json");
    // 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);
    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)
    {
        load_ems_sys_cfg(&g_usercfg_variant);
        load_ems_user_cfg(&g_usercfg_variant);
//         // TODO 加载完user cfg 同步最新配置
//         update_plan_cfg(var);
//         lc_discover_init(var->sn_str, DISCOVERY_TYPE_EMS, cabinet_info.en_lc);
// 
//         require_cabinet_count(var);

        device_layer_init(var->device_layer_ptr);
        ems_syslog(LOG_NOTICE, "ems config loaded");
        load_device_cfg(var->device_layer_ptr, CONFIG_PATH "/" EMS_DEVICE_MASTER_FILE, 1);

        // ((proto_forward_t *)var->device_layer_ptr)->iec104_param = iec104_server_init();
        // 注册ems一些点的回调函数
        variant_cb_register(var);
    }

    UA_Event_Init(((proto_forward_t *)var->device_layer_ptr)->ua_server_param);

//     if (var->north_mqtt_enable)
//         var->north_mqtt_var = north_mqtt_client_init(&var->mqttparam);
// 
    add_systrm_info(((proto_forward_t *)var->device_layer_ptr)->ua_server_param);
// 
//     pe_adapter_probe();

    wait_to_start_protocol(var);

    // 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_stactrl)
    // {
    //     mqtt_emms2_var_t* mqtt_stactrl_var = mqtt_emms2_client_init(MQTT_STACTRL_STATION_ID);
    //     mqtt_emms2_start(mqtt_stactrl_var);
    // }

    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 = false;
    }

    tmp_json = cJSON_GetObjectItemCaseSensitive(root, "data_persistence");
    if ((tmp_json != NULL) && cJSON_IsNumber(tmp_json))
    {
        pcs_ctrl_var.param.ua_base_data.data_persistence = false ;
    }
    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();
    }
#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 main(int argc, char *argv[])
{
    check_and_handle_mqtt_config();
    log_module_server_init();
    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 = false;
    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",SW_VERSION,__DATE__, __TIME__);
        exit(0);
    }

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