#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include "define.h"
#include "jsonct.h"
#include "deviceLayer.h"
#include "emsctrlProc.h"
#include "../common.h"
#include "main.h"
#include "../adapter/pe_adapter.h"
#include <math.h>
#include <time.h>
#include "ems_ua.h"
#include "lc_discovery.h"
#include "../proto_forward.h"
#include "automatic.h"
#include "pub_fun.h"
#include "log_module.h"
#include "../multilanguage.h"
#include "microgridtopology.h"
#include <semaphore.h>
#include "vlc_cfg.h"
#include "../business_log/business_log.h"

#define VLC_BUSINESS_LOG(_lev, _id, _no, _fmt, ...) BUSINESS_LOG(_lev, _id, _no, "[虚拟从机]" _fmt, ##__VA_ARGS__)
static void check_voltage_time(void);
typedef struct vlc_power_ctr
{
    const int *on_off;              // 开关机
    const int *prohibite_cha;       // 禁充
    const int *prohibite_disc;      // 禁放
    const int *status;              // 状态
    const double *set_power;        // 设置的有功功率
    const double *set_repower;      // 设置的无功功率
    const double *limit_power;      // 功率功率
    const int *en_bsm_limit;        // 使能bms功率限制
    const double *bms_max_chrge;    // bms最大充电
    const double *bms_max_dischrge; // bms最大放电
    const double *max_soc;
    const double *min_soc;
    //
    const int *dod_cha;        // DOD禁充
    const int *dod_disc;       // DOD禁放
    time_t start_0_power_time; // 0功率开始时间
    double exp_powert;         // 期望有功功率
    double exp_repowert;       // 期望无功功率
    const char *no;
} vlc_power_ctr;

typedef struct vlc_dev_info
{
    char no[DEV_NO_LEN];
    uint32_t stop_dc_flag;    // 禁充标志
    uint32_t stop_ch_flag;    // 禁放标志
    uint32_t cell_volt_flag;  // 单体电压阈值
    uint32_t sys_status_flag; // 系统状态标准
    //
    void *ext_info; // 模块扩展信息
    int voltage_delay_time; // 单体电压延迟时间
    time_t voltage_dc_time; // 单体电压延时时间记录
    time_t voltage_ch_time; // 单体电压延时时间记录
    //
    vlc_power_ctr pwer_ctr_info;
    //
    vlc_cfg_it_list *cfg_list;
} vlc_dev_info;

static vlc_dev_info g_vlc_info[MAX_LC_SUM_SUPPORT] = {0};

static const char *vlc_contrl_tab[] =
    {
        ON_OFF_STATE,
        CLEAR_ALARM,
        SET_CONNECT,
        LC_CLR_MANUAL_PROTECT,
        SET_ACT_POWER,
        SET_REACTPOWER,
        CELL_VOLT_PROTECT_TIME,
        PCS_AUTO_TURN
        // BMS_UPPER,
        // EN_RP_COMP,
        // SET_POWER_FACTOR,
};

static const char *vlc_dod_protect[] =
    {
        "." CURRENT_SOC,
};

// 处理欠压故障
static const char *vlc_stop_discharge_tab[] =
    {
        "." RACK_MIN_VOLT,
        // 我是分界线，上面是特殊处理，加的时候看看回调函数逻辑，这儿有个坑
        "." CELL_UNDER_VOL2,
        "." CELL_UNDER_VOL3,
        "." RACK_UNDER_VOL2,
        "." RACK_UNDER_VOL3,
        DOD_LIMITED_DC,

};

// 处理过压故障
static const char *vlc_stop_charge_tab[] =
    {
        "." RACK_MAX_VOLT,
        // 我是分界线，上面是特殊处理，加的时候看看回调函数逻辑，这儿有个坑
        "." CELL_OVER_VOL2,
        "." CELL_OVER_VOL3,
        "." RACK_OVER_VOL2,
        "." RACK_OVER_VOL3,
        DOD_LIMITED_CH,
};
// 系统异常
static const char *vlc_stop_cab_state_tab[] =
    {
        "." CELL_OVER_VOL3,
        "." RACK_OVER_VOL3,
        "." CELL_UNDER_VOL3,
        "." RACK_UNDER_VOL3,
        DEV_NO_PCS "." PCS_FAULT,
        IO_PROTECT,
};

// 系统异常反逻辑(1为正常，0为异常的点位)
static const char *vlc_stop_cab_state_tab_not[] =
    {
        "." STATE_ONLINE,
        DEV_NO_PCS "." STATE_ONLINE,
};

// 消防报警
static const char *vlc_stop_fire_tab[] = {
    FIRE,
};

static const char *vlc_node_sync_tab[] = {
    "." CURRENT_SOC,
    "." CURRENT_SOH,
    DEV_NO_PCS "." ACTIVE_POWER,
    DEV_NO_PCS "." REACTIVE_POWER,
};

static const char *vlc_maximum_charg_power_tab[] = {
    "." RACK_VOLTAGE,
    "." RACK_MAX_CHARGE_CUR,
};

static const char *vlc_maximum_discharg_power_tab[] = {
    "." RACK_VOLTAGE,
    "." RACK_MAX_DISCHA_CUR,
};

#if 1 // 功率控制

static void *get_tag_value_point_by_tagname(const char *lc_no, const char *tagname, int type)
{
    char dev_tag_name[TAG_MAX_LEN] = {0};
    snprintf(dev_tag_name, sizeof(dev_tag_name), "%s.%s", lc_no, tagname);
    return get_tag_value_point_by_name(dev_tag_name, type);
}

static int vlc_power_ctr_init(vlc_power_ctr *this_p, const char *no)
{
    this_p->no = no;
    this_p->on_off = get_tag_value_point_by_tagname(no, ON_OFF_STATE, 0);
    if (this_p->on_off == NULL)
    {
        return -1;
    }
    this_p->status = get_tag_value_point_by_tagname(no, SYSTEM_STATUS, 0);
    if (this_p->status == NULL)
    {
        return -2;
    }
    this_p->prohibite_cha = get_tag_value_point_by_tagname(no, PROHIBITE_CHARGE, 0);
    if (this_p->prohibite_cha == NULL)
    {
        return -3;
    }
    this_p->prohibite_disc = get_tag_value_point_by_tagname(no, PROHIBITE_DISCHARGE, 0);
    if (this_p->prohibite_disc == NULL)
    {
        return -4;
    }
    this_p->set_power = get_tag_value_point_by_tagname(no, SET_ACT_POWER, 1);
    if (this_p->set_power == NULL)
    {
        return -5;
    }
    this_p->limit_power = get_tag_value_point_by_tagname(no, POWER_LIMIT, 1);
    if (this_p->limit_power == NULL)
    {
        return -6;
    }
    this_p->en_bsm_limit = get_tag_value_point_by_tagname(no, BMS_POWER_LIMIT, 0);
    if (this_p->en_bsm_limit == NULL)
    {
        return -7;
    }

    this_p->bms_max_chrge = get_tag_value_point_by_tagname(no, MAX_CHARGE_POWER, 1);
    if (this_p->bms_max_chrge == NULL)
    {
        return -8;
    }

    this_p->bms_max_dischrge = get_tag_value_point_by_tagname(no, MAX_DISCHA_POWER, 1);
    if (this_p->bms_max_dischrge == NULL)
    {
        return -9;
    }

    this_p->dod_cha = get_tag_value_point_by_tagname(no, DOD_LIMITED_CH, 0);
    if (this_p->dod_cha == NULL)
    {
        return -10;
    }

    this_p->dod_disc = get_tag_value_point_by_tagname(no, DOD_LIMITED_DC, 0);
    if (this_p->dod_disc == NULL)
    {
        return -11;
    }

    this_p->max_soc = get_tag_value_point_by_tagname(no, EMS_SOCMAX, 1);
    if (this_p->max_soc == NULL)
    {
        return -12;
    }

    this_p->min_soc = get_tag_value_point_by_tagname(no, EMS_SOCMIN, 1);
    if (this_p->min_soc == NULL)
    {
        return -13;
    }

    this_p->set_repower = get_tag_value_point_by_tagname(no, SET_REACTPOWER, 1);
    if (this_p->set_repower == NULL)
    {
        return -14;
    }

    return 0;
}

