#include "lc.h"
#include "../tag.h"
#include "../common.h"
#include "../ems/main.h"
#include "../opcua/ua_client.h"
#include "../ems/ems_ua.h"
static lc_info_t lcs;
//
static char *lc_status_string[LC_STATUS_MAX + 1] = {
    "OK",
    "DISABLE",
    "OFFLINE",
    "ERR",
    "PROTECT",
    "MAX"};

#define LC_STATUS_2_STRING(e) (lc_status_string[e])

static LC_STATUS_ENUM lc_charge_valid(const char *lc_no)
{
    if (nod_cmp_int(lc_no, STATE_ONLINE, 0) == 0)
    {
        return LC_STATUS_OFFLINE;
    }
    //
    if (nod_cmp_int(lc_no, SYSTEM_STATUS, 0) != 0)
    {
        return LC_STATUS_SYSTEM_ERR;
    }
    
    if (nod_cmp_int(lc_no, PROHIBITE_CHARGE, 0) != 0)
    {
        return LC_STATUS_PROTECT;
    }

    return LC_STATUS_OK;
}

static int lc_discharge_valid(const char *lc_no)
{
    if (nod_cmp_int(lc_no, STATE_ONLINE, 0) == 0)
    {
        return LC_STATUS_OFFLINE;
    }
    //
    if (nod_cmp_int(lc_no, SYSTEM_STATUS, 0) != 0)
    {
        return LC_STATUS_SYSTEM_ERR;
    }
    
    if (nod_cmp_int(lc_no, PROHIBITE_DISCHARGE, 0) != 0)
    {
        return LC_STATUS_PROTECT;
    }

    return LC_STATUS_OK;
}


int set_ChDischStatus(const char *no, double cur_power)
{
    if (IS_CHARGE(cur_power))
    {
        dev_set_dev_tag_int(no, CH_DISCH_STATUS, 1);
    }
    else if
    (IS_DISCHARGE(cur_power))
    {
        dev_set_dev_tag_int(no, CH_DISCH_STATUS, 2);
    }
    else
    {
        dev_set_dev_tag_int(no, CH_DISCH_STATUS, 0);
    }
    return 0;
}


static int update_lc_available(lc_info_t *lc, double power)
{
    int available_sum = 0;
    int cur_sum = lc->lc_sum = get_pcs_ctrl_var()->lc_list_len;
    lc_it *lc_cfg = get_pcs_ctrl_var()->lc_list;
    //
    if (IS_CHARGE(power))
    {
        for (int i = 0; i < cur_sum; i++)
        {
            lc->lc[i].no = lc_cfg[i].no;
            lc->lc[i].is_vlc = lc_cfg[i].type == eLC_TYPE_VLC ? 1 : 0;
            lc->lc[i].status = (lc_cfg[i].disabled == 0) ? lc_charge_valid(lc_cfg[i].no) : LC_STATUS_DISABLE;
            if (lc->lc[i].status == LC_STATUS_OK)
            {
                available_sum++;
            }
        }
    }
    else if (IS_DISCHARGE(power))
    {
        for (int i = 0; i < cur_sum; i++)
        {
            lc->lc[i].no = lc_cfg[i].no;
            lc->lc[i].is_vlc = lc_cfg[i].type == eLC_TYPE_VLC ? 1 : 0;
            lc->lc[i].status = (lc_cfg[i].disabled == 0) ? lc_discharge_valid(lc_cfg[i].no) : LC_STATUS_DISABLE;
            if (lc->lc[i].status == LC_STATUS_OK)
            {
                available_sum++;
            }
        }
    }
    else
    {
        for (int i = 0; i < cur_sum; i++)
        {
            lc->lc[i].no = lc_cfg[i].no;
            lc->lc[i].is_vlc = lc_cfg[i].type == eLC_TYPE_VLC ? 1 : 0;
            lc->lc[i].status = (lc_cfg[i].disabled == 0) ? lc_charge_valid(lc_cfg[i].no) : LC_STATUS_DISABLE;
            if (lc->lc[i].status == LC_STATUS_OK)
            {
                available_sum++;
            }
        }
    }
    lc->valid_sum = available_sum;
    if (nod_cmp_int(DEV_NO_EMS, SYSTEM_STATUS, 0) != 0)
    {
        lc->valid_sum = 0;
        ems_syslog(LOG_ERR, "系统状态异常，导致期望功率为0，从机不可用；请检查系统状态字确认系统状态异常原因");
        return -1;
    }
    return available_sum;
}