static double power_limit(vlc_power_ctr *this_p)
{
    double set_power = 0, set_repower = 0;
    double tmp = 0;
    //
    if (*(this_p->status) == 0) // 系统正常
    {
        set_power = *(this_p->set_power);
        set_repower = *(this_p->set_repower);
        if (IS_CHARGE(set_power))
        {
            if (*(this_p->prohibite_cha) != 0)
            {
                set_power = 0;
                set_repower = 0;
            }
            else
            {
                if (*(this_p->en_bsm_limit) != 0)
                {
                    tmp = 0 - *(this_p->bms_max_chrge);
                    if (set_power < tmp)
                    {
                        set_power = tmp;
                        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0001, this_p->no, "%s:BMS最大可充限制->%.2f", set_power);
                    }
                    else
                    {
                        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0001, this_p->no, "%s:BMS最大可充限制->解除", this_p->no);
                    }
                }

                tmp = 0 - *(this_p->limit_power);
                if (set_power < tmp)
                {
                    set_power = tmp;
                    VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0002, this_p->no, "%s:功率限制->%.2f", this_p->no, set_power);
                }
                else
                {
                    VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0002, this_p->no, "%s:功率限制->解除", this_p->no);
                }
            }
        }
        else if (IS_DISCHARGE(set_power))
        {
            if (*(this_p->prohibite_disc) != 0)
            {
                set_power = 0;
                set_repower = 0;
            }
            else
            {
                if (*(this_p->en_bsm_limit) != 0)
                {
                    tmp = *(this_p->bms_max_dischrge);
                    if (set_power > tmp)
                    {
                        set_power = tmp;
                        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0003, this_p->no, "%s:BMS最大可放限制->%.2f", this_p->no, set_power);
                    }
                    else
                    {
                        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0003, this_p->no, "%s:BMS最大可放限制->解除", this_p->no);
                    }
                }

                tmp = *(this_p->limit_power);
                if (set_power > tmp)
                {
                    set_power = tmp;
                    VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0004, this_p->no, "%s:功率限制->%.2f", this_p->no, set_power);
                }
                else
                {
                    VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0004, this_p->no, "%s:功率限制->解除", this_p->no);
                }
            }
        }
        else
        {
            set_power = 0;
            //set_repower = 0;  // 暂时按照无功可以单独下发处理
        }
    }
    else
    {
        set_power = 0;
        set_repower = 0;
    }

    this_p->exp_powert = set_power;
    this_p->exp_repowert = set_repower;
    ems_syslog(LOG_NOTICE, "set_power=%f exp_powert=%f, set_repower=%f exp_repowert=%f, prohibite_cha=%d, prohibite_disc=%d, status=%d", *(this_p->set_power), this_p->exp_powert, *(this_p->set_repower), this_p->exp_repowert, *(this_p->prohibite_cha), *(this_p->prohibite_disc), *(this_p->status));
    return set_power;
}

static int vlc_power_sync(vlc_power_ctr *this_p)
{
    power_limit(this_p);
    dev_set_dev_tag_float(this_p->no, EXPECT_ACT_POWER, this_p->exp_powert);
    //
    char pcs_no[TAG_NAME_LEN] = {0};
    snprintf(pcs_no, sizeof(pcs_no), "%s.%s", this_p->no, DEV_NO_PCS);
    int onoff = pe_get_onoff(pcs_no);
    if (onoff >= 0)
    {
        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0005, this_p->no, "%s:获取PCS开关机状态->%d", this_p->no, onoff);
        if (IS_IDLE(this_p->exp_powert))
        {
            pe_set_power(pcs_no, this_p->exp_powert);     // 设置PCS有功功率
            pe_set_repower(pcs_no, this_p->exp_repowert); // 设置PCS无功功率
            if (dev_get_dev_tag_int(this_p->no, PCS_AUTO_TURN) != 0) // 使能自动关机
            {
                if (this_p->start_0_power_time == 0)
                {
                    this_p->start_0_power_time = time(NULL);
                }
                else
                {
                    int time_interval = dev_get_dev_tag_int(this_p->no, PCS_AUTO_TURN_OFF_TIME);
                    if (onoff == PE_ADP_ON)
                    {
                        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0006, this_p->no, "%s:等待PCS关机", this_p->no);
                        if (time(NULL) - this_p->start_0_power_time >= time_interval)
                        {
                            pe_set_onoff(pcs_no, PE_ADP_OFF);
                            VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0006, this_p->no, "%s:设置PCS关机", this_p->no);
                        }
                    }
                }
                return 0;
            }
        }
        //
        this_p->start_0_power_time = 0;
        if ((!(IS_IDLE(this_p->exp_powert))) && (onoff == PE_ADP_OFF)) // 没有开机
        {
            VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0007, this_p->no, "%s:设置PCS关机", this_p->no);
            if (pe_set_onoff(pcs_no, PE_ADP_ON) != 0)
            {
                VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0007, this_p->no, "%s:设置PCS关机失败", this_p->no);
                ems_syslog(LOG_ERR, "设置PCS开关机失败");
            }
        }
        if (0 != pe_set_power(pcs_no, this_p->exp_powert))
        {
            ems_syslog(LOG_ERR, "设置PCS有功功率失败");
        }
        if (0 != pe_set_repower(pcs_no, this_p->exp_repowert))
        {
            ems_syslog(LOG_ERR, "设置PCS无功功率失败");
        }
        //
    }
    else
    {
        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0005, this_p->no, "%s:获取PCS开关机状态失败->%d", this_p->no, onoff);
        ems_syslog(LOG_ERR, "获取%s开关机状态错误", DEV_NO_PCS);
    }

    return 0;
}

#endif

#if 1
typedef struct vlc_power_it
{
    list_head_t list;
    vlc_power_ctr *pwer_ctr_info;
} vlc_power_it;

typedef struct vlc_power
{
    list_head_t list;
    sem_t sem;
    pthread_mutex_t mutex;
} vlc_power;
static vlc_power vp;

typedef struct vlc_pcs_auto_off
{
    time_t set_zero_power_time;
    char vlc_no[64];
} vlc_pcs_auto_off;
static int vlc_power_init(vlc_power *this_p)
{
    if (sem_init(&this_p->sem, 0, 0) < 0)
    {
        return 1;
    }
    INIT_LIST_HEAD(&this_p->list);
    pthread_mutex_init(&this_p->mutex, NULL);
    return 0;
}

static int vlc_power_add(vlc_power *this_p, vlc_power_it *it)
{
    int s_value = 0;
    sem_getvalue(&this_p->sem, &s_value);
    if (s_value > 50)
    {
        ems_syslog(LOG_WARNING, "vlc_power_add: semaphore is full, drop one");
        return -1;
    }
    pthread_mutex_lock(&this_p->mutex);
    list_add_tail(&it->list, &this_p->list);
    pthread_mutex_unlock(&this_p->mutex);
    sem_post(&this_p->sem);
    return 0;
}

static vlc_power_it *vlc_power_wait(vlc_power *this_p, uint32_t timeout_ms)
{
    struct timespec now;
    struct timespec wt;
    vlc_power_it *it = NULL;
    int s_value = 0;
    sem_getvalue(&this_p->sem, &s_value);

    clock_gettime(CLOCK_REALTIME, &now);
    wt.tv_sec = now.tv_sec + timeout_ms / 1000;
    wt.tv_nsec = now.tv_nsec + (timeout_ms % 1000) * 1000 * 1000;
    if (wt.tv_nsec >= 1000000000)
    {
        wt.tv_nsec -= 1000000000;
        wt.tv_sec += 1;
    }
    if (0 == sem_timedwait(&this_p->sem, &wt))
    {
        //
        pthread_mutex_lock(&this_p->mutex);
        if (!list_empty(&this_p->list))
        {
            it = list_first_entry(&this_p->list, typeof(*it), list);
            list_del(&it->list);
        }
        pthread_mutex_unlock(&this_p->mutex);
    }
    return it;
}