static double bms_limit_power(double power, int index, lc_info_t *lc)
{
    double limit = 0;
    if (g_usercfg_variant.en_bms_power_limit != 0)
    {
        const char *cab_no = lc->lc[index].no;

        if (IS_CHARGE(power))
        {
            limit = 0 - dev_get_dev_tag_float(cab_no, MAX_CHARGE_POWER);
            if (power < limit)
            {
                power = limit;
            }
        }
        else
        {
            limit = dev_get_dev_tag_float(cab_no, MAX_DISCHA_POWER);
            if (power > limit)
            {
                power = limit;
            }
        }
    }
    return power;
}

static void print_lc_status(const char *no, LC_STATUS_ENUM status)
{
    switch (status)
    {
    case LC_STATUS_DISABLE:
        ems_syslog(LOG_ERR, "%s禁用,导致期望功率为0", no);
        break;
    case LC_STATUS_OFFLINE:
        ems_syslog(LOG_ERR, "%s离线,导致期望功率为0", no);
        break;
    case LC_STATUS_SYSTEM_ERR:
        ems_syslog(LOG_ERR, "%s系统异常,导致期望功率为0，请查看其系统状态字查询具体原因", no);
        break;
    case LC_STATUS_PROTECT:
        ems_syslog(LOG_ERR, "%s禁充/禁放保护,导致期望功率为0，请查看对BMS的SOC和高压/欠压告警", no);
        break;
    default:
        ems_syslog(LOG_ERR, "%s不可能来执行这儿", no);
        break;
    }
}

static int update_lc_expect_power(lc_info_t *lc, double val)
{
    double subpower = 0;
    int valid_sum = lc->valid_sum;
    ems_syslog(LOG_NOTICE, "cur valid lc sum:%d", valid_sum);
    if (valid_sum < 1)
    {
        ems_syslog(LOG_ERR, "cur valid lc sum:%d", valid_sum);
        subpower = 0;
    }
    else
    {
        // 每个柜子的功率
        subpower = val / (double)valid_sum;
    }

    val = 0;
    LC_STATUS_ENUM status = LC_STATUS_OK;
    for (int i = 0; i < lc->lc_sum; i++)
    {
        status = lc->lc[i].status;
        if (status == LC_STATUS_OK)
        {
            lc->lc[i].exp_powert = bms_limit_power(subpower, i, lc);
            val += lc->lc[i].exp_powert;
        }
        else
        {
            lc->lc[i].exp_powert = 0;
            print_lc_status(lc->lc[i].no, lc->lc[i].status);
        }
        ems_syslog(LOG_NOTICE, "%s status:%d[%s], expect poewr:%f", lc->lc[i].no, status, LC_STATUS_2_STRING(status), lc->lc[i].exp_powert);
    }
    lc->exp_powert = val;
    dev_set_dev_tag_float(DEV_NO_EMS, EXPECT_ACT_POWER, lc->exp_powert);
    return 0;
}
static int vlc_sync_power(lc_info_t *lc)
{
    char pcs_no[TAG_NAME_LEN] = {0};
    for (int i = 0; i < lc->lc_sum; i++)
    {
        if (lc->lc[i].is_vlc == 0)
        {
            continue;
        }
        //
        double exp_powert = lc->lc[i].exp_powert;
        //
        snprintf(pcs_no, TAG_NAME_LEN, "%s." DEV_NO_PCS, lc->lc[i].no);
        if (dev_get_dev_tag_int(DEV_NO_EMS, PCS_AUTO_TURN)) // 使能自动关机
        {
            if (IS_IDLE(exp_powert))
            {
                if (lc->lc[i].start_0_power_time == 0)
                {
                    lc->lc[i].start_0_power_time = time(NULL);
                }
                else
                {
                    extern int last_turn_off_time;
                    if (last_turn_off_time < lc->lc[i].start_0_power_time) // 没有手动控制开关
                    {
                        int time_interval = dev_get_dev_tag_int(DEV_NO_EMS, PCS_AUTO_TURN_OFF_TIME);
                        if ((time(NULL) - lc->lc[i].start_0_power_time >= time_interval) && (0 != def_get_pcs_onoff(pcs_no)))
                        {
                            def_set_pcs_onoff(pcs_no, 0); // 关机
                        }
                    }
                }
            }
            else
            {
                lc->lc[i].start_0_power_time = 0;
                if (0 == def_get_pcs_onoff(pcs_no)) // 没有开机
                {
                    def_set_pcs_onoff(pcs_no, 1); // 开机
                }
            }
        }
        else
        {
            lc->lc[i].start_0_power_time = 0;
            if ((!IS_IDLE(exp_powert)) && (0 == def_get_pcs_onoff(pcs_no))) // 没有开机
            {
                def_set_pcs_onoff(pcs_no, 1); // 开机
            }
        }
        //
        switch (lc->lc[i].status)
        {
        case LC_STATUS_OK:      // 正常
        case LC_STATUS_PROTECT: // 保护
            dev_set_dev_tag_float(lc->lc[i].no, EXPECT_ACT_POWER, lc->lc[i].exp_powert);
            def_set_pcs_power(pcs_no, exp_powert);
            break;
        default:
            ems_syslog(LOG_ERR, "柜[%s]的系统状态错误, 系统状态=%d", lc->lc[i].no, lc->lc[i].status);
            break;
        }
    }

    return 0;
}

static int sync_lc_power(lc_info_t *lc)
{
    const char *dev_no = NULL;
    LC_STATUS_ENUM lc_s = LC_STATUS_OK;

    // 写入功率
    for (int i = 0; i < lc->lc_sum; i++)
    {
        lc_s = lc->lc[i].status;
        dev_no = lc->lc[i].no;
        if (lc_s >= LC_STATUS_MAX)
        {
            ems_syslog(LOG_ERR, "%s.status error, status=%d", dev_no, lc_s);
            continue;
        }

        if (lc_s == LC_STATUS_OFFLINE)
        {
            ems_syslog(LOG_NOTICE, "%s.status error, status=%d", dev_no, lc_s);
            continue;
        }

        if (lc_s == LC_STATUS_DISABLE)
        {
            ems_syslog(LOG_NOTICE, "%s disable, exp_power=%f", dev_no, lc->lc[i].exp_powert);
        }

        //
        dev_set_dev_tag_float(dev_no, SET_ACT_POWER, lc->lc[i].exp_powert);
        // vlc_sync_power(lc);// 为了速度考虑，没有单独使用线程实现，直接做在这儿，后期有必要可以将下面的逻辑放在vlc_deviceLater.c中
    }
    return 0;
}

//
int update_lc_info(void)
{
    double val = get_lc_power();
    ems_syslog(LOG_NOTICE, "local power:%f", val);
    update_lc_available(&lcs, val);
    update_lc_expect_power(&lcs, val);
    sync_lc_power(&lcs);
    return 0;
}