#endif

static int vlc_collector_variant_monitor_add(pcs_ctrl_var_t *var, const char **node, size_t size, ems_data_par_cb cb, int type)// type:0->变化回调 1->刷新回调
{
    int err = 0, ret;
    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++)
    {
        if (lc_cfg[i].type == eLC_TYPE_VLC)
        {
            for (int j = 0; j < size; j++)
            {
                if (node[j][0] == '.') //如果是'.'开始，说明前面不需要添加设备no
                {
                    snprintf(node_name, sizeof(node_name), "%s%s", (char*)g_vlc_info[i].ext_info, node[j]);
                }
                else
                {
                    ret = snprintf(node_name, sizeof(node_name), "%s.%s", lc_cfg[i].no, node[j]);
					if (ret < 0 || ret >= sizeof(node_name)) {
						ems_syslog(LOG_WARNING, "node_name too long: %s.%s", lc_cfg[i].no, node[j]);
					}
                }
                //
                if (0 != collector_variant_monitor_add_one_with_par(var, node_name, cb, &g_vlc_info[i], type))
                {
                    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;
}

static void vlc_node_sync_cb(char *nod, tag_value *data_value, void *par);
static void vlc_contrl_cb(char *nod, tag_value *data_value, void *par);
static void vlc_dod_protect_cb(char *nod, tag_value *data_value, void *par);
static void vlc_stop_charge_tab_cb(char *nod, tag_value *data_value, void *par);
static void vlc_stop_discharge_tab_cb(char *nod, tag_value *data_value, void *par);
static void vlc_stop_cab_state_tab_cb(char *nod, tag_value *data_value, void *par);
static void vlc_stop_fire_cb(char *nod, tag_value *data_value, void *par);
static void vlc_maximum_charg_power_cb(char *nod,tag_value *data_value, void *par);
static void vlc_maximum_discharg_power_cb(char *nod,tag_value *data_value, void *par);
int vlc_sync_pcs(const char *lc_no)
{
    char pcs_no[TAG_NAME_LEN] = { 0 };
    snprintf(pcs_no, sizeof(pcs_no), "%s.PCS", lc_no);
    int onoff = def_get_pcs_onoff(pcs_no);
    if (onoff < 0)
    {
        // 底层函数会有打印了, ems_syslog(LOG_ERR, "vlc_sync_pcs: %s, get onoff failed", pcs_no);
        return -1;
    }
    else
    {
        dev_set_dev_tag_int(lc_no, ON_OFF_STATUS, onoff);
    }
    //
    int connect = dev_get_pcs_connect(pcs_no);
    if (connect < 0)
    {
        // 底层函数会有打印了 ems_syslog(LOG_ERR, "vlc_sync_pcs: %s, get connect failed", pcs_no);
        return -1;
    }
    else
    {
            dev_set_dev_tag_int(lc_no, CONNECT_STATUS, connect);

    }
    return 0;
}

int update_vlc_manual_mode_set_zero_time(vlc_pcs_auto_off *manual_mode_pcs_auto_off_time ,vlc_power_ctr *this_p ,int cur_sum)
{
    if(manual_mode_pcs_auto_off_time == NULL || this_p == NULL)
    {
        return -1;
    }
    for(int i = 0; i < cur_sum; i++)
    {
        if (this_p->no == NULL || manual_mode_pcs_auto_off_time[i].vlc_no == NULL)
        {
            ems_syslog(LOG_ERR, "update_vlc_manual_mode_set_zero_time: NULL pointer detected (this_p[%d].no or manual_mode_pcs_auto_off_time->vlc_no)", i);
            continue;
        }
        if(strcmp(this_p->no, manual_mode_pcs_auto_off_time[i].vlc_no) == 0)
        {
            if(IS_IDLE(this_p->exp_powert))
            {
                if(manual_mode_pcs_auto_off_time[i].set_zero_power_time == 0)
                {
                    manual_mode_pcs_auto_off_time[i].set_zero_power_time = time(NULL);
                }
            }else {
                manual_mode_pcs_auto_off_time[i].set_zero_power_time = 0;
            }
        }
    }

    return 0;
}

int check_manual_mode_pcs_auto_off(vlc_pcs_auto_off *manual_mode_pcs_auto_off_time)
{
    if(manual_mode_pcs_auto_off_time == NULL)
    {
        return -1;
    }
    
    pcs_ctrl_var_t *var = get_pcs_ctrl_var();
    int lc_cnt = var->lc_list_len;
    lc_it *lc_list_tmp = var->lc_list;

    for(int i = 0; i < lc_cnt; i++)
    {
        if(lc_list_tmp[i].type != eLC_TYPE_VLC)
        {
            continue;
        }

        for(int j = 0; j < lc_cnt; j++)
        {
            if(strcmp(lc_list_tmp[i].no, manual_mode_pcs_auto_off_time[j].vlc_no) != 0)
            {
                continue;
            }
            if(manual_mode_pcs_auto_off_time[j].set_zero_power_time != 0 && dev_get_dev_tag_int(manual_mode_pcs_auto_off_time[j].vlc_no, PCS_AUTO_TURN) != 0)
            {
                int time_interval = dev_get_dev_tag_int(manual_mode_pcs_auto_off_time[j].vlc_no, PCS_AUTO_TURN_OFF_TIME);
                char pcs_no[TAG_NAME_LEN+32] = {0};
                snprintf(pcs_no, sizeof(pcs_no), "%s.%s", manual_mode_pcs_auto_off_time[j].vlc_no, DEV_NO_PCS);
                int onoff = pe_get_onoff(pcs_no);
                if(onoff == PE_ADP_ON && (time(NULL) - manual_mode_pcs_auto_off_time[j].set_zero_power_time >= time_interval))
                {
                    pe_set_onoff(pcs_no, PE_ADP_OFF);
                    manual_mode_pcs_auto_off_time[i].set_zero_power_time = 0;
                }
            }
        }
    }

    return 0;
}

void *vlc_variant_syncthread(void *par)
{
    pcs_ctrl_var_t *var = (pcs_ctrl_var_t *)par;
    int cur_sum = var->lc_list_len;
    lc_it *lc_cfg = var->lc_list;
    time_t cur_time = time(NULL);
    time_t last_time = cur_time;
    vlc_pcs_auto_off *manual_mode_pcs_auto_off_info = calloc(cur_sum, sizeof(vlc_pcs_auto_off));
    if (!manual_mode_pcs_auto_off_info) {
        ems_syslog(LOG_ERR, "Memory allocation failed for manual_mode_pcs_auto_off_info");
    }

    for (int i = 0; i < cur_sum; i++)
    {
        snprintf(manual_mode_pcs_auto_off_info[i].vlc_no, sizeof(manual_mode_pcs_auto_off_info[i].vlc_no), "%s", lc_cfg[i].no);
    }

    while (1)
    {
        cur_time = time(NULL);
        for (int i = 0; i < cur_sum; i++)
        {
            if (lc_cfg[i].type == eLC_TYPE_VLC)
            {
                vlc_sync_pcs(lc_cfg[i].no);
                set_ChDischStatus(lc_cfg[i].no);
                if(manual_mode_pcs_auto_off_info)
                {
                    snprintf(manual_mode_pcs_auto_off_info[i].vlc_no, sizeof(manual_mode_pcs_auto_off_info[i].vlc_no), "%s", lc_cfg[i].no);
                }
            }
        }
        //
        vlc_power_it *it = vlc_power_wait(&vp, 200);
        while (it != NULL)
        {
            vlc_power_sync(it->pwer_ctr_info);
            //
            if(dev_get_dev_tag_int(it->pwer_ctr_info->no, PCS_AUTO_TURN) != 0)
            {
                update_vlc_manual_mode_set_zero_time(manual_mode_pcs_auto_off_info , it->pwer_ctr_info,cur_sum);
            }
            //
            free(it);
            it = vlc_power_wait(&vp, 200);
        }
        // 周期同步
        if ((cur_time - last_time) > 0) // 1s
        {
            if(g_usercfg_variant.ControlMode == EMS_MANUAL_MODE)
            {
                last_time = cur_time;
                check_manual_mode_pcs_auto_off(manual_mode_pcs_auto_off_info);
            }

            if (g_usercfg_variant.ControlMode == EMS_AUTO_MODE)
            {
                last_time = cur_time;
                for (int i = 0; i < cur_sum; i++)
                {
                    if (lc_cfg[i].type == eLC_TYPE_VLC)
                    {
                        vlc_power_sync(&g_vlc_info[i].pwer_ctr_info);
                    }
                }
            }
            //
            if (dev_get_dev_tag_int(DEV_NO_EMS, EN_CELL_VOLT_PROTECT) != 0)
            {
                check_voltage_time();
            }
            //从机系统运行状态检查
            sync_check_ems_sys_status();
        }
        // 检查配置
        for (int i = 0; i < cur_sum; i++)
        {
            if (lc_cfg[i].type == eLC_TYPE_VLC)
            {
                time_t update_time = vlc_cfg_it_list_get_update_time(g_vlc_info[i].cfg_list);
                if ((update_time != 0) && ((cur_time - update_time) > 10)) // 10s没有变化
                {
                    vlc_cfg_it_list_sync_to_file(g_vlc_info[i].cfg_list);
                }
            }
        }
    }
}


char *get_group_no_by_lc_pcs(const char *lc_no, const char *pcs_no)
{
    const char *file_path = DEV_GROUP_INFO_PATH;
    char *json_str = read_file_data(file_path);
    if (!json_str)
    {
        return NULL;
    }
    cJSON *root = cJSON_Parse(json_str);
    free(json_str);
    if (!root)
    {
        return NULL;
    }
    cJSON *dev_tree = cJSON_GetObjectItemCaseSensitive(root, "dev_tree");
    if (dev_tree == NULL)
    {
        cJSON_Delete(root);
        return NULL;
    }

    int array_size = cJSON_GetArraySize(dev_tree);
    cJSON *target = NULL;
    cJSON *no = NULL;
    cJSON *it = NULL;
    //
    for (int i = 0; i < array_size; i++)
    {
        it = cJSON_GetArrayItem(dev_tree, i);
        no = cJSON_GetObjectItemCaseSensitive(it, "no");
        if ((no != NULL) && cJSON_IsString(no) && (strcmp(no->valuestring, lc_no) == 0))
        {
            //
            target = it;
            dev_tree = cJSON_GetObjectItemCaseSensitive(target, "dev_tree");
            target = NULL;
            if (dev_tree != NULL)
            {
                int len = cJSON_GetArraySize(dev_tree);
                for (int j = 0; j < len; j++)
                {
                    it = cJSON_GetArrayItem(dev_tree, j);
                    no = cJSON_GetObjectItemCaseSensitive(it, "no");
                    if ((no != NULL) && cJSON_IsString(no) && (strcmp(no->valuestring, pcs_no) == 0))
                    {
                        target = it;
                        break;
                    }
                }
            }
        }
    }
    char *result = NULL;
    if (target != NULL)
    {
        cJSON *group = cJSON_GetObjectItemCaseSensitive(target, "group");
        if (group != NULL)
        {
            result = strdup(group->valuestring);
        }
    }
    //

    cJSON_Delete(root);
    return result;
}

char *get_dev_no_by_group(const char *group_no, char *dev_type)
{
    const char *file_path = DEV_GROUP_INFO_PATH;
    char *json_str = read_file_data(file_path);
    if (!json_str)
    {
        return NULL;
    }
    cJSON *root = cJSON_Parse(json_str);
    free(json_str);
    if (!root)
    {
        return NULL;
    }
    cJSON *dev_tree = cJSON_GetObjectItemCaseSensitive(root, "dev_tree");
    if (dev_tree == NULL)
    {
        cJSON_Delete(root);
        return NULL;
    }
    int lc_count = cJSON_GetArraySize(dev_tree);
    char *result = NULL;
    for (int i = 0; i < lc_count && result == NULL; i++)
    {
        cJSON *lc = cJSON_GetArrayItem(dev_tree, i);
        if (!lc) continue;
        cJSON *lc_no = cJSON_GetObjectItemCaseSensitive(lc, "no");
        cJSON *sub_tree = cJSON_GetObjectItemCaseSensitive(lc, "dev_tree");
        if (!sub_tree || !cJSON_IsArray(sub_tree) || !lc_no || !cJSON_IsString(lc_no)) continue;

        int dev_count = cJSON_GetArraySize(sub_tree);
        int bcu_count = 0;
        cJSON *bcu_last = NULL;
        cJSON *bau = NULL;
        for (int j = 0; j < dev_count; j++)
        {
            cJSON *dev = cJSON_GetArrayItem(sub_tree, j);
            if (!dev) continue;
            cJSON *type = cJSON_GetObjectItemCaseSensitive(dev, "type");
            cJSON *subtype = cJSON_GetObjectItemCaseSensitive(dev, "subtype");
            cJSON *group = cJSON_GetObjectItemCaseSensitive(dev, "group");
            if (!type || !cJSON_IsString(type) || !group || !cJSON_IsString(group)) continue;
            if (strcmp(group->valuestring, group_no) != 0) continue;

            if (strcmp(type->valuestring, "BMS") == 0) {
                if (subtype && cJSON_IsString(subtype)) {
                    if (strcmp(subtype->valuestring, "BAU") == 0) {
                        bau = dev;
                        break;
                    } else if (strcmp(subtype->valuestring, "BCU") == 0) {
                        bcu_count++;
                        bcu_last = dev;
                    }
                }
            }
        }
        if (bau) {
            cJSON *bau_no = cJSON_GetObjectItemCaseSensitive(bau, "no");
            if (bau_no && cJSON_IsString(bau_no)) {
                asprintf(&result, "%s.%s", lc_no->valuestring, bau_no->valuestring);
                break;
            }
        } else if (bcu_count > 1) {
            char *group_bau = NULL;
            asprintf(&group_bau, "%s_BAU", group_no);
            asprintf(&result, "%s.%s", lc_no->valuestring, group_bau);
            free(group_bau);
            break;
        } else if (bcu_count == 1 && bcu_last != NULL) {
            cJSON *bcu_no = cJSON_GetObjectItemCaseSensitive(bcu_last, "no");
            if (bcu_no && cJSON_IsString(bcu_no)) {
                asprintf(&result, "%s.%s", lc_no->valuestring, bcu_no->valuestring);
                break;
            }
        }
    }
    cJSON_Delete(root);
    return result;
}

char *get_bms_no_by_lc_no(char *lc_no)
{
    char *g = get_group_no_by_lc_pcs(lc_no, DEV_NO_PCS);
    char *result = NULL;
    if (g != NULL)
    {
        result = get_dev_no_by_group(g, "BMS");
        free(g);
    }
    return result; //
}

static int vlc_device_init(void *par)
{
    int ret = 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;
    switch (get_cabinet_info()->ms_mode)
    {
    case MS_MODE_STD:
    {
        for (int i = 0; i < cur_sum; i++)
        {
            if (lc_cfg[i].type == eLC_TYPE_VLC)
            {
                g_vlc_info[i].voltage_delay_time = dev_get_dev_tag_int(DEV_NO_EMS, CELL_VOLT_PROTECT_TIME);
                int rval = snprintf(g_vlc_info[i].no, sizeof(g_vlc_info[i].no), "%s", lc_cfg[i].no);
				if (rval < 0 || rval >= sizeof(g_vlc_info[i].no)) {
					ems_syslog(LOG_WARNING, "lc no truncated: %s", lc_cfg[i].no);
				}
                char *bms_no = NULL;
                asprintf(&bms_no, "%s.%s", lc_cfg[i].no, DEV_NO_BMS);
                g_vlc_info[i].ext_info = bms_no;
            }
        }
    }
    break;

    case MS_MODE_DC:
    {
        for (int i = 0; i < cur_sum; i++)
        {
            if (lc_cfg[i].type == eLC_TYPE_VLC)
            {
                g_vlc_info[i].voltage_delay_time = dev_get_dev_tag_int(DEV_NO_EMS, CELL_VOLT_PROTECT_TIME);
                int rval = snprintf(g_vlc_info[i].no, sizeof(g_vlc_info[i].no), "%s", lc_cfg[i].no);
				if (rval < 0 || rval >= sizeof(g_vlc_info[i].no)) {
					ems_syslog(LOG_WARNING, "lc no truncated: %s", lc_cfg[i].no);
				}
                g_vlc_info[i].ext_info = get_bms_no_by_lc_no(lc_cfg[i].no);
                if (g_vlc_info[i].ext_info == NULL)
                {
                    ret -= 1;
                    ems_syslog(LOG_ERR, "该分组[%s]没有找到对应的BMS设备", lc_cfg[i].no);
                    VLC_BUSINESS_LOG(BLOG_ERR, VLC_ID_0013, lc_cfg[i].no, "分组%s没有找到对应的BMS", lc_cfg[i].no);
                }
                else
                {
                    VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0013, lc_cfg[i].no, "分组%s找到对应的BMS:%s", lc_cfg[i].no, (char*)g_vlc_info[i].ext_info);
                }
            }
        }
    }
    break;

    default:
    {
        ret = -1;
    }
    break;
    }

    char cfg_path[196] = {0};
    for (int i = 0; i < cur_sum; i++)
    {
        if (lc_cfg[i].type == eLC_TYPE_VLC)
        {
            snprintf(cfg_path, sizeof(cfg_path), "%s/%s/%s", LC_DEVICE_CFG_DIR, lc_cfg[i].no, LC_USER_CFG_FILE);
            g_vlc_info[i].cfg_list = new_vlc_cfg_it_list(cfg_path, lc_cfg[i].no);
        }
    }
    vlc_power_init(&vp);
    for (int i = 0; i < cur_sum; i++)
    {
        if (lc_cfg[i].type == eLC_TYPE_VLC)
        {
            int re = vlc_power_ctr_init(&g_vlc_info[i].pwer_ctr_info, lc_cfg[i].no);
            if (re < 0)
            {
                ret = -100 + re;
                break;
            }
        }
    }
    

    return ret;
}

int vlc_variant_cb_register(pcs_ctrl_var_t *var)
{
    int ret = vlc_device_init(var);
    if (ret < -100)
    {
        VLC_BUSINESS_LOG(BLOG_ERR, VLC_ID_0013, NULL, "虚拟从机初始化失败,是因为从机点位不存在，查看模板目录是否存在，或者硬盘满了无法写入");
        ems_syslog(LOG_CRIT, "虚拟从机初始化失败 错误码:%d", ret);
        sleep(10);
        exit(22);
    }
    vlc_collector_variant_monitor_add(var, vlc_contrl_tab, ARRAY_SIZE(vlc_contrl_tab), vlc_contrl_cb, 1);
    vlc_collector_variant_monitor_add(var, vlc_dod_protect, ARRAY_SIZE(vlc_dod_protect), vlc_dod_protect_cb, 1);
    vlc_collector_variant_monitor_add(var, vlc_stop_discharge_tab, ARRAY_SIZE(vlc_stop_discharge_tab), vlc_stop_discharge_tab_cb, 1);
    vlc_collector_variant_monitor_add(var, vlc_stop_charge_tab, ARRAY_SIZE(vlc_stop_charge_tab), vlc_stop_charge_tab_cb, 1);
    vlc_collector_variant_monitor_add(var, vlc_stop_cab_state_tab, ARRAY_SIZE(vlc_stop_cab_state_tab), vlc_stop_cab_state_tab_cb, 1);
    vlc_collector_variant_monitor_add(var, vlc_stop_cab_state_tab_not, ARRAY_SIZE(vlc_stop_cab_state_tab_not), vlc_stop_cab_state_tab_cb, 1);
    vlc_collector_variant_monitor_add(var, vlc_stop_fire_tab, ARRAY_SIZE(vlc_stop_fire_tab), vlc_stop_fire_cb, 0);
    vlc_collector_variant_monitor_add(var, vlc_node_sync_tab, ARRAY_SIZE(vlc_node_sync_tab), vlc_node_sync_cb, 1);
    vlc_collector_variant_monitor_add(var, vlc_maximum_charg_power_tab, ARRAY_SIZE(vlc_maximum_charg_power_tab), vlc_maximum_charg_power_cb, 0);
    vlc_collector_variant_monitor_add(var, vlc_maximum_discharg_power_tab, ARRAY_SIZE(vlc_maximum_discharg_power_tab), vlc_maximum_discharg_power_cb, 0);
    // automatic_init(CONFIG_PATH "/lc/" AUTOMATIC_CONFIG); // 被主机统一管理
    pthread_t thread = PTHREAD_NULL;
    if (0 == pthread_create(&thread, NULL, vlc_variant_syncthread, var))
    {
        pthread_setname_np(thread, "vlc_sync");
    }
    return 0;
}

static void vlc_node_sync_cb(char *nod, tag_value *data_value, void *par)
{
    const char *vlc_no = ((vlc_dev_info *)par)->no;
    //
    const char *first_dot = strchr(nod, '.');
    if (first_dot == NULL)
    {
        ems_syslog(LOG_ERR, "No '.' found in the string:%s", nod);
        return;
    }
    const char *second_dot = strchr(first_dot + 1, '.');
    if (second_dot == NULL)
    {
        ems_syslog(LOG_ERR, "No second '.' found in the string:%s", nod);
        return;
    }
    if (data_value->type == 0)
    {
        dev_set_dev_tag_int(vlc_no, second_dot + 1, data_value->value.to_int);
    }
    else
    {
        dev_set_dev_tag_float(vlc_no, second_dot + 1, data_value->value.to_float);
    }
}

static void vlc_contrl_cb(char *nod, tag_value *data_value, void *par)
{
    const char *vlc_no = ((vlc_dev_info *)par)->no;
    ems_syslog(LOG_NOTICE, "vlc_contrl_cb:nod=%s,value=%ld, %f, vlc_no=%s", nod, data_value->value.to_int, data_value->value.to_float, vlc_no);
    if ((strstr(nod, CLEAR_ALARM) != NULL) && (data_value->value.to_int != 0))
    {
        dev_set_dev_tag_int(vlc_no, PROHIBITE_CHARGE, 0);
        dev_set_dev_tag_int(vlc_no, PROHIBITE_DISCHARGE, 0);
        dev_set_dev_tag_int(vlc_no, CLEAR_ALARM, 0);
    }
    else if ((strstr(nod, ON_OFF_STATE) != NULL) && (data_value->type == 0))
    {
        char vlc_pcs[TAG_NAME_LEN] = {0};
        snprintf(vlc_pcs, sizeof(vlc_pcs), "%s.%s", vlc_no, DEV_NO_PCS);
        pe_set_onoff(vlc_pcs, data_value->value.to_int);
    }
    else if ((strstr(nod, SET_CONNECT) != NULL) && (data_value->type == 0))
    {
        char vlc_pcs[TAG_NAME_LEN] = {0};
        snprintf(vlc_pcs, sizeof(vlc_pcs), "%s.%s", vlc_no, DEV_NO_PCS);
        pe_set_connect(vlc_pcs, data_value->value.to_int);
    }
    else if ((strstr(nod, LC_CLR_MANUAL_PROTECT) != NULL) && (data_value->type == 0))
    {
        dev_set_dev_tag_int(vlc_no, LC_CLR_MANUAL_PROTECT, 0);
        dev_set_dev_tag_int(vlc_no, LC_MANUAL_PROTECT, 0);
    }
    else if (strstr(nod, "." SET_ACT_POWER) != NULL || strstr(nod, "." SET_REACTPOWER) != NULL)
    {
        vlc_power_it *it = calloc(1, sizeof(vlc_power_it));
        if (it == NULL)
        {
            ems_syslog(LOG_ERR, "calloc error");
        }
        else
        {
            it->pwer_ctr_info = &((vlc_dev_info *)par)->pwer_ctr_info;
            vlc_power_add(&vp, it);
        }
    }
    else if (strcmp(nod, DEV_NO_EMS "." BMS_UPPER) == 0)
    {
        // xcl todo 主机里面做了
        // set_total_bms_connect(data_value->value.to_int);
    }
    else if (strcmp(nod, EN_RP_COMP) == 0) // PCS 无功功率补偿,设置功率因数
    {
        // xcl todo 主机里面做了
        // set_total_pcs_repower_comp(data_value->value.to_int);
    }
    else if (strcmp(nod, SET_POWER_FACTOR) == 0) // PCS 无功功率补偿,设置功率因数
    {
        // xcl todo 主机里面做了
        // set_total_pcs_power_factor(data_value->value.to_float);
    }
    else if (strstr(nod, CELL_VOLT_PROTECT_TIME) != NULL) // PCS 功率限制
    {
        ((vlc_dev_info *)par)->voltage_delay_time = data_value->value.to_int;
    }
    else if (strstr(nod, PCS_AUTO_TURN) != NULL)
    {
        if(g_usercfg_variant.ControlMode == EMS_MANUAL_MODE && data_value->value.to_int == 1 && dev_get_dev_tag_float(vlc_no, ACTIVE_POWER) == 0)
        {
            vlc_power_it *it = calloc(1, sizeof(vlc_power_it));
            if (it == NULL)
            {
                ems_syslog(LOG_ERR, "calloc error");
            }
            else
            {
                it->pwer_ctr_info = &((vlc_dev_info *)par)->pwer_ctr_info;
                vlc_power_add(&vp, it);
            }
        }
    }
}

static void vlc_dod_protect_cb(char *nod, tag_value *data_value, void *par)
{
    const char *vlc_no = (char *)par;
    double value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
    int en_dod = dev_get_dev_tag_int(vlc_no, EN_PROTECT_DOD);
    if (en_dod)
    {
        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0008, vlc_no, "%s:使能DOD", vlc_no, vlc_no);
        double soc_max = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMAX);
        double soc_min = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMIN);
        double soc_max_err = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMAXERR);
        double soc_min_err = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMINERR);
        if (value <= soc_min)
        {
            VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0007, vlc_no, "%s:当前SOC:.2f,DOD禁放->触发", vlc_no, value);
            dev_set_dev_tag_int(vlc_no, DOD_LIMITED_CH, 0);
            dev_set_dev_tag_int(vlc_no, DOD_LIMITED_DC, 1);
            dev_set_dev_tag_int(vlc_no, DOD_LIMITED, 1);
        }
        else if (value >= soc_max)
        {
            VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0007, vlc_no, "%s:当前SOC:.2f,DOD禁充->触发", vlc_no, value);
            dev_set_dev_tag_int(vlc_no, DOD_LIMITED_DC, 0);
            dev_set_dev_tag_int(vlc_no, DOD_LIMITED_CH, 1);
            dev_set_dev_tag_int(vlc_no, DOD_LIMITED, 1);
        }
        else if ((value > (soc_min + soc_min_err)) && (value < (soc_max - soc_max_err)))
        {
            VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0007, vlc_no, "%s:当前SOC:.2f,DOD禁充禁放->解除", vlc_no, value);
            dev_set_dev_tag_int(vlc_no, DOD_LIMITED_DC, 0);
            dev_set_dev_tag_int(vlc_no, DOD_LIMITED_CH, 0);
            dev_set_dev_tag_int(vlc_no, DOD_LIMITED, 0);
        }
    }
    else
    {
        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0008, vlc_no, "%s:禁用DOD,并且清除DOD禁充禁放", vlc_no);
        dev_set_dev_tag_int(vlc_no, DOD_LIMITED_DC, 0);
        dev_set_dev_tag_int(vlc_no, DOD_LIMITED_CH, 0);
        dev_set_dev_tag_int(vlc_no, DOD_LIMITED, 0);
    }
    ems_syslog(LOG_NOTICE, "%s[%s] -> vlc dod protect, value:%f", vlc_no, nod, value);
}

static void check_voltage_time(void)
{
    pcs_ctrl_var_t *var = get_pcs_ctrl_var();
    int cur_sum = var->lc_list_len;
    lc_it *lc_cfg = var->lc_list;
    time_t cur_time = time(NULL);
    for (int i = 0; i < cur_sum; i++)
    {
        if (lc_cfg[i].type == eLC_TYPE_VLC && g_vlc_info[i].voltage_delay_time != 0)
        {
            if((g_vlc_info[i].voltage_dc_time != 0)&&(cur_time- g_vlc_info[i].voltage_dc_time >= g_vlc_info[i].voltage_delay_time))
            {
                vlc_stop_discharge_tab_cb(RACK_MIN_VOLT, NULL, &g_vlc_info[i]);
            }
            //
            if((g_vlc_info[i].voltage_ch_time != 0)&&(cur_time- g_vlc_info[i].voltage_ch_time >= g_vlc_info[i].voltage_delay_time))
            {
                vlc_stop_charge_tab_cb(RACK_MAX_VOLT, NULL, &g_vlc_info[i]);
            }
        }
    }
}

static void vlc_stop_discharge_tab_cb(char *nod, tag_value *data_value, void *par)
{
    const char *vlc_no = ((vlc_dev_info *)par)->no;
    uint32_t flag = ((vlc_dev_info *)par)->stop_dc_flag;
    int index = 0;
    //
    if (strstr(nod, RACK_MIN_VOLT) != NULL) // 我是特殊处理的坑
    {
        uint32_t cell_volt_flag = ((vlc_dev_info *)par)->cell_volt_flag;
        if (dev_get_dev_tag_int(vlc_no, EN_CELL_VOLT_PROTECT) != 0)
        {
            
            if(((vlc_dev_info *)par)->voltage_delay_time == 0)
            {
                double dv = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
                double min_dv = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MIN_VOLTAGE) * 1000.0;
                double min_err = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MIN_VOLTAGE_ERR) * 1000.0;
                if (dv <= min_dv)
                {
                    SET_AMASK_BIT(flag, 0);
                    SET_AMASK_BIT(cell_volt_flag, 1);
                    index = 0;
                }
                else if (dv > (min_dv + min_err))
                {
                    CLR_AMASK_BIT(flag, 0);
                    CLR_AMASK_BIT(cell_volt_flag, 1);
                }
                ems_syslog(LOG_NOTICE, "%s -> lc stop discharge, value:%f, min:%f, err:%f, cell_volt_flag:%x", nod, dv, min_dv, min_err, cell_volt_flag);
            }
            else
            {
                if (data_value != NULL)
                {
                    double dv = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
                    double min_dv = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MIN_VOLTAGE) * 1000.0;
                    double min_err = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MIN_VOLTAGE_ERR) * 1000.0;
                    if (dv <= min_dv)
                    {
                        if (((vlc_dev_info *)par)->voltage_dc_time == 0)
                        {
                            ((vlc_dev_info *)par)->voltage_dc_time = time(NULL);
                        }
                    }
                    else
                    {
                        ((vlc_dev_info *)par)->voltage_dc_time = 0;
                        if (dv > (min_dv + min_err))
                        {
                            CLR_AMASK_BIT(flag, 0);
                            CLR_AMASK_BIT(cell_volt_flag, 1);
                        }
                    }
                }
                else
                {
                    SET_AMASK_BIT(flag, 0);
                    SET_AMASK_BIT(cell_volt_flag, 1);
                    index = 0;
                }
            }
        }
        else
        {
            CLR_AMASK_BIT(flag, 0);
            CLR_AMASK_BIT(cell_volt_flag, 1);
        }
        ((vlc_dev_info *)par)->cell_volt_flag = cell_volt_flag;
        cell_volt_flag = (cell_volt_flag == 0) ? 0 : 1;
        if (dev_get_dev_tag_int(vlc_no, SINGLE_CELL_VOL_THRESHOLD_ALARM) != cell_volt_flag)
        {
            dev_set_dev_tag_int(vlc_no, SINGLE_CELL_VOL_THRESHOLD_ALARM, cell_volt_flag);
        }
    }
    else
    {
        //
        if (strstr(nod, DEV_NO_BMS) != NULL)
        {
            //不使能BMS告警，清理保护
            if (0 == dev_get_dev_tag_int(vlc_no, EN_BMS_ALARM)) // 没有使能BMS告警
            {
                for (int i = 1; i < (sizeof(vlc_stop_discharge_tab) / sizeof(vlc_stop_discharge_tab[0])); i++)
                {
                    if (strstr(nod, vlc_stop_discharge_tab[i]) != NULL)
                    {
                        CLR_AMASK_BIT(flag, i);
                    }
                }

                goto set_protect;
            }
        }
        //
        int value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
        for (int i = 1; i < (sizeof(vlc_stop_discharge_tab) / sizeof(vlc_stop_discharge_tab[0])); i++)
        {
            if (strstr(nod, vlc_stop_discharge_tab[i]) != NULL)
            {
                if (value != 0)
                {
                    SET_AMASK_BIT(flag, i);
                }
                else
                {
                    CLR_AMASK_BIT(flag, i);
                }
            }
        }
    }
    //