int check_lc_lang(void)
{
    char url_buff[128] = {0};
    static char *local_lang = NULL;
    static char *sync_ok = NULL; // 用于同步标记，如果检测到语言一致的置1，否则置0，并继续尝试同步
    if (local_lang == NULL)
    {
        char *tmp = read_file_data(ENV_LANG_CFG_FILE);
        if (tmp == NULL)
        {
            return -1;
        }
        local_lang = tmp;
    }

    int cur_sum = get_pcs_ctrl_var()->lc_list_len;
    lc_it *lcs = get_pcs_ctrl_var()->lc_list;

    if (sync_ok == NULL)
    {
        sync_ok = calloc(cur_sum, sizeof(char));
        if (sync_ok == NULL)
        {
            ems_syslog(LOG_ERR, "calloc failed");
            return -3;
        }
    }
    for (int i = 0; i < cur_sum; i++)
    {
        if ((lcs[i].type != eLC_TYPE_LC) || (lcs[i].disabled != 0) || (sync_ok[i] != 0))
        {
            ems_syslog(LOG_NOTICE, "%s:[type:%d, disable:%d, sync_ok:%d];local_lang:%s", lcs[i].no, lcs[i].type, lcs[i].disabled, sync_ok[i], local_lang);
            continue;
        }
        snprintf(url_buff, sizeof(url_buff), "opc.tcp://%s:4841", lcs[i].ipaddr);
        char *ret = ua_client_call_read_cfg(url_buff, ENV_LANG);
        if (ret == NULL)
        {
            ems_syslog(LOG_ERR, "UA client calling read_cfg error,url:%s", url_buff);
        }
        else
        {
            ems_syslog(LOG_ERR, "%s:%s;local_lang:%s", url_buff, ret, local_lang);
            if (strcmp(ret, local_lang) == 0)
            {
                sync_ok[i] = 1;
            }
            else
            {
                ret = ua_client_call_write_cfg(url_buff, ENV_LANG, local_lang, strlen(local_lang));
                if (ret == NULL)
                {
                    ems_syslog(LOG_ERR, "UA client calling write_cfg error,url:%s", url_buff);
                }
            }
            free(ret);
        }
    }

    return 0;
}

static char *find_lc_ems_dev_group_cfg_info_by_no(cJSON *root, const char *no)
{
    cJSON *dev_tree = cJSON_GetObjectItem(root, "dev_tree");
    if (dev_tree == NULL)
    {
        ems_syslog(LOG_ERR, "dev_tree is null");
        return NULL;
    }
    int cnt = cJSON_GetArraySize(dev_tree);
    cJSON *target = NULL;
    for (int i = 0; i < cnt; i++)
    {
        cJSON *item = cJSON_GetArrayItem(dev_tree, i);
        if (item == NULL)
        {
            ems_syslog(LOG_ERR, "GetArrayItem error");
            continue;
        }

        cJSON *no_item = cJSON_GetObjectItem(item, "no");
        if ((no_item != NULL) && (strcmp(no_item->valuestring, no) == 0))
        {
            target = item;
        }
    }

    if (target != NULL)
    {
        cJSON *obj = cJSON_CreateObject();
        if (obj !=  NULL)
        {
            dev_tree = cJSON_DetachItemFromObject(target, "dev_tree");
            cJSON_AddItemToObject(obj, "dev_tree", dev_tree);
            return cJSON_PrintUnformatted(obj);
        }
    }
    //
    return NULL;
}