set_protect:
    ((vlc_dev_info *)par)->stop_dc_flag = flag;
    ems_syslog(LOG_NOTICE, "%s -> lc stop discharge, value:[int:%ld, float:%f], flag:0x%x", nod, data_value->value.to_int, data_value->value.to_float, flag);
    if (flag != 0)
    {
        if (0 != (flag & (0x01 << index)))
        {
            VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0009, vlc_no, "%s:禁放->触发，触发源:%s", vlc_no, nod);
        }
        if (dev_get_dev_tag_int(vlc_no, PROHIBITE_DISCHARGE) == 0)
        {
            dev_set_dev_tag_int(vlc_no, PROHIBITE_DISCHARGE, 1);
            if(dev_get_dev_tag_int(vlc_no, CH_DISCH_STATUS) == 2) // 放电状态
            {
                dev_set_dev_tag_int(vlc_no,SET_ACT_POWER,0);
                dev_set_dev_tag_int(vlc_no,SET_REACTPOWER,0);
            }
        }
    }
    else
    {
        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0009, vlc_no, "%s:禁放->解除", vlc_no);
        if (dev_get_dev_tag_int(vlc_no, PROHIBITE_DISCHARGE) != 0)
        {
            dev_set_dev_tag_int(vlc_no, PROHIBITE_DISCHARGE, 0);
        }
    }
}