int add_group_to_dev_tree(char **dev_tree)
{
    if (dev_tree == NULL || *dev_tree == NULL)
    {
        return -1;
    }

    cJSON *root = cJSON_Parse(*dev_tree);
    if (root == NULL)
    {
        return -2;
    }

    cJSON *dev_tree_arr = cJSON_GetObjectItem(root, "dev_tree");
    if (!cJSON_IsArray(dev_tree_arr))
    {
        cJSON_Delete(root);
        return -3;
    }

    int arr_size = cJSON_GetArraySize(dev_tree_arr);

    for (int i = 0; i < arr_size; i++)
    {
        cJSON *item = cJSON_GetArrayItem(dev_tree_arr, i);
        if (!cJSON_IsObject(item))
        {
            continue;
        }

        cJSON *type = cJSON_GetObjectItem(item, "type");
        cJSON *no = cJSON_GetObjectItem(item, "no");
        cJSON *sub_dev_tree = cJSON_GetObjectItem(item, "dev_tree");

        if (type && no && strcmp(type->valuestring, "LC") == 0 && cJSON_IsArray(sub_dev_tree))
        {

            int sub_size = cJSON_GetArraySize(sub_dev_tree);

            for (int j = 0; j < sub_size; j++)
            {
                cJSON *sub_item = cJSON_GetArrayItem(sub_dev_tree, j);
                if (!cJSON_IsObject(sub_item))
                {
                    continue;
                }

                cJSON *group = cJSON_GetObjectItem(sub_item, "group");
                if (group)
                {
                    cJSON_ReplaceItemInObject(sub_item, "group", cJSON_CreateString(no->valuestring));
                }
                else
                {
                    cJSON_AddStringToObject(sub_item, "group", no->valuestring);
                }
            }
        }
    }

    char *new_str = cJSON_PrintUnformatted(root);
    cJSON_Delete(root);

    if (new_str)
    {
        free(*dev_tree);
        *dev_tree = new_str;
        return 0;
    }

    return -4;
}
int flush_dev_group_info_file(void)
{
    char *dev_tree = get_all_dev_tree();
    if (dev_tree == NULL)
    {
        ems_syslog(LOG_ERR, "get all dev tree failed");
        return -1;
    }

    int result = add_group_to_dev_tree(&dev_tree);
    if (result != 0)
    {
        ems_syslog(LOG_WARNING, "add group info to devices failed");
        return -2;
    }

    char *old_dev_tree = read_file_data(CONFIG_PATH "/" DEV_GROUP_INFO_NAME".json");
    if ((old_dev_tree == NULL) || (strcmp(old_dev_tree, dev_tree) != 0)){
        write_file_data(CONFIG_PATH "/" DEV_GROUP_INFO_NAME".json", dev_tree, strlen(dev_tree));
        ems_syslog(LOG_INFO, "flush dev tree to %s", CONFIG_PATH "/" DEV_GROUP_INFO_NAME".json");
    }
    if (old_dev_tree != NULL)
    {
        free(old_dev_tree);
    }

    free(dev_tree);
    return 0;
}
int check_lc_group_info(void)
{
    static struct lc_group_info
    {
        // int ok;
        char *group_info;
    } *lc_group_cfg_info = NULL;
    static int lc_group_info_is_ok = 0;

    int cur_sum = get_pcs_ctrl_var()->lc_list_len;
    lc_it *lcs = get_pcs_ctrl_var()->lc_list;

    if (lc_group_info_is_ok == 0)
    {
        if (lc_group_cfg_info == NULL)
        {
            lc_group_cfg_info = calloc(cur_sum, sizeof(struct lc_group_info));
            if (lc_group_cfg_info == NULL)
            {
                return -1;
            }
        }
        //
        char *cfg_name = get_pcs_ctrl_var()->dev_group_file;
        char *tmp = read_file_data(cfg_name);
        if (tmp == NULL)
        {
            free(tmp);
            return -2;
        }
        cJSON *dev_group_info = cJSON_Parse(tmp);
        free(tmp);
        if (dev_group_info == NULL)
        {
            return -3;
        }
        //
        for (int i = 0; i < cur_sum; i++)
        {
            if (lcs[i].type == eLC_TYPE_EMS)
            {
                lc_group_cfg_info[i].group_info = find_lc_ems_dev_group_cfg_info_by_no(dev_group_info, lcs[i].no);
                if (lc_group_cfg_info[i].group_info == NULL)
                {
                    ems_syslog(LOG_WARNING, "没有获取到从机的分组信息:%s", lcs[i].no);
                }
                else
                {
                    ems_syslog(LOG_NOTICE, "%s -> 分组信息:%s", lcs[i].no, lc_group_cfg_info[i].group_info);
                }
            }
        }
        lc_group_info_is_ok = 1;
        cJSON_Delete(dev_group_info);
    }
    //
    for (int i = 0; i < cur_sum; i++)
    {
        if (lcs[i].type != eLC_TYPE_EMS)
        {
            ems_syslog(LOG_NOTICE, "%s:[type:%d, disable:%d", lcs[i].no, lcs[i].type, lcs[i].disabled);
            continue;
        }
        //
        char *lc_cur_group_info = slave_ems_get_dev_group_info(lcs[i].slave);
        if (lc_cur_group_info == NULL)
        {
            ems_syslog(LOG_NOTICE, "%s: 从机当前分组信息为空", lcs[i].no);
        }
        else if (lc_group_cfg_info[i].group_info == NULL)
        {
            ems_syslog(LOG_NOTICE, "%s: 从机配置的分组信息为空", lcs[i].no);
        }
        else if (strcmp(lc_cur_group_info, lc_group_cfg_info[i].group_info) == 0)
        {
            ems_syslog(LOG_NOTICE, "%s: 从机的分组信息没有改变", lcs[i].no);
        }
        else
        {
            // if (slave_ems_set_dev_group_info(lcs[i].slave, lc_cur_group_info) != 0)
            char url[128] = {0};
            snprintf(url, sizeof(url), "opc.tcp://%s:4840", lcs[i].ipaddr);
            char *ret = ua_client_call_write_cfg(url, DEV_GROUP_INFO_NAME, lc_group_cfg_info[i].group_info, strlen(lc_group_cfg_info[i].group_info));
            if (ret == NULL)
            {
                ems_syslog(LOG_ERR, "给从机设置分组信息失败,lc:%s", lcs[i].no);
            }
            else if (strstr(ret, "ok") != NULL)
            {
                ems_syslog(LOG_NOTICE, "给从机设置分组信息成功,lc:%s->%s, %s", lcs[i].no, lc_group_cfg_info[i].group_info, ret);
            }
        }
    }

    return 0;
}