static void vlc_stop_charge_tab_cb(char *nod, tag_value *data_value, void *par)
{
    const char *vlc_no = ((vlc_dev_info *)par)->no;
    uint32_t flag = ((vlc_dev_info *)par)->stop_ch_flag;
    int index = 0;
    //
    if (strstr(nod, RACK_MAX_VOLT) != NULL) // 我是特殊处理的坑
    {
        uint32_t cell_volt_flag = ((vlc_dev_info *)par)->cell_volt_flag;
        if (dev_get_dev_tag_int(vlc_no, EN_CELL_VOLT_PROTECT) != 0)
        {
            if (((vlc_dev_info *)par)->voltage_delay_time == 0)
            {
                double dv = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
                double max_dv = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MAX_VOLTAGE) * 1000.0;
                double max_err = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MAX_VOLTAGE_ERR) * 1000.0;
                if (dv >= max_dv)
                {
                    SET_AMASK_BIT(flag, 0);
                    SET_AMASK_BIT(cell_volt_flag, 0);
                    index = 0;
                }
                else if (dv < (max_dv - max_err))
                {
                    CLR_AMASK_BIT(flag, 0);
                    CLR_AMASK_BIT(cell_volt_flag, 0);
                }
                ems_syslog(LOG_NOTICE, "%s -> lc stop charge, value:%f, max:%f, err:%f, cell_volt_flag:%d", nod, dv, max_dv, max_err, cell_volt_flag);
            }
            else
            {
                if (data_value != NULL)
                {
                    double dv = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
                    double max_dv = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MAX_VOLTAGE) * 1000.0;
                    double max_err = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MAX_VOLTAGE_ERR) * 1000.0;
                    if (dv >= max_dv)
                    {
                        if (((vlc_dev_info *)par)->voltage_ch_time == 0)
                        {
                            ((vlc_dev_info *)par)->voltage_ch_time = time(NULL);
                        }
                    }
                    else
                    {
                        ((vlc_dev_info *)par)->voltage_ch_time = 0;
                        if (dv < (max_dv - max_err))
                        {
                            CLR_AMASK_BIT(flag, 0);
                            CLR_AMASK_BIT(cell_volt_flag, 0);
                        }
                    }
                }
                else
                {
                    SET_AMASK_BIT(flag, 0);
                    SET_AMASK_BIT(cell_volt_flag, 0);
                    index = 0;
                }
            }
        }
        else
        {
            CLR_AMASK_BIT(flag, 0);
            CLR_AMASK_BIT(cell_volt_flag, 0);
        }

        ((vlc_dev_info *)par)->cell_volt_flag = cell_volt_flag;
        cell_volt_flag = (cell_volt_flag == 0) ? 0 : 1;
        if (dev_get_dev_tag_int(vlc_no, SINGLE_CELL_VOL_THRESHOLD_ALARM) != cell_volt_flag)
        {
            dev_set_dev_tag_int(vlc_no, SINGLE_CELL_VOL_THRESHOLD_ALARM, cell_volt_flag);
        }
    }
    else
    {
        if (strstr(nod, DEV_NO_BMS) != NULL)
        {
            if (0 == dev_get_dev_tag_int(vlc_no, EN_BMS_ALARM)) // 没有使能BMS告警
            {
                for (int i = 1; i < (sizeof(vlc_stop_charge_tab) / sizeof(vlc_stop_charge_tab[0])); i++)
                {
                    if (strstr(nod, vlc_stop_charge_tab[i]) != NULL)
                    {
                        CLR_AMASK_BIT(flag, i);
                    }
                }
                goto set_protect;
            }
        }
        //
        int value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
        for (int i = 1; i < (sizeof(vlc_stop_charge_tab) / sizeof(vlc_stop_charge_tab[0])); i++)
        {
            if (strstr(nod, vlc_stop_charge_tab[i]) != NULL)
            {
                if (value != 0)
                {
                    index = i;
                    SET_AMASK_BIT(flag, i);
                }
                else
                {
                    CLR_AMASK_BIT(flag, i);
                }
            }
        }
    }
    //