#if 1 // LC点位汇集逻辑
static const char *lc_node_ave_sync_tab[] = {
    CURRENT_SOC,
};


static const char *lc_node_sum_sync_tab[] = {
    ACTIVE_POWER,
    MAX_CHARGE_POWER,
    MAX_DISCHA_POWER,
};

static const char *lc_node_status_sync_tab[] = {
    ON_OFF_STATUS,
    CONNECT_STATUS,
};


typedef struct lc_contex
{
    char no[DEV_NO_LEN];
} lc_contex;
int lc_sum = 0;

static lc_contex g_lc_contex[MAX_LC_SUM_SUPPORT] = {0};

static int lc_collector_variant_monitor_add(pcs_ctrl_var_t *var, const char **node, size_t size, ems_data_par_cb cb)
{
    int err = 0;
    int cur_sum = var->lc_list_len;
    lc_it *lc_cfg = var->lc_list;
    char node_name[TAG_NAME_LEN] = {};

    for (int i = 0; i < cur_sum; i++)
    {
        snprintf(g_lc_contex[i].no, sizeof(g_lc_contex[i].no), "%s", lc_cfg[i].no);
        for (int j = 0; j < size; j++)
        {
            snprintf(node_name, sizeof(node_name), "%s.%s", lc_cfg[i].no, node[j]);
            //
            if (0 != collector_variant_monitor_add_one_with_par(var, node_name, cb, &g_lc_contex[i], 0))
            {
                ems_syslog(LOG_ERR, "vlc add monitor fail:%s", node_name);
            }
            else
            {
                ems_syslog(LOG_NOTICE, "vlc add monitor success:%s", node_name);
            }
        }
    }

    return err;
}
char* extract_tag_name(const char* input) {
    const char* dot_pos = strchr(input, '.');
    
    if (dot_pos != NULL && dot_pos > input) {
        size_t len = strlen(dot_pos + 1);
        char* result = (char*)malloc(len + 1);
        if (result == NULL) return NULL; 

        strcpy(result, dot_pos + 1);
        return result;
    }

    return strdup(input);
}
static void lc_data_sum_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    double sum = 0.0;
    pcs_ctrl_var_t *var = (pcs_ctrl_var_t *)par;

    int cur_sum = var->lc_list_len;
    lc_it *lc_cfg = var->lc_list;
    MS_MODE_ENUM ms_mode = get_cabinet_info()->ms_mode;

    if (ms_mode == MS_MODE_STD) {
        char no[32];
        sum += dev_get_dev_tag_float(DEV_NO_LC, set_tag_name);
        for (int i = 1; i < lc_sum; i++) {
            sprintf(no, DEV_NO_LC "%d", i);
            sum += dev_get_dev_tag_float(no, set_tag_name);
        }
    } else if (ms_mode == MS_MODE_DC) {
        for (int i = 0; i < cur_sum; i++) {
            if (lc_cfg[i].type == eLC_TYPE_VLC)
                continue;
            sum += dev_get_dev_tag_float(lc_cfg[i].no, set_tag_name);
        }
    }

    if (data_value->type == 0) {
        dev_set_dev_tag_int(DEV_NO_EMS, set_tag_name, sum);
    } else {
        dev_set_dev_tag_float(DEV_NO_EMS, set_tag_name, sum);
    }
    free(set_tag_name);
}