set_protect:
    ((vlc_dev_info *)par)->stop_ch_flag = flag;
    ems_syslog(LOG_NOTICE, "%s -> lc stop charge, value:[int:%ld, float:%f], flag:0x%x", nod, data_value->value.to_int, data_value->value.to_float, flag);
    if (flag != 0)
    {
        if (0 != (flag & (0x01 << index)))
        {
            VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0010, vlc_no, "%s:禁充->触发，触发源:%s", vlc_no, nod);
        }
        if (dev_get_dev_tag_int(vlc_no, PROHIBITE_CHARGE) == 0)
        {
            dev_set_dev_tag_int(vlc_no, PROHIBITE_CHARGE, 1);
            //触发保护写零功率
            if(dev_get_dev_tag_int(vlc_no, CH_DISCH_STATUS) == 1) // 充电状态
            {
                dev_set_dev_tag_int(vlc_no,SET_ACT_POWER,0);
                dev_set_dev_tag_int(vlc_no,SET_REACTPOWER,0);
            }
        }
    }
    else
    {
        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0010, vlc_no, "%s:禁充->解除", vlc_no);
        if (dev_get_dev_tag_int(vlc_no, PROHIBITE_CHARGE) != 0)
        {
            dev_set_dev_tag_int(vlc_no, PROHIBITE_CHARGE, 0);
        }
    }
}