static void lc_data_ave_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    double num = 0.0, sum = 0.0, ave = 0.0;
    int valid_count = 0;
    char no[32];
    num = dev_get_dev_tag_float(DEV_NO_LC, set_tag_name);
    if (num > 0.0) {
        sum += num;
        valid_count++;
    }

    for(int i = 1; i < lc_sum; i++) {
        
        sprintf(no, DEV_NO_LC "%d", i);
        num = dev_get_dev_tag_float(no, set_tag_name);
        if (num > 0.0) {
            sum += num;
            valid_count++;
        }
    }

    if (valid_count > 0) {
        ave = sum / valid_count;
    } else {
        ave = 0.0;
    }

    if (data_value->type == 0)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, set_tag_name, ave);
    }
    else
    {
        dev_set_dev_tag_float(DEV_NO_EMS, set_tag_name, ave);
    }
    free(set_tag_name);
}

static void lc_data_status_tab_cb(char *nod, tag_value *data_value, void *par)
{
    char* set_tag_name = extract_tag_name(nod);
    int result = 0;
    char no[32];
    result |= dev_get_dev_tag_int(DEV_NO_LC, set_tag_name);

    for (int i = 1; i < lc_sum; i++) {
        
        sprintf(no, DEV_NO_LC "%d", i);
        result |= dev_get_dev_tag_int(no, set_tag_name);
    }

    dev_set_dev_tag_int(DEV_NO_EMS, set_tag_name, result ? 1 : 0);
    free(set_tag_name);
}

int lc_variant_cb_register(void *var)
{
    lc_sum = ((pcs_ctrl_var_t *)var)->lc_list_len;
    lc_collector_variant_monitor_add((pcs_ctrl_var_t *)var, lc_node_ave_sync_tab, sizeof(lc_node_ave_sync_tab) / sizeof(char *), lc_data_ave_tab_cb);
    lc_collector_variant_monitor_add((pcs_ctrl_var_t *)var, lc_node_sum_sync_tab, sizeof(lc_node_sum_sync_tab) / sizeof(char *), lc_data_sum_tab_cb);
    lc_collector_variant_monitor_add((pcs_ctrl_var_t *)var, lc_node_status_sync_tab, sizeof(lc_node_status_sync_tab) / sizeof(char *), lc_data_status_tab_cb);
    return 0;
}

#endif


int copy_remote_template_to_local(const char *ipaddr, const char *lc_no) {
    char local_path[256];
    snprintf(local_path, sizeof(local_path), LOCAL_TEMPLATE_FILE_PATH_FMT, lc_no);
    char cmd[1024];
    snprintf(cmd, sizeof(cmd),
             "sshpass -p '%s' scp -o StrictHostKeyChecking=no root@%s:%s %s",
             SSH_PASSWORD, ipaddr, REMOTE_TEMPLATE_FILE_PATH, local_path);
    int ret = system(cmd);
    if (ret != 0) {
        ems_syslog(LOG_ERR, "SCP failed: %s", cmd);
        return -1;
    }
    return 0;
}

static char *get_remote_file_md5(const char *ipaddr, const char *remote_file_path)
{
    char cmd[256] = {0};
    snprintf(cmd, sizeof(cmd), "sshpass -p lnxall123 ssh -o StrictHostKeyChecking=no root@%s \"md5sum %s | cut -d' ' -f1 | tr -d '\n'\"", ipaddr, remote_file_path);
    char re_str[256] = {0};
    int ret = getCommandReturn(cmd, re_str, 256);
    ems_syslog(LOG_ERR, "exc_cmd: %s, ret[%d]:%s", cmd, ret, (re_str == NULL ? "NULL" : re_str));
    if ((ret == 0) && (strlen(re_str) == 32))
    {
        return strdup(re_str);
    }
    return NULL;
}

static int copy_remote_file_to_local(const char *ipaddr, const char *remote_file_path, const char *local_file_path)
{
    char cmd[1024] = {0};
    snprintf(cmd, sizeof(cmd),
             "sshpass -p '%s' scp -o StrictHostKeyChecking=no root@%s:%s %s",
             SSH_PASSWORD, ipaddr, remote_file_path, local_file_path);
    int ret = system(cmd);
    if (ret != 0)
    {
        ems_syslog(LOG_ERR, "scp failed: %s", cmd);
        return -1;
    }
    return 0;
}

static int remote_file_sync(const char *ipaddr, const char *remote_file_path, const char *local_file_path)
{
    int ret = -1;
    char *local_md5 = cal_file_md5(local_file_path);
    if (local_md5 != NULL)
    {
        char *remote_md5 = get_remote_file_md5(ipaddr, remote_file_path);
        ems_syslog(LOG_WARNING, "%s md5:%s", local_file_path, local_md5);
        if (remote_md5 != NULL)
        {
            if (strcmp(local_md5, remote_md5) != 0)
            {
                ems_syslog(LOG_WARNING, "%s md5:%s, %s:%s md5:%s", local_file_path, local_md5, ipaddr, remote_file_path, remote_md5);
                copy_remote_file_to_local(ipaddr, remote_file_path, local_file_path);
                ret = 1;
            }
            else
            {
                ems_syslog(LOG_INFO, "%s md5:%s, %s:%s md5:%s", local_file_path, local_md5, ipaddr, remote_file_path, remote_md5);
                ret = 0;
            }
            free(remote_md5);
        }
        free(local_md5);
    }
    return ret;
}
int check_and_sync_lc_template() {
    int cur_sum = get_pcs_ctrl_var()->lc_list_len;
    lc_it *lc_list = get_pcs_ctrl_var()->lc_list;
    static char *sync_ok = NULL;
    char lc_template[256] = {0};
    int need_restart = 0;
    int ret = 0;
    if (sync_ok == NULL) {
        sync_ok = calloc(cur_sum, sizeof(char));
        if (sync_ok == NULL) {
            ems_syslog(LOG_ERR, "calloc failed for sync_ok");
            return -1;
        }
    }

    ems_syslog(LOG_NOTICE, "check_and_sync_lc_template: lc_sum=%d", cur_sum);

    for (int i = 0; i < cur_sum; i++) {
        if ((lc_list[i].type != eLC_TYPE_LC)) {
            ems_syslog(LOG_NOTICE, "skip main LC: %s", lc_list[i].no);
            continue;
        }
        if (sync_ok[i]) {
            ems_syslog(LOG_NOTICE, "LC %s already synced, skip", lc_list[i].no);
            continue;
        }
        snprintf(lc_template, sizeof(lc_template), "/app/config/lc_devs/%s/lc_template.json", lc_list[i].no);
        ret = remote_file_sync(lc_list[i].ipaddr, "/app/template/lc_template.json", lc_template);
        if (ret > 0)
        {
            ret = 0;
            need_restart++;
        }
        else if (ret == 0)
        {
            sync_ok[i] = 1;
        }
        //

    }
    if (need_restart) {
        ems_syslog(LOG_NOTICE, "Template sync done, exiting for restart...");
        exit(25);
    }
    return 0;
}