int sync_check_ems_sys_status()
{
    int cur_sum = get_pcs_ctrl_var()->lc_list_len;
    lc_it *lc_cfg = get_pcs_ctrl_var()->lc_list;
    int normal_count = 0;
    for (int i = 0; i < cur_sum; i++)
    {

        int sys_status = dev_get_dev_tag_int(lc_cfg[i].no, SYSTEM_STATUS);
        if (sys_status == 0) // 0为正常
        {
           normal_count++;
        }
    }
    if (normal_count > 0)
    {
        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0011, NULL, "所有从机异常->触发");
        dev_set_dev_tag_int(DEV_NO_EMS, LC_SYSTEM_STATUS, 0); // 从机系统正常
    }
    else
    {
        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0011, NULL, "所有从机异常->解除");
        dev_set_dev_tag_int(DEV_NO_EMS, LC_SYSTEM_STATUS, 1); // 从机系统故障
    }
    return normal_count;
}

static void vlc_stop_cab_state_tab_cb(char *nod, tag_value *data_value, void *par)
{
    const char *vlc_no = ((vlc_dev_info *)par)->no;
    uint32_t status = ((vlc_dev_info *)par)->sys_status_flag;
    int index = 0;
    int value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;

    // 检查是否是PCS.STATE_ONLINE点位
    char online_tag[TAG_NAME_LEN] = {0};
    snprintf(online_tag, sizeof(online_tag), ".%s.%s", DEV_NO_PCS, STATE_ONLINE);
    if (strstr(nod, online_tag) != NULL && value == 0)
    {
        // PCS离线，设置vlc的有功功率和无功功率为0
        dev_set_dev_tag_float(vlc_no, ACTIVE_POWER, 0.0f);
        dev_set_dev_tag_float(vlc_no, REACTIVE_POWER, 0.0f);
    }

    if (strstr(nod, DEV_NO_BMS) == nod)
    {
        if (0 == dev_get_dev_tag_int(DEV_NO_EMS, EN_BMS_ALARM)) // 没有使能BMS告警
        {
            return;
        }
    }
    //
    int not_size = sizeof(vlc_stop_cab_state_tab_not) / sizeof(vlc_stop_cab_state_tab_not[0]);
    if (value != 0)
    {

        for (int i = not_size - 1; i >= 0; i--)
        {
            if (strstr(nod, vlc_stop_cab_state_tab_not[i]) != NULL) {
                status &= (~(0x01 << i));
                break;
            }
        }
        //
        for (int i = 0; i < sizeof(vlc_stop_cab_state_tab) / sizeof(vlc_stop_cab_state_tab[0]); i++)
        {
            if (strstr(nod, vlc_stop_cab_state_tab[i]) != NULL)
            {
                status |= (0x01 << (i + not_size));
                index = i + not_size;
                break;
            }
        }
    }
    else
    {
        for (int i = not_size - 1; i >= 0; i--)
        {
            if (strstr(nod, vlc_stop_cab_state_tab_not[i]) != NULL) {
                status |= (0x01 << i);
                index = i;
                break;
            }
        }
        //
        for (int i = 0; i < sizeof(vlc_stop_cab_state_tab) / sizeof(vlc_stop_cab_state_tab[0]); i++)
        {
            if (strstr(nod, vlc_stop_cab_state_tab[i]) != NULL)
            {
                status &= (~(0x01 << (i + not_size)));
                break;
            }
        }
    }
    //
    ((vlc_dev_info *)par)->sys_status_flag = status;
    dev_set_dev_tag_int(vlc_no, SYSTEM_STATUS_W, status);
    if (status != 0)
    {
        if (0 != (status & (0x01 << index)))
        {
            VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0012, NULL, "系统状态异常->触发，触发源:%s", nod);
        }
        dev_set_dev_tag_int(vlc_no, SYSTEM_STATUS, 1);
        //触发系统异常写零功率
        dev_set_dev_tag_int(vlc_no,SET_ACT_POWER,0);
        dev_set_dev_tag_int(vlc_no,SET_REACTPOWER,0);
    }
    else
    {
        VLC_BUSINESS_LOG(BLOG_NOTICE, VLC_ID_0012, NULL, "系统状态异常->解除");
        dev_set_dev_tag_int(vlc_no, SYSTEM_STATUS, 0);
    }
    ems_syslog(LOG_NOTICE, "%s[%s] -> lc stste fault, value:%d", vlc_no, nod, value);
}

void vlc_do_trip(void *par) // 脱扣
{
    pthread_t tip;
    char *maybe_lc_no = (char *)par;
    char *dot = strchr(maybe_lc_no, '.');
    if (dot != NULL) // 包含点
    {
        pcs_ctrl_var_t *var = get_pcs_ctrl_var();
        int cur_sum = var->lc_list_len;
        lc_it *lc_cfg = var->lc_list;
        for (int i = 0; i < cur_sum; i++)
        {
            if ((strlen(lc_cfg->no) == dot - maybe_lc_no) && (memcmp(lc_cfg->no, maybe_lc_no, dot - maybe_lc_no) == 0)) // no完全匹配
            {
                maybe_lc_no = lc_cfg->no;
                break;
            }
            lc_cfg++;
        }
    }
    if (0 == pthread_create(&tip, NULL, trip_task, maybe_lc_no))
    {
        pthread_setname_np(tip, "vlc_trip_task");
    }
}

static void vlc_stop_fire_cb(char *nod, tag_value *data_value, void *par)
{
    // 从机消防告警，触发脱扣
    char *vlc_no = ((vlc_dev_info *)par)->no;
    int value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
    if (value > 0)
    {
        vlc_do_trip(vlc_no);
    }
}

static void vlc_maximum_charg_power_cb(char *nod,tag_value *data_value, void *par) {
    const char *vlc_bms_no = ((vlc_dev_info *)par)->ext_info;
    const char *vlc_no = ((vlc_dev_info *)par)->no;
    int value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
    double RackVoltage = 0.0, RackMaxChargeCur = 0.0, ChargePower = 0.0;
    if(strstr(nod, RACK_VOLTAGE))
    {
        RackVoltage = value; 
        if(dev_get_dev_tag_float_with_result(vlc_bms_no, RACK_MAX_CHARGE_CUR, &RackMaxChargeCur) < 0)
        {
            ems_syslog(LOG_ERR, "vlc_maximum_charg_power_cb: get %s failed", RACK_MAX_CHARGE_CUR);
        }
    }
    else if(strstr(nod, RACK_MAX_CHARGE_CUR))
    {
        RackMaxChargeCur = value;
        if(dev_get_dev_tag_float_with_result(vlc_bms_no, RACK_VOLTAGE, &RackVoltage) < 0)
        {
            ems_syslog(LOG_ERR, "vlc_maximum_charg_power_cb: get %s failed", RACK_VOLTAGE);
        }
    }
    ChargePower = RackVoltage * RackMaxChargeCur / 1000.0;
    dev_set_dev_tag_float(vlc_no, MAX_CHARGE_POWER, ChargePower);
}

static void vlc_maximum_discharg_power_cb(char *nod,tag_value *data_value, void *par) {
    const char *vlc_bms_no = ((vlc_dev_info *)par)->ext_info;
    const char *vlc_no = ((vlc_dev_info *)par)->no;
    int value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
    double RackVoltage = 0.0, RackMaxDischargeCur = 0.0, DisChargePower = 0.0;
    if(strstr(nod, RACK_VOLTAGE))
    {
        RackVoltage = value; 
        if(dev_get_dev_tag_float_with_result(vlc_bms_no, RACK_MAX_DISCHA_CUR, &RackMaxDischargeCur) < 0)
        {
            ems_syslog(LOG_ERR, "vlc_maximum_charg_power_cb: get %s failed", RACK_MAX_DISCHA_CUR);
        }
    }
    else if(strstr(nod, RACK_MAX_DISCHA_CUR))
    {
        RackMaxDischargeCur = value;
        if(dev_get_dev_tag_float_with_result(vlc_bms_no, RACK_VOLTAGE, &RackVoltage) < 0)
        {
            ems_syslog(LOG_ERR, "vlc_maximum_charg_power_cb: get %s failed", RACK_VOLTAGE);
        }
    }
    DisChargePower = RackVoltage * RackMaxDischargeCur / 1000.0;
    dev_set_dev_tag_float(vlc_no, MAX_DISCHA_POWER, DisChargePower);
}
