#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"

extern double get_ems_lc_power(void);

// static struct hash_intptr *ems_variant_hash;
// static struct hash_intptr *device_variant_hash;
// static void *g_collector = NULL;
// extern usercfg_variant_t g_usercfg_variant;

//配置文件参数 //此处不包含计划参数 计划参数一定会存储
const ems_cfg_info_t ems_config_tab[] = 
{
    {DEV_NO_EMS"."EN_DRAUG_FLOW_POWER   ,offsetof(usercfg_variant_t, EnDraughtFanFlowPower),             EMS_POINT_INT},
    {DEV_NO_EMS"."EN_DRAUG_FLOW_POWER_T ,offsetof(usercfg_variant_t, DraughtFanFlowPowerThreShold),      EMS_POINT_INT},
    {DEV_NO_EMS"."EN_DRAUG_FLOW_TEM     ,offsetof(usercfg_variant_t, EnDraughtFanFlowTem),               EMS_POINT_INT},
    {DEV_NO_EMS"."EN_DRAUG_FLOW_TEM_T   ,offsetof(usercfg_variant_t, DraughtFanFlowTemThreShold),        EMS_POINT_INT},
    {DEV_NO_EMS"."DRAUG_DEAE_TIME       ,offsetof(usercfg_variant_t, DeadTime),                          EMS_POINT_INT}, 
    {DEV_NO_EMS"."EN_TRACELOAD          ,offsetof(usercfg_variant_t, EnTraceLoad),      EMS_POINT_INT}, 
    {DEV_NO_EMS"."EN_PROTECT_REVERSE    ,offsetof(usercfg_variant_t, EnProtectReverse), EMS_POINT_INT}, 
    {DEV_NO_EMS"."PROTECT_REVERSE_WIN    ,offsetof(usercfg_variant_t, ProtectReverseWin), EMS_POINT_INT},
    {DEV_NO_EMS"."DEMAND_WIN             ,offsetof(usercfg_variant_t, DemandWin), EMS_POINT_INT},
    {DEV_NO_EMS"."EN_PROTECT_TRANSF     ,offsetof(usercfg_variant_t, EnProtectTransf),  EMS_POINT_INT}, 
    {DEV_NO_EMS"."STRANTAGY_TYPE        ,offsetof(usercfg_variant_t, StratagyType),     EMS_POINT_INT},
    {DEV_NO_EMS"."EN_PROTECT_DOD        ,offsetof(usercfg_variant_t, EnProtectDOD),      EMS_POINT_INT},
    {DEV_NO_EMS"."CONTROL_MODE          ,offsetof(usercfg_variant_t, ControlMode),      EMS_POINT_INT},
    {DEV_NO_EMS"."EN_BMS_ALARM          ,offsetof(usercfg_variant_t, EnBmsAlarm),      EMS_POINT_INT},

    {DEV_NO_EMS"."EMS_PMLMAX          ,offsetof(usercfg_variant_t, Pmlmax)           ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_K1              ,offsetof(usercfg_variant_t, K1)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_K2              ,offsetof(usercfg_variant_t, K2)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_K3              ,offsetof(usercfg_variant_t, K3)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_SOCMAX          ,offsetof(usercfg_variant_t, SOCmax)           ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."EMS_SOCMIN          ,offsetof(usercfg_variant_t, SOCmin)           ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."EMS_PSET            ,offsetof(usercfg_variant_t, Pset)             ,EMS_POINT_INT},
 
    {DEV_NO_EMS"."CAB_POWER           ,offsetof(usercfg_variant_t, cab_power),        EMS_POINT_INT},
    {DEV_NO_EMS"."CAB_CAPACITY        ,offsetof(usercfg_variant_t, cab_capacity),     EMS_POINT_INT},  
    {DEV_NO_EMS"."EMS_ADJPRERIOD      ,offsetof(usercfg_variant_t, Adjpreriod)       ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_TEMP_THRESHOLD  ,offsetof(usercfg_variant_t, tempThreshold)    ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_TRACKING_LIMIT  ,offsetof(usercfg_variant_t, loadTrackingLimit),EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_NUMPCS          ,offsetof(usercfg_variant_t, numOfPcs)         ,EMS_POINT_INT},
    {DEV_NO_EMS"."METER_INVERT       ,offsetof(usercfg_variant_t, meter_invert)    ,EMS_POINT_INT},
    {DEV_NO_EMS"."BMS_POWER_LIMIT     ,offsetof(usercfg_variant_t, en_bms_power_limit)    ,EMS_POINT_INT},
    {DEV_NO_EMS"."ENPROTECTRASLV       ,offsetof(usercfg_variant_t, EnProtectTrasLV)    ,EMS_POINT_INT},
    {DEV_NO_EMS"."PLVMLMAX          ,offsetof(usercfg_variant_t, PLVmlmax)    ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_K4            ,offsetof(usercfg_variant_t, K4)            ,EMS_POINT_INT},
    {DEV_NO_EMS"."EN_IO_PROTECT       ,offsetof(usercfg_variant_t, EnIoProtect)      ,EMS_POINT_INT},
    {DEV_NO_EMS"."PCS_AUTO_TURN       ,offsetof(usercfg_variant_t, PcsAutoTurn)      ,EMS_POINT_INT},
    {DEV_NO_EMS"."PCS_AUTO_TURN_OFF_TIME,offsetof(usercfg_variant_t, PcsAutoTurnOffTime),EMS_POINT_INT},
    {DEV_NO_EMS"."EN_RP_COMP       ,offsetof(usercfg_variant_t, EnPcsRpComp)      ,EMS_POINT_INT},
    {DEV_NO_EMS"."PACK_ALARM_LIM_TEMP,offsetof(usercfg_variant_t, PackAlarmLimitTemp),EMS_POINT_INT},
    {DEV_NO_EMS"."WORK_SAMPLE_PERIOD,offsetof(usercfg_variant_t, WorkSamplePeriod),EMS_POINT_INT},
    {DEV_NO_EMS"."IDLE_SAMPLE_PERIOD ,offsetof(usercfg_variant_t, IdleSamplePeriod),EMS_POINT_INT},
    {DEV_NO_EMS"."MAX_DEMAND ,offsetof(usercfg_variant_t, MaxDemand),EMS_POINT_INT},
    {DEV_NO_EMS"."DEMAND_MARGIN,offsetof(usercfg_variant_t, DemandMargin),EMS_POINT_INT},
    {DEV_NO_EMS"."DYNAMIC_AUGMENT,offsetof(usercfg_variant_t, DynamicAugmentation),EMS_POINT_INT},
    {DEV_NO_EMS"."PV_MAX_POWER           ,offsetof(usercfg_variant_t, PvMaxPower),        EMS_POINT_INT},


    //热管理
    {DEV_NO_EMS"."ENABLE_AUTO_COOL, offsetof(usercfg_variant_t, EnableAutoCool),EMS_POINT_INT},
    {DEV_NO_EMS"."SELF_LOOP_DIFF, offsetof(usercfg_variant_t, SelfLoopDiff),EMS_POINT_INT},
    {DEV_NO_EMS"."EN_SELF_LOOP_DIFF, offsetof(usercfg_variant_t, En_SelfLoopDiff),EMS_POINT_INT},
    
    // {DEV_NO_EMS"."BEFORE_WORK_TIME    , offsetof(usercfg_variant_t,BeforeWorkTime),EMS_POINT_INT},
    {DEV_NO_EMS"."WORK_START_HOT_TEMP , offsetof(usercfg_variant_t,WStartHot),EMS_POINT_INT},
    {DEV_NO_EMS"."WORK_END_HOT_TEMP   , offsetof(usercfg_variant_t,WEndHot),EMS_POINT_INT},
    {DEV_NO_EMS"."WORK_START_COOL_TEMP, offsetof(usercfg_variant_t,WStartCool),EMS_POINT_INT},
    {DEV_NO_EMS"."WORK_END_COOL_TEMP  , offsetof(usercfg_variant_t,WEndCool),EMS_POINT_INT},
    // {DEV_NO_EMS"."STOP_START_HOT_TEMP , offsetof(usercfg_variant_t,SStartHot),EMS_POINT_INT},
    // {DEV_NO_EMS"."STOP_END_HOT_TEMP   , offsetof(usercfg_variant_t,SEndHot),EMS_POINT_INT},
    // {DEV_NO_EMS"."STOP_START_COOL_TEMP, offsetof(usercfg_variant_t,SStartCool),EMS_POINT_INT},
    // {DEV_NO_EMS"."STOP_END_COOL_TEMP ,  offsetof(usercfg_variant_t,SEndCool),EMS_POINT_INT},
    // {DEV_NO_EMS"."PREPARE_START_HOT_TEMP , offsetof(usercfg_variant_t,PStartHot),EMS_POINT_INT},
    // {DEV_NO_EMS"."PREPARE_END_HOT_TEMP   , offsetof(usercfg_variant_t,PEndHot),EMS_POINT_INT},
    // {DEV_NO_EMS"."PREPARE_START_COOL_TEMP, offsetof(usercfg_variant_t,PStartCool),EMS_POINT_INT},
    // {DEV_NO_EMS"."PREPARE_END_COOL_TEMP  , offsetof(usercfg_variant_t,PEndCool),EMS_POINT_INT},
    {DEV_NO_EMS"."WORK_DIST_HOT_TEMP  , offsetof(usercfg_variant_t,WDistHot),EMS_POINT_INT},
    {DEV_NO_EMS"."WORK_DIST_COOL_TEMP , offsetof(usercfg_variant_t,WDistCool),EMS_POINT_INT},
    // {DEV_NO_EMS"."STOP_DIST_HOT_TEMP    , offsetof(usercfg_variant_t,SDistHot),EMS_POINT_INT},
    // {DEV_NO_EMS"."STOP_DIST_COOL_TEMP   , offsetof(usercfg_variant_t,SDistCool),EMS_POINT_INT},
    // {DEV_NO_EMS"."PREPARE_DIST_HOT_TEMP , offsetof(usercfg_variant_t,PDistHot),EMS_POINT_INT},
    // {DEV_NO_EMS"."PREPARE_DIST_COOL_TEMP, offsetof(usercfg_variant_t,PDistCool),EMS_POINT_INT},

    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"1" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t,  demand_threshold)+sizeof(int)*0   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"2" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t,  demand_threshold)+sizeof(int)*1   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"3" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t,  demand_threshold)+sizeof(int)*2   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"4" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t,  demand_threshold)+sizeof(int)*3   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"5" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t,  demand_threshold)+sizeof(int)*4   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"6" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t,  demand_threshold)+sizeof(int)*5   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"7" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t,  demand_threshold)+sizeof(int)*6   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"8" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t,  demand_threshold)+sizeof(int)*7   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"9" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t,  demand_threshold)+sizeof(int)*8   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"10" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t, demand_threshold)+sizeof(int)*9   ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"11" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t, demand_threshold)+sizeof(int)*10  ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_THRESHOLD"12" ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t, demand_threshold)+sizeof(int)*11  ,EMS_POINT_INT},

    {DEV_NO_EMS"."EMS_CHARGE_STARTPOWER,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t, charge_startpower)                        ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DISCHARGE_THRESHOLD,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t, discharge_threshold)                    ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_START_HOUR,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t, tou_seg)+offsetof(demand_seg_t, StHour )  ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_START_MIN ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t, tou_seg)+offsetof(demand_seg_t, StMin  )  ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_END_HOUT  ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t, tou_seg)+offsetof(demand_seg_t, EnHour )  ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DEMAND_END_MIN   ,offsetof(usercfg_variant_t, demand_param)+offsetof(demand_param_t, tou_seg)+offsetof(demand_seg_t, EnMin  )  ,EMS_POINT_INT},

    {DEV_NO_EMS"."SINGLE_MAX_VOLTAGE              ,offsetof(usercfg_variant_t, SingleMaxVoltage)               ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."SINGLE_MIN_VOLTAGE              ,offsetof(usercfg_variant_t, SingleMinVoltage)               ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."EN_CELL_VOLT_PROTECT            ,offsetof(usercfg_variant_t, EnCellVoltProtect)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."SINGLE_MAX_VOLTAGE_ERR          ,offsetof(usercfg_variant_t, SingleMaxVoltageErr)               ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."SINGLE_MIN_VOLTAGE_ERR          ,offsetof(usercfg_variant_t, SingleMinVoltageErr)               ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."EMS_SOCMINERR                   ,offsetof(usercfg_variant_t, SOCmaxErr)               ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."EMS_SOCMAXERR                   ,offsetof(usercfg_variant_t, SOCminErr)               ,EMS_POINT_FLOAT},

    {DEV_NO_EMS"."CHARGE_MAX_POWER              ,offsetof(usercfg_variant_t, chargeMaxPower)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."DISCHARGE_THRES_HOLD              ,offsetof(usercfg_variant_t, dischargeThreshold)               ,EMS_POINT_INT},

    // 微网
    {DEV_NO_EMS"."SOC_NET_DEAD_MAX              ,offsetof(usercfg_variant_t, SOCmaxNetDead)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."SOC_NET_DEAD_MIN              ,offsetof(usercfg_variant_t, SOCminNetDead)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."DG_RATED_POWER              ,offsetof(usercfg_variant_t, DGRatedPower)               ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."DG_BOOT_SOC              ,offsetof(usercfg_variant_t, DGBootSoc)               ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."SOC_ALARM              ,offsetof(usercfg_variant_t, SocAlarmThreshold)               ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."EN_AC_D2C              ,offsetof(usercfg_variant_t, en_ac_d2c)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."EN_AC_C2D              ,offsetof(usercfg_variant_t, en_ac_c2d)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."EMS_DG_DIFF              ,offsetof(usercfg_variant_t, DgDiff)               ,EMS_POINT_FLOAT},
    {DEV_NO_EMS"."ACTION_TIMEOUT              ,offsetof(usercfg_variant_t, action_timeout)               ,EMS_POINT_INT},
    {DEV_NO_EMS"."DG_CAP_PRO              ,offsetof(usercfg_variant_t, DG_Capacity_protection)               ,EMS_POINT_INT},
};
int ems_config_tab_count_g = sizeof(ems_config_tab) / sizeof(ems_config_tab[0]);
//处理快速响应配置
const char * ems_config_fast_tab[] = 
{

};

//处理控制命令
const char * ems_contrl_tab[] = 
{
    DEV_NO_EMS"."ON_OFF_STATE,     
    DEV_NO_EMS"."SET_ACT_POWER, 
    DEV_NO_EMS"."SET_REACTPOWER,
    DEV_NO_EMS"."SET_CONNECT,  
    DEV_NO_EMS"."CLEAR_ALARM,
    DEV_NO_EMS"."SET_PV_POWER,
    DEV_NO_EMS"."CONTROL_MODE,
    DEV_NO_EMS"."BMS_UPPER,
    DEV_NO_EMS"."EN_RP_COMP,
    DEV_NO_EMS"."SET_POWER_FACTOR,
};

//DOD保护
const char * fast_stop_dod_protect_tab[] = 
{
    DEV_NO_BMS"."CURRENT_SOC,
};
//处理欠压故障
const char * fast_stop_discharge_tab[] = 
{
    DEV_NO_BMS"."CELL_UNDER_VOL2,
    DEV_NO_BMS"."CELL_UNDER_VOL3,
    DEV_NO_BMS"."RACK_UNDER_VOL2,
    DEV_NO_BMS"."RACK_UNDER_VOL3,
    DEV_NO_BMS"."CURRENT_SOC,
    DEV_NO_BMS "." RACK_MIN_VOLT,
    DEV_NO_EMS "." USER_STOP_DISCHA1,
    DEV_NO_EMS "." USER_STOP_DISCHA2,
    DEV_NO_EMS "." USER_STOP_DISCHA3
};

//处理过压故障
const char * fast_stop_charge_tab[] = 
{
    DEV_NO_BMS"."CELL_OVER_VOL2,
    DEV_NO_BMS"."CELL_OVER_VOL3,
    DEV_NO_BMS"."RACK_OVER_VOL2,
    DEV_NO_BMS"."RACK_OVER_VOL3,
    DEV_NO_BMS"."CURRENT_SOC,
    DEV_NO_BMS "." RACK_MAX_VOLT,
    DEV_NO_EMS "." USER_STOP_CHA1,
    DEV_NO_EMS "." USER_STOP_CHA2,
    DEV_NO_EMS "." USER_STOP_CHA3
};


//系统状态
const char * fast_stop_system_state_tab[] = 
{
    DEV_NO_BMS"."CELL_OVER_VOL3,
    DEV_NO_BMS"."RACK_OVER_VOL3,
    DEV_NO_BMS"."CELL_UNDER_VOL3,
    DEV_NO_BMS"."RACK_UNDER_VOL3,
    DEV_NO_EMS"."IO_PROTECT,
    DEV_NO_EMS"."METER_ERROR,
    DEV_NO_PCS"."PCS_FAULT,
    DEV_NO_PCS"."PCS_TOTAL_FAULT,
    DEV_NO_EMS"."USER_STOP1,
    DEV_NO_EMS"."USER_STOP2,
    DEV_NO_EMS"."USER_STOP3,
    DEV_NO_EMS"."TEMP_CTRL_ERR,//温控系统异常
    DEV_NO_EMS"."ALL_FIRE,//消防系统触发
};

const char * fast_stop_system_state_tab_not[] = 
{
    DEV_NO_PCS"."STATE_ONLINE,
    DEV_NO_BMS"."STATE_ONLINE,
};

//温控系统异常
const char * fast_stop_temp_ctrl_tab[] = 
{
    DEV_NO_AIRCOND"."STATE_ONLINE,
    DEV_NO_AIRCOND"."DEV_FAULT,
    DEV_NO_LIQUID"."STATE_ONLINE,
    DEV_NO_LIQUID"."DEV_FAULT,
};

//电表故障  此处回调改为周期检查
// const char * fast_stop_meter_err_tab[] = 
// {
//     DEV_NO_GRID_MT"."STATE_ONLINE,
//     DEV_NO_PCS_MT"."STATE_ONLINE,
// };

//外部消防触发  //在广播处触发


//消防触发（FireTrigger）

const char * fast_stop_fire_tab[] = 
{
    DEV_NO_EMS"."EXTERN_FIRE,//外部消防的点位
    DEV_NO_EMS"."FIRE,
};


//PCS功率反馈异常
const char * fast_stop_PCS_power_limit_tab[] = 
{
    DEV_NO_PCS"."ACTIVE_POWER,
    DEV_NO_EMS"."EXPECT_ACT_POWER,
};

//直流总压异常异常
const char * fast_stop_dc_voltage_unusual_tab[] = 
{
    DEV_NO_BMS"."RACL_VOLTAGE,
};

const char* fast_stop_power_tab[] =
{
    DEV_NO_EMS "." EMS_POWER, //当前系统是否停电 0 正常
};

//向数采系统注册点位
ems_base_variant_t ems_base_variant[] =
    {
        // 读写点位 (参数配置)
        //使能负荷跟踪
        {DEV_NO_EMS, EN_TRACELOAD,          "Enable load tracking",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "0:Disabled;1:Enabled;",         0x03, 0x00, TYPE_INT,   ORDER_AB,   1},
        //使能逆流防护
        {DEV_NO_EMS, EN_PROTECT_REVERSE,    "Enable anti-backflow protection",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "0:Disabled;1:Enabled;2:Enable over-limit Locking;",         0x03, 0x01, TYPE_INT,   ORDER_AB,   1},
        //需量控制
        {DEV_NO_EMS, EN_PROTECT_TRANSF,     "Demand control",    O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "0:Disabled;1:Transformer demand;2:Maximum demand;",         0x03, 0x02, TYPE_INT,   ORDER_AB,   1},
        //设置工作模式
        {DEV_NO_EMS, CONTROL_MODE,          "Set working mode", O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "0:Manual;1:Strategy;2:Remote;",         0x03, 0x03, TYPE_INT,   ORDER_AB,   1},
        //变压器容量
        {DEV_NO_EMS, EMS_PMLMAX,            "Transformer capacity(kVar)",        O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x04, TYPE_INT,   ORDER_AB,   1},
        //需量控制系数
        {DEV_NO_EMS, EMS_K1,                "Demand control factor(%)",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x05, TYPE_INT,   ORDER_AB,   1},
        //负载跟踪系数
        {DEV_NO_EMS, EMS_K2,                "Load tracking factor(%)",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x06, TYPE_INT,   ORDER_AB,   1},
        //多站点分配系数
        {DEV_NO_EMS, EMS_K3,                "Multi site allocation factor(%)",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x07, TYPE_INT,   ORDER_AB,   1},
        //最大充电SOC
        {DEV_NO_EMS, EMS_SOCMAX,            "Upper SOC limit(%)",       O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,  PROPERTY_REALDATA, "change", "",                      0x03, 0x09, TYPE_FLOAT, ORDER_ABCD, 1},
        //最小放电SOC
        {DEV_NO_EMS, EMS_SOCMIN,            "Lower SOC limit(%)",       O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,  PROPERTY_REALDATA, "change", "",                      0x03, 0x0b, TYPE_FLOAT, ORDER_ABCD, 1},
        //防逆流余量
        {DEV_NO_EMS, EMS_PSET,              "Anti-backflow margin(kW)",        O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x0d, TYPE_INT,   ORDER_AB,   1},
        //调整间隔
        {DEV_NO_EMS, EMS_ADJPRERIOD,        "Adjustment interval(s)",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x11, TYPE_INT,   ORDER_AB,   1},
        //调整间隔
        {DEV_NO_EMS, EMS_TEMP_THRESHOLD,    "Temperature threshold(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x12, TYPE_INT,   ORDER_AB,   1},
        //使能负荷跟踪限制
        {DEV_NO_EMS, EMS_TRACKING_LIMIT,    "Enable load tracking limit",  O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "0:Disabled;1:Enabled;",          0x03, 0x13, TYPE_INT,   ORDER_AB,   1},
        //使能DOD防护
        {DEV_NO_EMS, EN_PROTECT_DOD,        "Enable DOD protection",       O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "0:Disabled;1:Enabled;",          0x03, 0x15, TYPE_INT,   ORDER_AB,   1},
        //1月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"1","January demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x16, TYPE_INT,    ORDER_AB,    1},
        //2月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"2","February demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x17, TYPE_INT,    ORDER_AB,    1},
        //3月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"3","March demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x18, TYPE_INT,    ORDER_AB,    1},
        //4月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"4","April demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x19, TYPE_INT,    ORDER_AB,    1},
        //5月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"5","May demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x1A, TYPE_INT,    ORDER_AB,    1},
        //6月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"6","June demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x1B, TYPE_INT,    ORDER_AB,    1},
        //7月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"7","July demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x1C, TYPE_INT,    ORDER_AB,    1},
        //8月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"8","August demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x1D, TYPE_INT,    ORDER_AB,    1},
        //9月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"9","September demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x1E, TYPE_INT,    ORDER_AB,    1},
        //10月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"10","October demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x1F, TYPE_INT,    ORDER_AB,    1},
        //11月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"11","November demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x20, TYPE_INT,    ORDER_AB,    1},
        //12月需量控制门限
        {DEV_NO_EMS, EMS_DEMAND_THRESHOLD"12","December demand control threshold(kW)",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,                   0x03, 0x21, TYPE_INT,    ORDER_AB,    1},
        //需量充电开始小时
        {DEV_NO_EMS, EMS_DEMAND_START_HOUR, "Charging starting in hour",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,              0x03, 0x22, TYPE_INT,    ORDER_AB,    1},
        //需量充电开始分钟
        {DEV_NO_EMS, EMS_DEMAND_START_MIN , "Charging starting in minute",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",                 0x03, 0x23, TYPE_INT,   ORDER_AB,   1},
        //需量充电结束小时
        {DEV_NO_EMS, EMS_DEMAND_END_HOUT  , "Charging ending in hour",      O_WRONLY | O_RDONLY,EMS_POINT_INT,    PROPERTY_REALDATA , "change",   "" ,              0x03, 0x24, TYPE_INT,    ORDER_AB,    1},
        //需量充电结束分钟
        {DEV_NO_EMS, EMS_DEMAND_END_MIN   , "Charging ending in minute",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",                 0x03, 0x25, TYPE_INT,   ORDER_AB,   1},
        //设置策略模式
        {DEV_NO_EMS, STRANTAGY_TYPE,        "Set strategy mode",          O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,    "change", NULL, 0x03, 0x26, TYPE_INT,   ORDER_AB,   1},
        //使能BMS告警
        {DEV_NO_EMS, EN_BMS_ALARM,          "Enable BMS alarms",           O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,    "change", "0:Disabled;1:Enabled;",        0x03, 0x27, TYPE_INT,   ORDER_AB,   1},
        //使能风机跟随功率
        {DEV_NO_EMS, EN_DRAUG_FLOW_POWER   , "Enable fan to follow power",  O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "0:Disabled;1:Enabled;",    0x03, 0x28, TYPE_INT,   ORDER_AB,   1},
        //风机跟随功率门限
        {DEV_NO_EMS, EN_DRAUG_FLOW_POWER_T , "Fan following power threshold(kW)",  O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,       "change", "",                0x03, 0x29, TYPE_INT,   ORDER_AB,   1},
        //使能风机跟随温度
        {DEV_NO_EMS, EN_DRAUG_FLOW_TEM     , "Enable fan to follow TEMP",  O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "0:Disabled;1:Enabled;",    0x03, 0x2a, TYPE_INT,   ORDER_AB,   1},
        //风机跟随温度门限
        {DEV_NO_EMS, EN_DRAUG_FLOW_TEM_T   , "Fan following TEMP threshold(℃)",  O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",                 0x03, 0x2b, TYPE_INT,   ORDER_AB,   1},
        //跟随时间死区
        {DEV_NO_EMS, DRAUG_DEAE_TIME       , "Follow time deadband(s)",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",                 0x03, 0x2c, TYPE_INT,   ORDER_AB,   1},
        //储能电表功率方向
        {DEV_NO_EMS, METER_INVERT,          "Power direction of PCS-meter",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "0:Normal;1:Reversed;",   0x03, 0x2d, TYPE_INT,   ORDER_AB,   1},
        //自动PCS关机
        {DEV_NO_EMS, PCS_AUTO_TURN,         "PCS auto-shutdown",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "0:Disabled;1:Enabled;",   0x03, 0x2e, TYPE_INT,   ORDER_AB,   1},
        //自动PCS关机时间
        {DEV_NO_EMS, PCS_AUTO_TURN_OFF_TIME,"PCS auto-shutdown time(s)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x2f, TYPE_INT,   ORDER_AB,   1},
        //PACK消防触发温度
        {DEV_NO_EMS, PACK_ALARM_LIM_TEMP,   "TEMP to trigger PACK fire system(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x30, TYPE_INT,   ORDER_AB,   1},
        //工作时电芯抽样间隔
        {DEV_NO_EMS, WORK_SAMPLE_PERIOD,    "Sampling interval of battery cells during work(s)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x31, TYPE_INT,   ORDER_AB,   1},
        //空闲时电芯抽样间隔
        {DEV_NO_EMS, IDLE_SAMPLE_PERIOD ,   "Sampling interval of battery cells in idle(s)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x32, TYPE_INT,   ORDER_AB,   1},

        //使能液冷机管理
        {DEV_NO_EMS, ENABLE_AUTO_COOL,          "Enable liquid cooling MGT",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "0:Disabled;1:Enabled;",              0x03, 0x33, TYPE_INT,   ORDER_AB,   1},
        //使能间隙自循环
        {DEV_NO_EMS, EN_SELF_LOOP_DIFF,         "Enable gap self circulation",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "0:Disabled;1:Enabled;",              0x03, 0x34, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, BEFORE_WORK_TIME    ,   "工作之前时间液冷启动(min)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",       0x03, 0x35, TYPE_INT,   ORDER_AB,   1},
        //制热启动温度
        {DEV_NO_EMS, WORK_START_HOT_TEMP ,      "Heating start-up temperature(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x35, TYPE_INT,   ORDER_AB,   1},
        //制热停止温度
        {DEV_NO_EMS, WORK_END_HOT_TEMP   ,      "Heating stop temperature(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x36, TYPE_INT,   ORDER_AB,   1},
        //制冷启动温度
        {DEV_NO_EMS, WORK_START_COOL_TEMP,      "Cooling start-up temperature(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x37, TYPE_INT,   ORDER_AB,   1},
        //制冷停止温度
        {DEV_NO_EMS, WORK_END_COOL_TEMP  ,      "Cooling stop temperature(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x38, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, PREPARE_START_HOT_TEMP ,"预热段制热启动温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x3a, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, PREPARE_END_HOT_TEMP   ,"预热段制热停止温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x39, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, PREPARE_START_COOL_TEMP,"预热段制冷启动温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x3a, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, PREPARE_END_COOL_TEMP  ,"预热段制冷停止温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x3b, TYPE_INT,   ORDER_AB,   1},

        // {DEV_NO_EMS, STOP_START_HOT_TEMP ,   "空闲段制热启动温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x3c, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, STOP_END_HOT_TEMP   ,   "空闲段制热停止温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x3d, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, STOP_START_COOL_TEMP,   "空闲段制冷启动温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x3e, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, STOP_END_COOL_TEMP  ,   "空闲段制冷停止温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x3f, TYPE_INT,   ORDER_AB,   1},



        //制热目标温度
        {DEV_NO_EMS, WORK_DIST_HOT_TEMP    ,    "Heating target temperature(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x39, TYPE_INT,   ORDER_AB,   1},
        //制冷目标温度
        {DEV_NO_EMS, WORK_DIST_COOL_TEMP   ,    "Cooling target temperature(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x3a, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, STOP_DIST_HOT_TEMP    ,"空闲段制热目标温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x42, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, STOP_DIST_COOL_TEMP   ,"空闲段制冷目标温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x43, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, PREPARE_DIST_HOT_TEMP ,"预热段制热目标温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x44, TYPE_INT,   ORDER_AB,   1},
        // {DEV_NO_EMS, PREPARE_DIST_COOL_TEMP,"预热段制冷目标温度",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x45, TYPE_INT,   ORDER_AB,   1},
        //使能空调控制
        {DEV_NO_EMS, ENABLE_AIR_COND,          "Enable AC control",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,        "change", "0:Disabled;1:Enabled;",              0x03, 0x3b, TYPE_INT,   ORDER_AB,   1},
        //空调提前启动时间
        {DEV_NO_EMS, AIR_COND_AHEAD_TIME,      "AC early start time(min)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,     "change", "",              0x03, 0x3c, TYPE_INT,   ORDER_ABCD,   1},
        //防逆流滑动窗口
        {DEV_NO_EMS, PROTECT_REVERSE_WIN,      "Anti-backflow sliding window(s)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,     "change", "",              0x03, 0x3e, TYPE_UINT,   ORDER_AB,   1},
        
        //最大需量
        {DEV_NO_EMS, MAX_DEMAND,               "Maximum demand(kW)",         O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,                  "both", "",                 0x03, 0x3f, TYPE_INT, ORDER_ABCD, 1},
        //光伏总最大功率
        {DEV_NO_EMS, PV_MAX_POWER,             "Maximum total power of PV(kW)",   O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,       "both", "",                 0x03, 0x41, TYPE_FLOAT, ORDER_ABCD, 1},
        //需量滑动窗口
        {DEV_NO_EMS, DEMAND_WIN,               "Demand sliding window(s)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,        "change", "",              0x03, 0x43, TYPE_UINT,   ORDER_AB,   1},
        //使能BMS功率限制
        {DEV_NO_EMS, BMS_POWER_LIMIT,          "Enable BMS power limitation",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "0:Disabled;1:Enabled;",   0x03, 0x44, TYPE_INT,   ORDER_AB,   1},
        //使能低压需量控制
        {DEV_NO_EMS, ENPROTECTRASLV ,          "Enable low voltage demand control",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,   "change", "0:Disabled;1:Enabled;",0x03, 0x45, TYPE_INT,   ORDER_AB,   1},
        //低压变压器容量
        {DEV_NO_EMS, PLVMLMAX ,                "Low voltage transformer capacity(kVA)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,   "change", "",                  0x03, 0x46, TYPE_INT,   ORDER_AB,   1},
        //低压需量控制系数
        {DEV_NO_EMS, EMS_K4 ,                  "Low voltage demand control factor(%)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,   "change", "",                0x03, 0x47, TYPE_INT,   ORDER_AB,   1},
        //空调延后关机时间
        {DEV_NO_EMS, AIR_COND_DELAY_TIME,      "Delayed shutdown time of AC(min)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,     "change", "",              0x03, 0x48, TYPE_INT,   ORDER_ABCD,   1},
        //启动自循环温差
        {DEV_NO_EMS, SELF_LOOP_DIFF,            "Start self circulating TEMP difference(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x4a, TYPE_FLOAT,  ORDER_ABCD,   1},
        
        
        //单体过压保护阈值
        {DEV_NO_EMS, SINGLE_MAX_VOLTAGE,       "Single cell Over-V protect threshold(V)",      O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x4c, TYPE_FLOAT,   ORDER_ABCD,   1},
        //单体欠压保护阈值
        {DEV_NO_EMS, SINGLE_MIN_VOLTAGE,       "Single cell Under-V protect threshold(V)",      O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x4e, TYPE_FLOAT,   ORDER_ABCD,   1},
        //使能单体电压保护
        {DEV_NO_EMS, EN_CELL_VOLT_PROTECT,     "Enable single cell voltage protection",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "0:Disabled;1:Enabled;",                      0x03, 0x50, TYPE_INT,   ORDER_AB,   1},
        //单体过压保护偏差
        {DEV_NO_EMS, SINGLE_MAX_VOLTAGE_ERR,   "Single cell Over-V protect difference(V)",      O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x51, TYPE_FLOAT,   ORDER_ABCD,   1},
        //单体欠压保护偏差
        {DEV_NO_EMS, SINGLE_MIN_VOLTAGE_ERR,   "Single cell Under-V protect difference(V)",      O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,    PROPERTY_REALDATA, "change", "",                      0x03, 0x53, TYPE_FLOAT,   ORDER_ABCD,   1},
        //禁止放电SOC回差
        {DEV_NO_EMS, EMS_SOCMINERR,            "SOC Prohibited discharging difference(%)",   O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,  PROPERTY_REALDATA, "change", "",                              0x03, 0x55, TYPE_FLOAT, ORDER_ABCD, 1},
        //禁止充电SOC回差
        {DEV_NO_EMS, EMS_SOCMAXERR,            "SOC Prohibited charging difference(%)",   O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,  PROPERTY_REALDATA, "change", "",                              0x03, 0x57, TYPE_FLOAT, ORDER_ABCD, 1},
        // 系统最大放电功率
        {DEV_NO_EMS, DISCHARGE_THRES_HOLD,     "Maximum discharge power of the system(kW)",   O_WRONLY | O_RDONLY, EMS_POINT_INT,  PROPERTY_REALDATA, "change", "",                                              0x03, 0x5b, TYPE_FLOAT, ORDER_ABCD, 1},
        // 系统最大充电功率
        {DEV_NO_EMS, CHARGE_MAX_POWER,         "Maximum charging power of the system(kW)",   O_WRONLY | O_RDONLY, EMS_POINT_INT,  PROPERTY_REALDATA, "change", "",                                              0x03, 0x5d, TYPE_FLOAT, ORDER_ABCD, 1},
        // 需量门限所使用的缓冲值(类比防逆流余量)
        {DEV_NO_EMS, DEMAND_MARGIN,             "Demand Margin(kW)",                        O_WRONLY | O_RDONLY, EMS_POINT_INT,  PROPERTY_REALDATA, "change", "",                                              0x03, 0x5f, TYPE_INT, ORDER_AB, 1},
        // 变压器保护余量
        {DEV_NO_EMS, DYNAMIC_AUGMENT,          "Dynamic increase allowance(kW)",        O_WRONLY | O_RDONLY, EMS_POINT_INT,  PROPERTY_REALDATA, "change", "",                                   0x03, 0x60, TYPE_INT, ORDER_AB, 1},

         // (设置动作)
        //开关机
        {DEV_NO_EMS, ON_OFF_STATE,         "Power on/off",            O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,   "both", "0:Off;1:On;",         0x03, 0x100, TYPE_INT,   ORDER_AB,   1},
        //设置有功功率
        {DEV_NO_EMS, SET_ACT_POWER,        "Set active power(kW)",      O_WRONLY | O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA,  "both", "",                     0x03, 0x102, TYPE_FLOAT, ORDER_ABCD, 1},
        //设置无功功率
        {DEV_NO_EMS, SET_REACTPOWER,       "Set reactive power(kVar)",      O_WRONLY | O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA,  "both", "",                     0x03, 0x104, TYPE_FLOAT, ORDER_ABCD, 1},
        //设置并离网模式
        {DEV_NO_EMS, SET_CONNECT,          "Set on/off-grid mode",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,    "both", "0:Off-grid;1:On-grid;",        0x03, 0x106, TYPE_INT,   ORDER_AB,    1},
        //清除保护
        {DEV_NO_EMS, CLEAR_ALARM,          "Clear all protections",          O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,    "both", "",                     0x03, 0x107, TYPE_INT,   ORDER_AB,   1},
        //使能动环保护
        {DEV_NO_EMS, EN_IO_PROTECT,        "Enable dynamic environment protection",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,    "both", "0:Disabled;1:Enabled;",         0x03, 0x108, TYPE_INT,   ORDER_AB,   1},     
        //液冷机控制
        {DEV_NO_EMS, TMS_CTRL_STATE,     "Liquid cooling control",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,    "both", "0:Standby;1:Cooling;2:Heating;3:Pump circulation;", 0x03, 0x10a, TYPE_INT,   ORDER_AB,   1},     
        //脱扣
        {DEV_NO_EMS, TRIP    ,             "Trip",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,        "both", "",              0x03, 0x10c, TYPE_INT,   ORDER_AB,   1},     
        //限制光伏功率百分比
        {DEV_NO_EMS, SET_PV_POWER,         "Restrict PV power percentage",O_WRONLY | O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA,  "change", "",              0x03, 0x10f,TYPE_FLOAT, ORDER_AB, 1},
        //开始充电功率
        {DEV_NO_EMS, EMS_CHARGE_STARTPOWER, "Starting charging power(kW)",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",      0x03, 0x110, TYPE_INT,   ORDER_AB,   1},
        //开始放电功率
        {DEV_NO_EMS, EMS_DISCHARGE_THRESHOLD, "Starting discharge power(kW)",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",    0x03, 0x111, TYPE_INT,   ORDER_AB,   1},
        //风机
        {DEV_NO_EMS, FAN,                   "Fan",      O_WRONLY | O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "change", "",             0x03, 0x112, TYPE_INT,   ORDER_AB,   1},
        //输出制冷目标温度
        {DEV_NO_EMS, TMS_CTRL_DIST_TEMP_COLD ,  "Output cooling target temperature(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x113, TYPE_INT,   ORDER_AB,   1},
        //输出制热目标温度
        {DEV_NO_EMS, TMS_CTRL_DIST_TEMP_HOT ,  "Output heating target temperature(℃)",    O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,      "change", "",              0x03, 0x114, TYPE_INT,   ORDER_AB,   1},
        //BMS上下高压
        {DEV_NO_EMS, BMS_UPPER,          "Set BMS HV_Connect",            O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,   "both", "0:HV connect;1:HV disconnect;",         0x03, 0x115, TYPE_INT,   ORDER_AB,   1},
        //启用无功补偿
        {DEV_NO_EMS, EN_RP_COMP,       "enable repower compensate",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,    "both", "0:Disabled;1:Enabled;",         0x03, 0x116, TYPE_INT,   ORDER_AB,   1},     
        // 设置功率因数
        {DEV_NO_EMS, SET_POWER_FACTOR,        "Set power factor",      O_WRONLY | O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA,  "both", "",                     0x03, 0x117, TYPE_FLOAT, ORDER_ABCD, 1},

        // 只读点位
        //系统运行状态
        {DEV_NO_EMS, SYSTEM_STATUS,        "System operating status",     O_RDONLY, EMS_POINT_INT,   PROPERTY_REALDATA, "both", "0:Normal;1:Abnormal;",                 0x04, 0x00, TYPE_INT,   ORDER_AB,    1},
        //逆流窗口功率
        {DEV_NO_EMS, GRID_ACT_POWER,       "Anti-backflow window power(kW)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x01, TYPE_FLOAT, ORDER_ABCD, 1},
        //负载功率
        {DEV_NO_EMS, LOAD_ACT_POWER,       "Load power(kW)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x03, TYPE_FLOAT, ORDER_ABCD, 1},
        //期望有功功率
        {DEV_NO_EMS, EXPECT_ACT_POWER,     "Expected active power(kW)",     O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x05, TYPE_FLOAT, ORDER_ABCD,  1},
        //LC输入功率
        {DEV_NO_EMS, LOCAL_CTRL_POWER,     "LC input power(kW)",       O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA,  "both", "",                            0x04, 0x07, TYPE_FLOAT,  ORDER_ABCD, 1},
        //额定功率
        {DEV_NO_EMS, CAB_POWER ,           "Rated power(kW)",         O_RDONLY,EMS_POINT_INT,     PROPERTY_REALDATA , "off",   "" ,                            0x04, 0x09, TYPE_INT,    ORDER_AB,    1},
        //额定容量
        {DEV_NO_EMS, CAB_CAPACITY  ,       "Rated capacity(kWh)",         O_RDONLY, EMS_POINT_INT,    PROPERTY_REALDATA, "off", "",                                0x04, 0x0a, TYPE_INT,   ORDER_AB,   1},
        //告警数量
        {DEV_NO_EMS, ALARMCNT,             "Number of alarms",         O_RDONLY, EMS_POINT_INT,   PROPERTY_REALDATA,    "both", "",                              0x04, 0x0e, TYPE_INT,   ORDER_AB,   1},
        //禁充保护
        {DEV_NO_EMS, PROHIBITE_CHARGE,     "Prohibited charging protection",          O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:Unprotected;1:Protected;",                 0x04, 0x0f, TYPE_INT, ORDER_AB, 1},
        //禁放保护
        {DEV_NO_EMS, PROHIBITE_DISCHARGE,  "Prohibited discharging protection",          O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:Unprotected;1:Protected;",                 0x04, 0x10, TYPE_INT, ORDER_AB, 1},
        //上次充放功率
        {DEV_NO_EMS, LAST_CHADISCHA_STA,   "Last charge and discharge power(kW)",       O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "",                 0x04, 0x11, TYPE_INT, ORDER_AB, 1},
        //DOD保护
        {DEV_NO_EMS, DOD_LIMITED,          "DOD protection",          O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Unprotected;1:Protected;", 0x04, 0x12, TYPE_UINT, ORDER_AB, 1},
        //直流总压异常
        {DEV_NO_EMS, DC_TOTAL_VOL_LOW,     "DC total voltage abnormal",     O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",     0x04, 0x14, TYPE_UINT, ORDER_AB, 1},
        //最大可充功率
        {DEV_NO_EMS, MAX_CHARGE_POWER  ,   "Maximum rechargeable power(kW)",     O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "change", "",               0x04, 0x15, TYPE_FLOAT, ORDER_ABCD, 1},
        //最大可放功率
        {DEV_NO_EMS, MAX_DISCHA_POWER  ,   "Maximum dischargable power(kW)",     O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "change", "",               0x04, 0x17, TYPE_FLOAT, ORDER_ABCD, 1},
        //光伏设置百分比
        {DEV_NO_EMS, PV_CUR_PERCENT,         "Set PV percentage(%)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA,  "change", "",              0x04, 0x19, TYPE_INT, ORDER_ABCD, 1},
        //EMS周期心跳指示
        {DEV_NO_EMS, EMS_CIRCLE_BEAT,      "EMS cycle heartbeat indicator", O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "", 0x4, 0x21, TYPE_INT, ORDER_ABCD, 1},
        //PCS总有功功率
        {DEV_NO_EMS, PCS_ACTIVEPOWER,      "PCS total active power(kW)",  O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",  0x4, 0x23, TYPE_FLOAT, ORDER_AB, 1},
        //PCS总无功功率
        {DEV_NO_EMS, PCS_REACTIVEPOWER,    "PCS total reactive power(kVar)",  O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",  0x4, 0x24, TYPE_FLOAT, ORDER_AB, 1},
        //系统运行状态字
        {DEV_NO_EMS, SYSTEM_STATUS_W,        "System operation status word",     O_RDONLY, EMS_POINT_INT,   PROPERTY_REALDATA, "both", "",         0x04, 0x25, TYPE_INT,   ORDER_ABCD,    1},
        //总有功功率
        {DEV_NO_EMS, TOTAL_ACTIVE_POWER,    "Total active power(kW)",  O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",  0x4, 0x27, TYPE_FLOAT, ORDER_ABCD, 1},
        //总无功功率
        {DEV_NO_EMS, TOTAL_REACTIVE_POWER,  "Total reactive power(kVar)",  O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",  0x4, 0x29, TYPE_FLOAT, ORDER_ABCD, 1},
        //总正向电能
        {DEV_NO_EMS, TOTAL_FORWARD_ENERGY,    "Total positive electrical energy(kWh)",  O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",  0x4, 0x3b, TYPE_FLOAT, ORDER_ABCD, 1},
        //总反向电能
        {DEV_NO_EMS, TOTAL_REVERSE_ENERGY,    "Total reverse electrical energy(kWh)",  O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",  0x4, 0x3d, TYPE_FLOAT, ORDER_ABCD, 1},
        // 逆流状态
        {DEV_NO_EMS, REFLUX_STATE,        "Reflux State",   O_RDONLY,       EMS_POINT_INT,  PROPERTY_REALDATA, "both",      "0:Normal;1:Occasional Excursion;2:Failed State;", 0x04, 0x53, TYPE_INT, ORDER_AB, 1},
        {DEV_NO_EMS, RESET_REFLUX_STATE_TIMES, "Reset Reflux State Times",   O_RDONLY,       EMS_POINT_INT,  PROPERTY_REALDATA, "change",      ""                                   , 0x04, 0x54, TYPE_INT, ORDER_AB, 1},
        
        //状态机当前状态
        // {DEV_NO_EMS, STATE_MACHINE,     "State Machine",      O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,    "both", "1:并网;2:纯离网;4:初始状态;0:待机;", 0x04, 0x53, TYPE_INT,   ORDER_AB,   1},
        //
        // 只读信号量
        //EMS限功率运行
        // {DEV_NO_EMS, POWER_LIMITED,      "EMS running in limited power",     O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:Unrestricted;1:Restricted;", 0x02, 0x00, TYPE_BOOL, ORDER_A, 1},
        //EMS逆流运行
        // {DEV_NO_EMS, POWER_REFLUX,       "EMS running in backflow",       O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:No backflow;1:Backflow occurred;", 0x02, 0x01, TYPE_BOOL, ORDER_A, 1},
        //手动保护
        {DEV_NO_EMS, SYS_STOPED,         "EMS manual protection",     O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Unprotected;1:Protected;", 0x02, 0x02, TYPE_BOOL, ORDER_A, 1},
        //电表故障
        {DEV_NO_EMS, METER_ERROR,        "PCS-meter malfunction",          O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",     0x02, 0x06, TYPE_BOOL,  ORDER_A, 1},
        //动环保护
        {DEV_NO_EMS, IO_PROTECT,         "Dynamic environment protection",          O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Unprotected;1:Protected;", 0x02, 0x08, TYPE_BOOL, ORDER_A, 1},
        //自定义提示1
        {DEV_NO_EMS, USER_ALARM1,        "Custom Tip 1",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "",                    0x02, 0x09,  TYPE_BOOL, ORDER_A,   1},
        //自定义提示2
        {DEV_NO_EMS, USER_ALARM2,        "Custom Tip 2",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "",                    0x02, 0x0a,  TYPE_BOOL, ORDER_A,   1},
        //自定义提示3
        {DEV_NO_EMS, USER_ALARM3,        "Custom Tip 3",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "",                    0x02, 0x0b,  TYPE_BOOL, ORDER_A,   1},
        //自定义停机1
        {DEV_NO_EMS, USER_STOP1 ,        "Custom Shutdown 1",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x0c,  TYPE_BOOL, ORDER_A,   1},
        //自定义停机2
        {DEV_NO_EMS, USER_STOP2 ,        "Custom Shutdown 2",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x0d,  TYPE_BOOL, ORDER_A,   1},
        //自定义停机3
        {DEV_NO_EMS, USER_STOP3 ,        "Custom Shutdown 3",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x0e,  TYPE_BOOL, ORDER_A,   1},
        //自定义告警
        {DEV_NO_EMS, USER_ALARM ,        "Custom alarm",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x0f,  TYPE_BOOL, ORDER_A,   1},
        //自定义提示告警
        {DEV_NO_EMS, USER_NOTICE_ALARM , "Custom prompt alarm",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x50,  TYPE_BOOL, ORDER_A,   1},
        //自定义重要告警
        {DEV_NO_EMS, USER_VITAL_ALARM ,  "Custom important alarm",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x51,  TYPE_BOOL, ORDER_A,   1},
        //自定义严重告警
        {DEV_NO_EMS, USER_SERVER_ALARM , "Custom critical alarm",    O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x52,  TYPE_BOOL, ORDER_A,   1},
 
        // {DEV_NO_EMS, ,          "柜消防灭火告警", O_RDONLY, EMS_POINT_INT,   PROPERTY_ALARM, "both", "0:正常;1:异常",   0x02, 0x0f,  TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS,  ,          "柜消防急停联动", O_RDONLY, EMS_POINT_INT,   PROPERTY_ALARM, "both", "0:正常;1:异常",   0x02, 0x10,  TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS, PCS_LINK_ERR,        "PCS通讯故障",    O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:正常;1:异常",     0x02, 0x11, TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS, BMS_LINK_ERR,        "BMS通讯故障",    O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:正常;1:异常", 0x02, 0x12, TYPE_BOOL, ORDER_A, 1},
        //PCS功率反馈异常
        // {DEV_NO_EMS, PCS_POWER_ERR,       "PCS power feedback abnormal", O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x13, TYPE_BOOL, ORDER_A, 1},
        //总消防
        {DEV_NO_EMS, ALL_FIRE,            "Host fire system",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x14, TYPE_BOOL, ORDER_A, 1},
        //外部消防
        {DEV_NO_EMS, EXTERN_FIRE,         "External fire system",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x15, TYPE_BOOL, ORDER_A, 1},
        //本机消防
        {DEV_NO_EMS, FIRE,                "Local fire protection",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x16, TYPE_BOOL, ORDER_A, 1},
        //温控系统异常
        {DEV_NO_EMS, TEMP_CTRL_ERR,       "Temperature control system abnormal",O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x1d, TYPE_BOOL, ORDER_A, 1},
        //空调启停控制
        {DEV_NO_EMS, AIR_COND_STARTSTOP_CTRL, "AC control", O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "both", "0:Off;1:On;", 0x02, 0x1e, TYPE_BOOL, ORDER_A, 1},
        // 系统电源
        {DEV_NO_EMS, EMS_POWER,           "System power",   O_RDONLY,       EMS_POINT_INT,  PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x1f, TYPE_BOOL, ORDER_A, 1},
        {DEV_NO_EMS, SINGLE_CELL_VOL_THRESHOLD_ALARM, "Single cell voltage threshold alarm", O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x20, TYPE_BOOL, ORDER_A, 1},
        // CPU使用率过高
        {DEV_NO_EMS, EMS_CPU_HIGH,           "CPU usage rate high",   O_RDONLY,       EMS_POINT_INT,  PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x27, TYPE_BOOL, ORDER_A, 1},
        // 内存使用率过高
        {DEV_NO_EMS, EMS_MEM_HIGH,           "Memory usage rate high",   O_RDONLY,       EMS_POINT_INT,  PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x28, TYPE_BOOL, ORDER_A, 1},
        // 磁盘使用率过高
        {DEV_NO_EMS, EMMC_DISK_HIGH,           "Disk usage rate high",   O_RDONLY,       EMS_POINT_INT,  PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x29, TYPE_BOOL, ORDER_A, 1},
        {DEV_NO_EMS, SSD_DISK_HIGH,           "Disk usage rate high",   O_RDONLY,       EMS_POINT_INT,  PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;", 0x02, 0x2a, TYPE_BOOL, ORDER_A, 1},
        
        // {DEV_NO_EMS, BMS_PACK_ALARM"1",  "Pack1消防告警",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "", 0x02, 0x13, TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS, BMS_PACK_ALARM"2",  "Pack2消防告警",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "", 0x02, 0x14, TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS, BMS_PACK_ALARM"3",  "Pack3消防告警",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "", 0x02, 0x15, TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS, BMS_PACK_ALARM"4",  "Pack4消防告警",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "", 0x02, 0x16, TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS, BMS_PACK_ALARM"5",  "Pack5消防告警",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "", 0x02, 0x17, TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS, BMS_PACK_ALARM"6",  "Pack6消防告警",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "", 0x02, 0x18, TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS, BMS_PACK_ALARM"7",  "Pack7消防告警",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "", 0x02, 0x19, TYPE_BOOL, ORDER_A, 1},
        // {DEV_NO_EMS, BMS_PACK_ALARM"8",  "Pack8消防告警",      O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "change", "", 0x02, 0x1a, TYPE_BOOL, ORDER_A, 1},
        
        //自定义充电时停机1
        {DEV_NO_EMS, USER_STOP_CHA1 ,        "Custom Shutdown in Charge1",   O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x21,  TYPE_BOOL, ORDER_A,   1},
        //自定义放电时停机2
        {DEV_NO_EMS, USER_STOP_DISCHA1 ,        "Custom Shutdown in Discharge1",  O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x22,  TYPE_BOOL, ORDER_A,   1},
        //自定义充电时停机3
        {DEV_NO_EMS, USER_STOP_CHA2 ,        "Custom Shutdown in Charge2",   O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x23,  TYPE_BOOL, ORDER_A,   1},
        //自定义放电时停机1
        {DEV_NO_EMS, USER_STOP_DISCHA2 ,        "Custom Shutdown in Discharge2",   O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x24,  TYPE_BOOL, ORDER_A,   1},
        //自定义充电时停机2
        {DEV_NO_EMS, USER_STOP_CHA3 ,        "Custom Shutdown in Charge3",   O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x25,  TYPE_BOOL, ORDER_A,   1},
        //自定义放电时停机3
        {DEV_NO_EMS, USER_STOP_DISCHA3 ,        "Custom Shutdown in Discharge3",   O_RDONLY,  EMS_POINT_INT,   PROPERTY_ALARM, "change", "0:Normal;1:Abnormal;",  0x02, 0x26,  TYPE_BOOL, ORDER_A,   1},
};
const int g_ems_base_variant_count = sizeof(ems_base_variant) / sizeof(ems_base_variant_t);

ems_base_variant_t ems_microgri_variant[] =
{
    //并离网识别反馈
    {DEV_NO_EMS, CONN_SIGN,    "conn sign",     O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:disconnect;1:connect;", 0x04, 0x300, TYPE_INT, ORDER_AB, 1},
    //ATS反馈
    {DEV_NO_EMS, ATS_SIGN,     "ats sign",      O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:second;1:primary;", 0x04, 0x301, TYPE_INT, ORDER_AB, 1},
    //负载断路器反馈
    {DEV_NO_EMS, LCB_SIGN,     "lcb sign",      O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:off;1:on;", 0x04, 0x302, TYPE_INT, ORDER_AB, 1},
    //并离网断路器反馈
    {DEV_NO_EMS, ICB_SIGN,     "icb sign",      O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:off;1:on;", 0x04, 0x303, TYPE_INT, ORDER_AB, 1},
    //柴发启停反馈
    {DEV_NO_EMS, DG_SIGN,      "DG sign",       O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:stop;1:start;", 0x04, 0x304, TYPE_INT, ORDER_AB, 1},
    //微网状态机状态
    {DEV_NO_EMS, STATE_MACHINE,    "StateMachine status",     O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:待机状态;1:市电并网状态;2:离网状态;3:柴发并网状态;4:初始状态;5:异常状态;6:默认;", 0x04, 0x305, TYPE_INT, ORDER_AB, 1},  

    //并离网断路器控制
    {DEV_NO_EMS, ICB_CTRL,      "icb ctrl",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,   "both", "",         0x03, 0x306, TYPE_INT, ORDER_AB, 1},
    //负载断路器控制
    {DEV_NO_EMS, LCB_CTRL,      "lcb ctrl",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,  "both", "",          0x03, 0x307, TYPE_INT, ORDER_AB, 1},
    //柴发控制
    {DEV_NO_EMS, DG_CTRL,       "DG ctrl",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,  "both", "",            0x03, 0x308, TYPE_INT, ORDER_AB, 1},
	
	//总光伏功率-- 功率汇总--便于多柜调试
    {DEV_NO_EMS, TOTAL_PV_POWER,    "Total Pv Power(kW)",  O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "", 0x04, 0x309, TYPE_FLOAT, ORDER_ABCD, 1},
    //总储能功率
    {DEV_NO_EMS, TOTAL_PCS_POWER,   "Total Pcs Power(kW)", O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "", 0x04, 0x30b, TYPE_FLOAT, ORDER_ABCD, 1},
    //平均SOC
    {DEV_NO_EMS, AVER_SOC,    "Aver Soc",     O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "", 0x04, 0x30d, TYPE_FLOAT, ORDER_ABCD, 1}, 

    //使能交流放转充
    {DEV_NO_EMS, EN_AC_D2C,      "Enable discharge to charge",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,  "both", "0:失能;1:使能;",          0x03, 0x30f, TYPE_INT, ORDER_AB, 1},
    //使能交流充转放
    {DEV_NO_EMS, EN_AC_C2D,      "Enable charge to discharge",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,  "both", "0:失能;1:使能;",            0x03, 0x310, TYPE_INT, ORDER_AB, 1},

    // 离网SOC上限
    {DEV_NO_EMS, SOC_NET_DEAD_MAX,         "Off-grid SOC Upper limit(%)",   O_WRONLY | O_RDONLY, EMS_POINT_INT,  PROPERTY_REALDATA, "change", "",                                              0x03, 0x311, TYPE_FLOAT, ORDER_ABCD, 1},
    // 离网SOC下限
    {DEV_NO_EMS, SOC_NET_DEAD_MIN,         "Off-grid SOC Lower limit (%)",   O_WRONLY | O_RDONLY, EMS_POINT_INT,  PROPERTY_REALDATA, "change", "",                                              0x03, 0x313, TYPE_FLOAT, ORDER_ABCD, 1},
    // 柴发额定功率
    {DEV_NO_EMS, DG_RATED_POWER,         "Rated power of DG(kW)",   O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,  PROPERTY_REALDATA, "change", "",                                              0x03, 0x315, TYPE_FLOAT, ORDER_ABCD, 1},
    // 柴发启动的SOC阈值
    {DEV_NO_EMS, DG_BOOT_SOC,         "SOC threshold for DG startup(%)",   O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,  PROPERTY_REALDATA, "change", "",                                              0x03, 0x317, TYPE_FLOAT, ORDER_ABCD, 1},
    // SOC告警阈值
    {DEV_NO_EMS, SOC_ALARM,         "SOC alarm threshold (%)",   O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,  PROPERTY_REALDATA, "change", "",                                              0x03, 0x319, TYPE_FLOAT, ORDER_ABCD, 1},
    // 柴发容量回差
    {DEV_NO_EMS, EMS_DG_DIFF,       "DG capacity diff (kW)",   O_WRONLY | O_RDONLY, EMS_POINT_FLOAT,  PROPERTY_REALDATA, "change", "",                                              0x03, 0x402, TYPE_FLOAT, ORDER_ABCD, 1},

    // SOC告警控制
    {DEV_NO_EMS, SOC_ALARM_CTRL, "Soc Alarm Ctrl",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,  "change", "",          0x03, 0x404, TYPE_INT, ORDER_AB, 1},
    // 负载断路器异常
    {DEV_NO_EMS, LCB_ABNORMAL, "Lcb startup abnormal",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,  "change", "0:正常;1:合闸异常;2:分闸异常;",          0x03, 0x406, TYPE_INT, ORDER_A, 1},
    // 并离网断路器异常
    {DEV_NO_EMS, ICB_ABNORMAL, "Icb startup abnormal",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,  "change", "0:正常;1:合闸异常;2:分闸异常;",          0x03, 0x407, TYPE_INT, ORDER_A, 1},
    // 柴发启动异常
    {DEV_NO_EMS, DG_ABNORMAL, "DG startup abnormal",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,  "change", "0:正常;1:启动异常;2:停机异常;",          0x03, 0x408, TYPE_INT, ORDER_A, 1},
    // 动作超时时间
    {DEV_NO_EMS, ACTION_TIMEOUT, "Execute action timeout",      O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA,  "change", "",          0x03, 0x40c, TYPE_INT, ORDER_AB, 1},
    // 柴发容量保护开关
    {DEV_NO_EMS, DG_CAP_PRO,       "diesel generator Capacity protection",         O_WRONLY | O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "change", "0:禁用;1:使能",         0x03, 0x40d, TYPE_INT, ORDER_AB, 1},

    //市电有功功率
    {DEV_NO_EMS, GRID_P,       "Active power of grid(kW)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x53f, TYPE_FLOAT, ORDER_ABCD, 1},
    //市电无功功率
    {DEV_NO_EMS, GRID_Q,       "Reactive power of grid(kVar)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x541, TYPE_FLOAT, ORDER_ABCD, 1},
    //充电桩有功功率
    {DEV_NO_EMS, PCU_P,       "Active power of charging station(kW)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x543, TYPE_FLOAT, ORDER_ABCD, 1},
    //储能有功功率
    {DEV_NO_EMS, PCS_P,       "Energy storage active power(kW)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x545, TYPE_FLOAT, ORDER_ABCD, 1},
    //储能无功功率
    {DEV_NO_EMS, PCS_Q,       "Energy storage reactive power(kVar)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x547, TYPE_FLOAT, ORDER_ABCD, 1},
    //PV发电功功率
    {DEV_NO_EMS, MPPT_P,       "PV generation power(kW)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x549, TYPE_FLOAT, ORDER_ABCD, 1},
    //储能直流功率
    {DEV_NO_EMS, BMS_P,       "Energy storage DC power(kW)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x54b, TYPE_FLOAT, ORDER_ABCD, 1},
    //储能SOC
    {DEV_NO_EMS, BMS_SOC,       "Energy storage SOC(%)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x54d, TYPE_FLOAT, ORDER_ABCD, 1},
    //光伏逆变器有功功率
    {DEV_NO_EMS, PV_P,       "Active power of PV inverter(kW)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x54f, TYPE_FLOAT, ORDER_ABCD, 1},
    //光伏逆变器无功功率
    {DEV_NO_EMS, PV_Q,       "Reactive power of PV inverter(kVar)",         O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "both", "",                             0x04, 0x551, TYPE_FLOAT, ORDER_ABCD, 1},
};
const int g_ems_microgri_count = sizeof(ems_microgri_variant) / sizeof(ems_base_variant_t);

ems_base_variant_t cab_variant[] = {
     // 柜%d运行状态
    {DEV_NO_EMS, CAB_SYSTEM_STATUS "%d",       "Cabinet %d operating status", O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "both", "0:Normal;", 0x04, 0x4000, TYPE_UINT, ORDER_AB, 1},
     // 柜%d禁充保护
    {DEV_NO_EMS, CAB_PROHIBITE_CHARGE "%d",    "Cabinet %d charging prohibited protection", O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "both", "0:Unprotected;1:Protected;", 0x04, 0x4001, TYPE_UINT, ORDER_AB, 1},
     // 柜%d禁放保护
    {DEV_NO_EMS, CAB_PROHIBITE_DISCHARGE "%d", "Cabinet %d discharging prohibited protection", O_RDONLY, EMS_POINT_INT, PROPERTY_REALDATA, "both", "0:Unprotected;1:Protected;", 0x04, 0x4002, TYPE_UINT, ORDER_AB, 1},
     // 柜%dDOD保护
    {DEV_NO_EMS, CAB_DOD_LIMITED "%d",      "Cabinet %d DOD protection", O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "both", "0:Unprotected;1:Protected;", 0x04, 0x4003, TYPE_UINT, ORDER_AB, 1},
     // 柜%d直流总压异常
    {DEV_NO_EMS, CAB_DC_TOTAL_VOL_LOW "%d", "Cabinet %d DC total voltage abnormal", O_RDONLY, EMS_POINT_INT, PROPERTY_ALARM, "both", "0:Normal;1:Abnormal;", 0x04, 0x4004, TYPE_UINT, ORDER_AB, 1},
     // 柜%d最大可充功率(kW)
    {DEV_NO_EMS, MAX_CHARGE_POWER "%d",     "Maximum rechargeable power of cabinet %d(kW)", O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "change", "", 0x04, 0x4005, TYPE_FLOAT, ORDER_ABCD, 1},
     // 柜%d最大可放功率(kW)
    {DEV_NO_EMS, MAX_DISCHA_POWER "%d",     "Maximum dischargable power of cabinet %d(kW)", O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "change", "", 0x04, 0x4007, TYPE_FLOAT, ORDER_ABCD, 1},
     // 柜%d期望有功功率(kW)
    {DEV_NO_EMS, EXPECT_ACT_POWER "%d",     "Expected active power of cabinet %d(kW)", O_RDONLY, EMS_POINT_FLOAT, PROPERTY_REALDATA, "change", "", 0x04, 0x4009, TYPE_FLOAT, ORDER_ABCD, 1},
};
const int g_cab_variant_count = sizeof(cab_variant) / sizeof(ems_base_variant_t);

//映射，存放点位名与对应的描述
typedef struct
{
    const char *tag_name;
    char *desc;
} ems_tag_description;

//板子信息与对应的映射
typedef struct
{
    char *b_name;
    int desc_map_size;
    ems_tag_description *desc_map;
} ems_board_desc_mapping;

ems_tag_description rk3568_map[] =
{
    {.tag_name = STRANTAGY_TYPE,.desc = "0:planning mode;1:Dynamic augmentation;3:Self use absorption;4:Emergency backup power;"},
};

ems_tag_description lc100_map[] =
{
    {.tag_name = STRANTAGY_TYPE,.desc = "0:planning mode;1:Dynamic augmentation;"},
};

ems_tag_description common_map[] =
{
    {.tag_name = STRANTAGY_TYPE,.desc = "0:planning mode;1:Dynamic augmentation;3:Self use absorption;4:Emergency backup power;"},
};


ems_board_desc_mapping ems_point_desc_map[] =
{
    {.b_name = "WLOSANY_RK3568",
    .desc_map_size = sizeof(rk3568_map) / sizeof(rk3568_map[0]),
    .desc_map = rk3568_map},
    {.b_name = "WLOSANY_LC100",
    .desc_map_size = sizeof(lc100_map) / sizeof(lc100_map[0]),
    .desc_map = lc100_map},
    {.b_name = "WLOSANY",
    .desc_map_size = sizeof(common_map) / sizeof(common_map[0]),
    .desc_map = common_map},
};

const int desc_num = sizeof(ems_point_desc_map) / sizeof(ems_point_desc_map[0]);

void set_point_desc() {
    int j = 0;
    int map_index = 0;
    const char *boardName = get_board_name();

    if (boardName == NULL) {
        ems_syslog(LOG_ERR, "Failed to get board name");
        return;
    }
    //遍历设备，比对设备名
    for (j = 0; j < desc_num; j++) {
        if (strstr(boardName, ems_point_desc_map[j].b_name) == boardName) {
            map_index = j;
            break;
        }
    }

    //便利设备描述表
    for (int i = 0; i < g_ems_base_variant_count; i++) {
        if (ems_base_variant[i].desc == NULL) {
            //遍历点位
            for(int k = 0; k < ems_point_desc_map[map_index].desc_map_size; k++)
            {
                if(strcmp(ems_base_variant[i].variant,ems_point_desc_map[map_index].desc_map->tag_name) == 0)
                {
                    ems_base_variant[i].desc = ems_point_desc_map[map_index].desc_map->desc;
                }
            }
        }
    }
}

char* alloc_node_tag(const char* device,const char* variant)
{
    if(!device || !variant)return NULL;
    size_t size = strlen(device) + strlen(variant) + 1;
    char * node = malloc(size);
    if(!node) return NULL;
    sprintf(node,"%s.%s",device,variant);
    return node;
}

#define NODE_SIZE DEV_NAME_LEN + TAG_NAME_LEN
double get_meter_active_power(const char *dev_no)
{
    double ret = dev_get_dev_tag_float(dev_no, METER_POWER);
    if (dev_get_dev_tag_int(DEV_NO_EMS, METER_INVERT) != 0)
    {
        return -ret;
    }
    else
    {
        return ret;
    }
}

#if 0 // 移动到cc_lc_com/collector_api.c
/*********************************PCS操作方法,不同PCS操作方法不一样********************************************/
void def_set_pcs_power(const char* dev_id, float val)
{
    pe_set_power(dev_id, val * EXPECT_POWER_FACTOR);
}
void def_set_pcs_repower(const char* dev_id, float val)
{
    pe_set_repower(dev_id, val * EXPECT_POWER_FACTOR);
}

void def_set_pcs_onoff(const char* dev_id, int val)
{
    if (val == 0)
    {
        pe_set_onoff(dev_id, PE_ADP_OFF);
    }
    else
    {
        pe_set_onoff(dev_id, PE_ADP_ON);
    }
    
}

void def_set_pcs_mode(const char* dev_id, int val)
{
    pe_set_mode(dev_id, val);
}

void dev_set_pcs_connect(const char* dev_id, PCS_CONNSTA_E val)
{
    pe_set_connect(dev_id, val);
}

double def_get_pcs_power(const char* dev_id)
{
    double value = 0;
    pe_get_power(dev_id, &value);
    return value;
}

double def_get_pcs_repower(const char* dev_id)
{
    double value = 0;
    pe_get_repower(dev_id, &value);
    return value;
}

float def_get_pcs_repower(const char* dev_id)
{
    return pe_get_repower(dev_id);
}

int def_get_pcs_onoff(const char* dev_id)
{
    return pe_get_onoff(dev_id);
}

int dev_get_pcs_connect(const char* dev_id)
{
    return pe_get_connect(dev_id);
}
#endif
void set_alarm_cnt(int alarmcnt)
{
    dev_set_dev_tag_int(DEV_NO_EMS, ALARMCNT,alarmcnt);
}


// void set_fault(int fault)
// {
//     if (fault>0)
//     {
//         if(dev_get_dev_tag_int(DEV_NO_EMS, DEVFAULT)!=1)
//             dev_set_dev_tag_int(DEV_NO_EMS, DEVFAULT,1); 
//     }
//     else
//     {
//         if(dev_get_dev_tag_int(DEV_NO_EMS, DEVFAULT)!=0)
//             dev_set_dev_tag_int(DEV_NO_EMS, DEVFAULT,0); 
//     }
    
// }

void set_sample_period_by_run_state()
{
    float bms_currue = dev_get_dev_tag_float(DEV_NO_BMS, RACK_CURRUE);
    if (CHECK_STOP(bms_currue))
    {
        set_sample_period("BMS_CELLS",dev_get_dev_tag_float(DEV_NO_EMS, IDLE_SAMPLE_PERIOD));
    }
    else
    {
        set_sample_period("BMS_CELLS",dev_get_dev_tag_float(DEV_NO_EMS, WORK_SAMPLE_PERIOD));
    }
}

extern int update_ems_user_cfg(usercfg_variant_t *pcfg, const char *node_id, tag_value *data_value);
extern char* create_ems_template(template_info_t *info_p,ems_base_variant_t **tags,int tags_size);
static void ems_config_data_change_cb(char *nod,tag_value *data_value)
{
    pcs_ctrl_var_t *var = get_pcs_ctrl_var();
    if(update_ems_user_cfg(&g_usercfg_variant,nod,data_value)<0)return;
    var->config_chaged = true;
    var->config_save = true;
}


void mul_ems_expect_power_set(float val)
{
    // 只有自动模式不可以写
    if(g_usercfg_variant.ControlMode == EMS_AUTO_MODE)
        return;
    check_lc_and_set(val);
}

int get_mode()
{
    return g_usercfg_variant.ControlMode;
}

static void ems_contrl_data_change_cb(char *nod,tag_value *data_value)
{
    if(data_value->type == 0)
        ems_syslog(LOG_NOTICE, "%s  -> [%ld]",nod,data_value->value.to_int);
    else
        ems_syslog(LOG_NOTICE, "%s  -> [%f]",nod,data_value->value.to_float);
    
    if(strcmp(nod,DEV_NO_EMS"."ON_OFF_STATE) == 0) 
    {
        if (data_value->value.to_int == 0)
        {
            dev_set_dev_tag_int(DEV_NO_EMS, SET_ACT_POWER,0);
            /* code */
        }
        
        set_total_pcs_onoff_reflush_time(data_value->value.to_int);
    }
    else if(strcmp(nod,DEV_NO_EMS"."SET_ACT_POWER) == 0) mul_ems_expect_power_set(data_value->value.to_float);
    else if(strcmp(nod,DEV_NO_EMS"."SET_REACTPOWER) == 0) set_total_pcs_repower(data_value->value.to_float);
    else if(strcmp(nod,DEV_NO_EMS"."SET_CONNECT) == 0) set_total_pcs_connect(data_value->value.to_int);
    else if(strcmp(nod,DEV_NO_EMS"."SET_PV_POWER) == 0) set_total_pv_power(data_value->value.to_float);
    else if(strcmp(nod,DEV_NO_EMS"."CLEAR_ALARM) == 0)
    {
        if ((data_value->type == 0) && (data_value->value.to_int != 0))
        {
            clean_stop_state();
            clean_prohibite_chag_and_dischag(get_pcs_ctrl_var()->cabinet_cnt);
            dev_set_dev_tag_int(DEV_NO_EMS, CLEAR_ALARM,0);
        }
    }
    else if(strcmp(nod,DEV_NO_EMS"."CONTROL_MODE) == 0)
    {
        //切换为非自动模式,且这次模式和上一次不同(虽然是变化回调，但是上电第一次所有回调都会被调用一次，所以多了一个判断)
        if ((data_value->value.to_int != EMS_AUTO_MODE) && (data_value->value.to_int != data_value->value_last.to_int))
        {
            // dev_set_dev_tag_int(DEV_NO_EMS, SET_ACT_POWER,0);
        }
    }
    else if (strcmp(nod, DEV_NO_EMS "." BMS_UPPER) == 0)
    {
        set_total_bms_connect(data_value->value.to_int);
    }
    else if (strcmp(nod, DEV_NO_EMS "." EN_RP_COMP ) == 0) // PCS 无功功率补偿,设置功率因数
    {
        set_total_pcs_repower_comp(data_value->value.to_int);
    }
    else if (strcmp(nod, DEV_NO_EMS "." SET_POWER_FACTOR ) == 0) // PCS 无功功率补偿,设置功率因数
    {
        set_total_pcs_power_factor(data_value->value.to_float);
    }
}



static void ems_contrl_data_change_cb_lc_ctr_mode(char *nod,tag_value *data_value)
{
    if(data_value->type == 0)
        ems_syslog(LOG_NOTICE, "%s  -> [%ld]",nod,data_value->value.to_int);
    else
        ems_syslog(LOG_NOTICE, "%s  -> [%f]",nod,data_value->value.to_float);
    
    if(strcmp(nod,DEV_NO_EMS"."ON_OFF_STATE) == 0)
    {
        // 不处理，在lc_server_sync_tag中同步
    }
    else if(strcmp(nod,DEV_NO_EMS"."SET_ACT_POWER) == 0)
    {
        mul_ems_expect_power_set(data_value->value.to_float);
    }
    else if(strcmp(nod,DEV_NO_EMS"."SET_REACTPOWER) == 0)
    {
        // 不处理
    }
    else if(strcmp(nod,DEV_NO_EMS"."SET_CONNECT) == 0)
    {
        // 不处理，在lc_server_sync_tag中同步
    }
    else if(strcmp(nod,DEV_NO_EMS"."SET_PV_POWER) == 0) set_total_pv_power(data_value->value.to_float);
    else if(strcmp(nod,DEV_NO_EMS"."CLEAR_ALARM) == 0)
    {

        if(dev_get_dev_tag_int(DEV_NO_EMS, CLEAR_ALARM)==0)
            return;
        clean_prohibite_chag_and_dischag(get_pcs_ctrl_var()->cabinet_cnt);
        dev_set_dev_tag_int(DEV_NO_EMS, CLEAR_ALARM,0);
    }
    else if(strcmp(nod,DEV_NO_EMS"."CONTROL_MODE) == 0)
    {
        
        if (data_value->value.to_int != EMS_AUTO_MODE)
        {
            //切换为远程模式,且这次模式和上一次不同(虽然是变化回调，但是上电第一次所有回调都会被调用一次，所以多了一个判断)
            if (data_value->value.to_int != data_value->value_last.to_int)
            {
                dev_set_dev_tag_int(DEV_NO_EMS, SET_ACT_POWER, 0);
            }
        }
        else
        {
            dev_set_dev_tag_int(DEV_NO_EMS, ON_OFF_STATE, 1);
        }
    }
    else if (strcmp(nod, DEV_NO_EMS "." BMS_UPPER) == 0)
    {
        set_total_bms_connect(data_value->value.to_int);
    }
}




#define SET_AMASK_BIT(alarm_flag, bit)	(alarm_flag |= (1ULL << (bit)))
#define CLR_AMASK_BIT(alarm_flag, bit)	(alarm_flag &= ~(1ULL << (bit)))

static void fast_stop_temp_ctrl_cb(char *nod,tag_value *data_value)
{
   int val = ALARM_STATUS_NO;
    static int status = 0;

    if(data_value->type == TYPE_TAG_INT)
        val = data_value->value.to_int;
    else 
        val = data_value->value.to_float;
    if (strcmp(nod,DEV_NO_AIRCOND"."STATE_ONLINE)==0)
    {
        if(val == 0)
        {
            SET_AMASK_BIT(status,0);
        }
        else
        {
            CLR_AMASK_BIT(status,0);
        }
    }
    else if (strcmp(nod,DEV_NO_AIRCOND"."DEV_FAULT)==0)
    {
        if(val == ALARM_STATUS_TRIG)
        {
            SET_AMASK_BIT(status,1);
        }
        else
        {
            CLR_AMASK_BIT(status,1);
        }
    }
    else if (strcmp(nod,DEV_NO_LIQUID"."STATE_ONLINE)==0)
    {
        if(val == 0)
        {
            SET_AMASK_BIT(status,2);
        }
        else
        {
            CLR_AMASK_BIT(status,2);
        }
    }
    else if (strcmp(nod,DEV_NO_LIQUID"."DEV_FAULT)==0)
    {
        if(val == ALARM_STATUS_TRIG)
        {
            SET_AMASK_BIT(status,3);
        }
        else
        {
            CLR_AMASK_BIT(status,3);
        }
    }
    ems_syslog(LOG_WARNING, "fast stop temp_ctrl [nod:%s] -> [%d]",nod,val);
    if(status > 0)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, TEMP_CTRL_ERR,  1);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS, TEMP_CTRL_ERR,  0);
    }
}

void check_all_cell()
{
    char temp_name[64] = {0};
    int packhightempnum[20] = {0};
    int packno = 0;
    int packtempnum = get_cabinet_info()->pack_temp_point;
    int temp_int = dev_get_dev_tag_int(DEV_NO_EMS,PACK_ALARM_LIM_TEMP);
    int temp_limit_int = (temp_int== 0?80:temp_int);
    for (int i = 1; ; i++)
    {
        sprintf(temp_name,"BMS_CELLS.RkCeTemp%03d",i);
        // pcs_ctrl_var_t *var = get_pcs_ctrl_var();
        double val = 0;
        if(collector_read_one_float_data(temp_name,&val)<0)
        {
            ems_syslog(LOG_NOTICE," no cell name :%s\n",temp_name);
            break;
        } 
        if (val >= temp_limit_int)//默认值80
        {
            packno = (packtempnum%i == 0)?(packtempnum/i):(packtempnum+1);
            packhightempnum[packno]++;
        }     
    }
    for (int i = 0; i < 20; i++)
    {
        if ((packhightempnum[i] >= 2)&&(packhightempnum[i] <= 10))
        {
            char tmp_no[64] = {0};
            sprintf(tmp_no,BMS_PACK_ALARM"%d",i);
            ems_syslog(LOG_NOTICE,"temp_limit_int %d hightemppack %d num %d trigger fire :%s\n",temp_limit_int,i,packhightempnum[i],temp_name);
            dev_set_dev_tag_int(DEV_NO_EMS,tmp_no,1);
        }
    }
    
       
    
    
}
void *check_pack_temp_task(void *args)
{
    pcs_ctrl_var_t *var = args;
    pthread_detach(pthread_self());

    while (1)
    {
        int ret = sem_wait_time(&var->rack_temp_sem, 0, 10);
        if (ret == 0)
        {
            check_all_cell();
        }
    }
    return NULL;
}
void check_pack_temp(pcs_ctrl_var_t *var) 
{
    pthread_t tip;
    sem_init(&var->rack_temp_sem,0,0);
    if (0 == pthread_create(&tip,NULL,check_pack_temp_task,var))
    {
        pthread_setname_np(tip, "checkPackTask");
    }
}


static void fast_stop_dod_protect_cb(char *nod,tag_value *data_value)
{
    static int chadischapro = 0; //0正常1上限2下限
    int need_dod = 0;
    int en_dod = dev_get_dev_tag_int(DEV_NO_EMS, EN_PROTECT_DOD);
    if(en_dod)
    {
        double value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
        ems_syslog(LOG_WARNING, "fast stop discharge [nod:%s] -> [%f]",nod,value);
        double socmax = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMAX);
        double socmin = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMIN);
        if(value > socmax)
        {
            chadischapro = 1;   
        }
        else if (value < socmin)
        {
            chadischapro = 2;
        }
        
        if (chadischapro > 0)
        {
            double socmaxerr = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMAXERR);
            double socminerr = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMINERR);
            if ((chadischapro == 1) && (value < (socmax - socmaxerr)))
            {
                chadischapro = 0;
            }
            else if ((chadischapro == 2) && (value > (socmin + socminerr)))
            {
                chadischapro = 0;
            }

            if (chadischapro>0)
            {
                need_dod = 1;
            }
            else
            {
                need_dod = 0;
                if (dev_get_dev_tag_int(DEV_NO_EMS, DOD_LIMITED)!=0)
                {
                    dev_set_dev_tag_int(DEV_NO_EMS, DOD_LIMITED,0);
                }
            }
        } 
        else
        {
            need_dod = 0;
        } 
    }
    if (need_dod != dev_get_dev_tag_int(DEV_NO_EMS, DOD_LIMITED))
    {
        dev_set_dev_tag_int(DEV_NO_EMS, DOD_LIMITED,need_dod);
    } 
    if (need_dod == 1 && (get_cabinet_info()->mode_microgrid == MODE_MICROGRID_NONE))
    {
        set_lc_power(0); 
    }
}


static void fast_stop_charge_tab_cb(char *nod,tag_value *data_value)
{
    static int pro_chg_flag = 0;
    if (strcmp(nod,DEV_NO_BMS"."CURRENT_SOC) == 0)
    {
        if(dev_get_dev_tag_int(DEV_NO_EMS, EN_PROTECT_DOD))
        {
            double value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
            double socmax = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMAX);
            double socmaxerr = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMAXERR);
            if (value > socmax)
            {
                SET_AMASK_BIT(pro_chg_flag,0);
            }
            else if (value < (socmax - socmaxerr))
            {
                CLR_AMASK_BIT(pro_chg_flag,0);
            }
        }
        else
        {
            CLR_AMASK_BIT(pro_chg_flag,0);
        }
    } 
    else if (strcmp(nod,DEV_NO_BMS"."RACK_MAX_VOLT) == 0)
    {
        if(g_usercfg_variant.EnCellVoltProtect)
        {
            int val = data_value->value.to_int;
            int cur_vol_min = dev_get_dev_tag_int(DEV_NO_BMS, RACK_MIN_VOLT);
            int volmax = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MAX_VOLTAGE)*1000;
            int volmaxerr = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MAX_VOLTAGE_ERR)*1000;
            int volmin = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MIN_VOLTAGE)*1000;
            int volminerr = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MIN_VOLTAGE_ERR)*1000;
            if (val > volmax)
            {
                SET_AMASK_BIT(pro_chg_flag,5);
                dev_set_dev_tag_int(DEV_NO_EMS, SINGLE_CELL_VOL_THRESHOLD_ALARM, 1);
            }
            else if (val < (volmax - volmaxerr))
            {
                CLR_AMASK_BIT(pro_chg_flag, 5);
                if ((cur_vol_min > (volmin + volminerr)))
                {
                    dev_set_dev_tag_int(DEV_NO_EMS, SINGLE_CELL_VOL_THRESHOLD_ALARM, 0);
                }
            }
        }
        else
        {
            CLR_AMASK_BIT(pro_chg_flag,5);
        }
    }
    else if (strcmp(nod, DEV_NO_EMS "." USER_STOP_CHA1) == 0 || strcmp(nod, DEV_NO_EMS "." USER_STOP_CHA2) == 0 || strcmp(nod, DEV_NO_EMS "." USER_STOP_CHA3) == 0)
    {
        int val = (data_value->type == TYPE_TAG_INT) ? data_value->value.to_int : data_value->value.to_float;
        if (val == ALARM_STATUS_TRIG)
            SET_AMASK_BIT(pro_chg_flag, 6);
        else
            CLR_AMASK_BIT(pro_chg_flag, 6);
    }
    else if(dev_get_dev_tag_int(DEV_NO_EMS, EN_BMS_ALARM))
    {
        int val = (data_value->type == TYPE_TAG_INT) ? data_value->value.to_int : data_value->value.to_float;
        if (strcmp(nod,DEV_NO_BMS"."CELL_OVER_VOL2) == 0)
        {
            if(val == ALARM_STATUS_TRIG )
            {
                SET_AMASK_BIT(pro_chg_flag,1);
            }
            else
            {
                CLR_AMASK_BIT(pro_chg_flag,1);
            }
        }
        if (strcmp(nod,DEV_NO_BMS"."CELL_OVER_VOL3) == 0)
        {
            if(val == ALARM_STATUS_TRIG )
            {
                SET_AMASK_BIT(pro_chg_flag,2);
            }
            else
            {
                CLR_AMASK_BIT(pro_chg_flag,2);
            }
        }
        if (strcmp(nod,DEV_NO_BMS"."RACK_OVER_VOL2) == 0)
        {
            if(val == ALARM_STATUS_TRIG )
            {
                SET_AMASK_BIT(pro_chg_flag,3);
            }
            else
            {
                CLR_AMASK_BIT(pro_chg_flag,3);
            }
        }
        if (strcmp(nod,DEV_NO_BMS"."RACK_OVER_VOL3) == 0)
        {
            if(val == ALARM_STATUS_TRIG )
            {
                SET_AMASK_BIT(pro_chg_flag,4);
            }
            else
            {
                CLR_AMASK_BIT(pro_chg_flag,4);
            }
        }
    }
    else //不使能清空
    {
        CLR_AMASK_BIT(pro_chg_flag,1);
        CLR_AMASK_BIT(pro_chg_flag,2);
        CLR_AMASK_BIT(pro_chg_flag,3);
        CLR_AMASK_BIT(pro_chg_flag,4);
    }

    if (pro_chg_flag>0)
    {
        if (dev_get_dev_tag_int(DEV_NO_EMS, PROHIBITE_CHARGE)!=1)
        {
            dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_CHARGE,  1);
            set_lc_power(0); 
        }
    }
    else
    {
        if (dev_get_dev_tag_int(DEV_NO_EMS, PROHIBITE_CHARGE)!=0)
        {
            dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_CHARGE,0);
        }
    }
}

static void fast_stop_discharge_tab_cb(char *nod,tag_value *data_value)
{
    static int pro_dischg_flag = 0;
    if (strcmp(nod,DEV_NO_BMS"."CURRENT_SOC) == 0)
    {
        if(g_usercfg_variant.EnProtectDOD)
        {
            double value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
            double socmin = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMIN);
            double socminerr = dev_get_dev_tag_float(DEV_NO_EMS, EMS_SOCMINERR);
            if (value < socmin)
            {
                SET_AMASK_BIT(pro_dischg_flag,0);
            }
            else if (value > (socmin + socminerr))
            {
                CLR_AMASK_BIT(pro_dischg_flag,0);
            }
        }
        else
        {
            CLR_AMASK_BIT(pro_dischg_flag,0);
        }
    } 
    else if (strcmp(nod,DEV_NO_BMS"."RACK_MIN_VOLT) == 0)
    {
        if(g_usercfg_variant.EnCellVoltProtect)
        {
            int val = data_value->value.to_int;
            int cur_vol_max = dev_get_dev_tag_int(DEV_NO_BMS, RACK_MAX_VOLT);
            int volmin = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MIN_VOLTAGE)*1000;
            int volminerr = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MIN_VOLTAGE_ERR)*1000;
            int volmax = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MAX_VOLTAGE)*1000;
            int volmaxerr = dev_get_dev_tag_float(DEV_NO_EMS, SINGLE_MAX_VOLTAGE_ERR)*1000;
            if (val < volmin)
            {
                SET_AMASK_BIT(pro_dischg_flag,5);
                dev_set_dev_tag_int(DEV_NO_EMS, SINGLE_CELL_VOL_THRESHOLD_ALARM, 1);
            }
            else if (val > (volmin + volminerr))
            {
                CLR_AMASK_BIT(pro_dischg_flag, 5);
                if ((cur_vol_max < (volmax - volmaxerr)))
                {
                    dev_set_dev_tag_int(DEV_NO_EMS, SINGLE_CELL_VOL_THRESHOLD_ALARM, 0);
                }
            }
        }
        else
        {
            CLR_AMASK_BIT(pro_dischg_flag,5);
        }
    }

    else if (strcmp(nod, DEV_NO_EMS "." USER_STOP_DISCHA1) == 0 || strcmp(nod, DEV_NO_EMS "." USER_STOP_DISCHA2) == 0 || strcmp(nod, DEV_NO_EMS "." USER_STOP_DISCHA3) == 0)
    {
        int val = (data_value->type == TYPE_TAG_INT) ? data_value->value.to_int : data_value->value.to_float;
        if (val == ALARM_STATUS_TRIG)
            SET_AMASK_BIT(pro_dischg_flag, 7);
        else
            CLR_AMASK_BIT(pro_dischg_flag, 7);
    }
    else if(dev_get_dev_tag_int(DEV_NO_EMS, EN_BMS_ALARM))
    {
        int val = (data_value->type == TYPE_TAG_INT) ? data_value->value.to_int : data_value->value.to_float;
        if (strcmp(nod,DEV_NO_BMS"."CELL_UNDER_VOL2) == 0)
        {
            if(val == ALARM_STATUS_TRIG )
            {
                SET_AMASK_BIT(pro_dischg_flag,1);
            }
            else
            {
                CLR_AMASK_BIT(pro_dischg_flag,1);
            }
        }
        if (strcmp(nod,DEV_NO_BMS"."CELL_UNDER_VOL3) == 0)
        {
            if(val == ALARM_STATUS_TRIG )
            {
                SET_AMASK_BIT(pro_dischg_flag,2);
            }
            else
            {
                CLR_AMASK_BIT(pro_dischg_flag,2);
            }
        }
        if (strcmp(nod,DEV_NO_BMS"."RACK_UNDER_VOL2) == 0)
        {
            if(val == ALARM_STATUS_TRIG )
            {
                SET_AMASK_BIT(pro_dischg_flag,3);
            }
            else
            {
                CLR_AMASK_BIT(pro_dischg_flag,3);
            }
        }
        if (strcmp(nod,DEV_NO_BMS"."RACK_UNDER_VOL3) == 0)
        {
            if(val == ALARM_STATUS_TRIG )
            {
                SET_AMASK_BIT(pro_dischg_flag,4);
            }
            else
            {
                CLR_AMASK_BIT(pro_dischg_flag,4);
            }
        }
    }
    else //不使能得清空
    {
        CLR_AMASK_BIT(pro_dischg_flag,1);
        CLR_AMASK_BIT(pro_dischg_flag,2);
        CLR_AMASK_BIT(pro_dischg_flag,3);
        CLR_AMASK_BIT(pro_dischg_flag,4);
    }

    if (pro_dischg_flag>0)
    {
        if (dev_get_dev_tag_int(DEV_NO_EMS, PROHIBITE_DISCHARGE)!=1)
        {
            dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_DISCHARGE,  1);
            set_lc_power(0); 
        }
    }
    else
    {
        if (dev_get_dev_tag_int(DEV_NO_EMS, PROHIBITE_DISCHARGE)!=0)
        {
            dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_DISCHARGE,0);
        }
    }
}

static void fast_stop_system_state_cb(char *nod, tag_value *data_value)
{
    int val = ALARM_STATUS_NO;
    static int status = 0;

    if (data_value->type == TYPE_TAG_INT)
        val = data_value->value.to_int;
    else
        val = data_value->value.to_float;

    int enableBmsAlarm = dev_get_dev_tag_int(DEV_NO_EMS, EN_BMS_ALARM);
    int fast_stop_system_state_tab_size = sizeof(fast_stop_system_state_tab) / sizeof(fast_stop_system_state_tab[0]);
    if (enableBmsAlarm && (strstr(nod, DEV_NO_BMS ".") == nod))
    {
        return;
    }

    for (size_t i = 0; i < fast_stop_system_state_tab_size; i++)
    {
        if (strcmp(nod, fast_stop_system_state_tab[i]) == 0)
        {
            if (val != 0)
            {
                SET_AMASK_BIT(status, i);
            }
            else
            {
                CLR_AMASK_BIT(status, i);
            }
        }
    }

    for (size_t i = 0; i < sizeof(fast_stop_system_state_tab_not) / sizeof(fast_stop_system_state_tab_not[0]); i++)
    {
        if (strcmp(nod, fast_stop_system_state_tab_not[i]) == 0)
        {
            if (val == 0)
            {
                SET_AMASK_BIT(status, (i + fast_stop_system_state_tab_size));
            }
            else
            {
                CLR_AMASK_BIT(status, (i + fast_stop_system_state_tab_size));
            }
        }
    }
    ems_syslog(LOG_WARNING, "fast stop SYSTEM_STATUS [nod:%s] -> [%d], status:%d",nod,val,status);
    dev_set_dev_tag_int(DEV_NO_EMS, SYSTEM_STATUS_W, status);
    if(status > 0 )
    {
        int sys_flag = dev_get_dev_tag_int(DEV_NO_EMS, SYSTEM_STATUS);
        dev_set_dev_tag_int(DEV_NO_EMS, SYSTEM_STATUS,  1);
        if (0 == sys_flag)
            set_lc_power(0);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS, SYSTEM_STATUS, 0);
    }
}

static void fast_stop_PCS_power_limit_cb(char *nod,tag_value *data_value)
{
//     if (cabinet_info.ctrl_mode == EMS_MODE_ZC) // 如果是主从模式不使用PCS功率异常
//     {
//         dev_set_dev_tag_int(DEV_NO_EMS, PCS_POWER_ERR,  0);
//         return;
//     }
//     //float val = data_value->value.to_float;
//     float val = dev_get_dev_tag_float(DEV_NO_PCS, ACTIVE_POWER);
//     float expectActPower = dev_get_dev_tag_float(DEV_NO_EMS, EXPECT_ACT_POWER);
//     float SOC = dev_get_dev_tag_float(DEV_NO_BMS, "SOC");
// 
//     //SOC到一定值，BMS限制导致的功率不对，不判断
//     int flag = ( (SOC >= 90.0) || ( SOC <= 10.0)) ? 0 : 1; 
//     if ( ((fabs(expectActPower) >= 1.0f) && (fabs((fabs(expectActPower) - fabs(val))) > fabs(expectActPower)*0.2)) && flag )
//     {
//         dev_set_dev_tag_int(DEV_NO_EMS, PCS_POWER_ERR,  1);
//     }
//     else
//     {
//         dev_set_dev_tag_int(DEV_NO_EMS, PCS_POWER_ERR,  0);
//     }
}

static void fast_stop_cell_voltage_unusual_cb(char *nod, tag_value *data_value)
{
    if(!g_usercfg_variant.EnCellVoltProtect) return;
    int val = data_value->value.to_int;
    
    if (!dev_get_dev_tag_int(DEV_NO_EMS, PROHIBITE_DISCHARGE))
    {
        if(val < g_usercfg_variant.SingleMinVoltage * 1000)
        {
            dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_DISCHARGE,  1);
        }
    }
    if(!dev_get_dev_tag_int(DEV_NO_EMS, PROHIBITE_CHARGE))
    {
        if(val > g_usercfg_variant.SingleMaxVoltage * 1000)
        {
            dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_CHARGE,  1);
        }
    }
}

static void fast_stop_dc_voltage_unusual_cb(char *nod,tag_value *data_value)
{

    float val = data_value->value.to_float;
    if (val<DC_TOTAL_VOL_LIMIT)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, DC_TOTAL_VOL_LOW,  1);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS, DC_TOTAL_VOL_LOW,  0);
    }
    
}

int add_ems_cab_variant(int cur_rack_sum)
{
    char variant[NODE_SIZE] = {0};
    char name[NODE_SIZE] = {0};
    ems_base_variant_t tmp_variant = {};
    for (int i = 1; i <= cur_rack_sum; i++)
    {
        for (int j = 0; j < sizeof(cab_variant) / sizeof(cab_variant[0]); j++)
        {
            tmp_variant = cab_variant[j];
            //
            snprintf(variant, sizeof(variant), cab_variant[j].variant, i);
            snprintf(name, sizeof(name), cab_variant[j].name, i);
            tmp_variant.variant = strdup(variant); // 没释放，为了保持点位
            tmp_variant.name = strdup(name);
            tmp_variant.addr = tmp_variant.addr + 0x100 * i;
            ems_base_variant_sing_add(&tmp_variant);
        }
    }

    return 0;
}

void *context = NULL;
int add_ems_variant()
{
    ems_base_variant_multi_add(ems_base_variant,ARRAY_SIZE(ems_base_variant));
    ems_base_variant_t ems_var = {.dev = NULL, .flag = O_WRONLY | O_RDONLY, .data_type = TYPE_TAG_INT, .point_type = PROPERTY_REALDATA, .history_type = "both", .fun_code = 0x03, .addr = 0x200,  .type = TYPE_INT, .order = ORDER_AB, .coefficient = 1};
    for(int i = 0;i< NUM_TOU_SEG_MAX;i++)//最大6段
    {
        ems_var.dev = strdup(DEV_NO_EMS);
        
        ems_syslog(LOG_NOTICE, "ems_var.dev:%s",ems_var.dev);

        char *variant = NULL;
        char *name = NULL;
        char variant_a[NODE_SIZE]={0};
        char name_a[NODE_SIZE]={0};

        //TODO 数据类型
        snprintf(variant_a,sizeof(variant_a),"S%dStHour",i+1);
        ems_syslog(LOG_NOTICE, "variant_a:%s len:%lu",variant_a,(long unsigned int)strlen(variant_a));
        snprintf(name_a, sizeof(name_a), _("Starting hour of the %d strategy"), i + 1);

        variant = strdup(variant_a);
        name = strdup(name_a);

        ems_var.name = name;
        ems_var.variant = variant;
        ems_base_variant_sing_add(&ems_var);
        ems_var.addr++;

        snprintf(variant_a,sizeof(variant_a),"S%dStMin",i+1);
        snprintf(name_a, sizeof(name_a), _("Starting minute of the %d strategy"), i + 1);

        variant = strdup(variant_a);
        name = strdup(name_a);

        ems_var.name = name;
        ems_var.variant = variant;
        ems_base_variant_sing_add(&ems_var);
        ems_var.addr++;

        snprintf(variant_a,sizeof(variant_a),"S%dEnHour",i+1);
        snprintf(name_a, sizeof(name_a), _("Ending hour of the %d strategy"), i + 1);

        variant = strdup(variant_a);
        name = strdup(name_a);

        ems_var.name = name;
        ems_var.variant = variant;
        ems_base_variant_sing_add(&ems_var);
        ems_var.addr++;
        
        snprintf(variant_a,sizeof(variant_a),"S%dEnMin",i+1);
        snprintf(name_a, sizeof(name_a), _("Ending minute of the %d strategy"), i + 1);

        variant = strdup(variant_a);
        name = strdup(name_a);


        ems_var.name = name;
        ems_var.variant = variant;
        ems_base_variant_sing_add(&ems_var);
        ems_var.addr++;

        snprintf(variant_a,sizeof(variant_a),"S%dPower",i+1);
        snprintf(name_a, sizeof(name_a), _("Executed power of the %d strategy(-charging +discharging)"), i + 1);

        variant = strdup(variant_a);
        name = strdup(name_a);

        ems_var.name = name;
        ems_var.variant = variant;
        ems_base_variant_sing_add(&ems_var);
        ems_var.addr++;

        // snprintf(variant_a,sizeof(variant_a),"S%dMode",i+1);
        // snprintf(name_a,sizeof(name_a),"第%d段策略模式",i+1);

        // variant = strdup(variant_a);
        // name = strdup(name_a);
        
        // ems_var.name = name;
        // ems_var.variant = variant;
        // ems_base_variant_sing_add(&ems_var);   
    }
    if (get_pcs_ctrl_var()->enable_group_ctrl)
    {
        add_ems_cab_variant(get_pcs_ctrl_var()->cabinet_cnt);
    }
    if (get_cabinet_info()->mode_microgrid != 0){
        ems_base_variant_multi_add(ems_microgri_variant, ARRAY_SIZE(ems_microgri_variant));
    }
    return 0;
}


int device_variant_mirrors(void)
{

    // write_file_data(VAR_MIRROR_FILE, string, strlen(string));   
    return 0;
}
int device_layer_selftest(void *p)
{
    //TODO检查相关点位是否存在
    return 0;
}



int device_layer_init(void *parm)
{
    int ret = 0;
    if (parm == NULL)
    {
        return -1;
    }
    MODBUS_SERVER_PARAM *var = ((proto_forward_t *)parm)->modbus_server_param;
    ret = add_ems_variant();
    char *proto_arr[] = {"ems", NULL};
    template_info_t info = {.protocol = proto_arr, .vendor ="Lnxall", .model = DEV_NO_EMS, .version = "", .type = "EMS"};
    ems_base_variant_t **base_var_p =NULL;
    int size = 0;
    ret = ems_base_variant_get(&base_var_p,&size); 
    if(ret == 0)
    {
        if(size == 0)
        {
            return -1;
        }
        bool  created = false;
        char *ems_dev_json = NULL;
        char *temp_json = NULL;
        char *ems_temp_json_save= NULL;
        char *ems_dev_json_save =NULL;

        var->modbus_ser_base_data->config_str_to_ems = create_md_server_tag_addr(base_var_p, size);
        if (var->modbus_ser_base_data->config_str_to_ems != NULL)
        {
            write_file_data(MODBUS_SERVER_TAG_ADDR_MAP_FILE, var->modbus_ser_base_data->config_str_to_ems, strlen(var->modbus_ser_base_data->config_str_to_ems));
        }
        else
        {
            ems_syslog(LOG_ERR, "create modbus server tag addr failed");
        }
        char *p_tmp = create_modbus_server_template(base_var_p, size);
        if (p_tmp != NULL)
        {
            write_file_data(MODBUS_SERVER_TAG_TEMPLATE_FILE, p_tmp, strlen(p_tmp));
            free(p_tmp);
        }
        else
        {
            ems_syslog(LOG_ERR, "create modbus server tag template failed");
        }
        
        ems_dev_json_save = read_file_data(CONFIG_PATH"/"EMS_DEVICE_MASTER_FILE);
        if(!ems_dev_json_save)
        {
            ems_dev_json = create_ems_device(EMS_TEMP_MASTER_FILE);
            if (ems_dev_json)
            {
                created = true;
                write_file_data(CONFIG_PATH"/"EMS_DEVICE_MASTER_FILE, ems_dev_json, strlen(ems_dev_json));
            }
            else
            {
                ems_syslog(LOG_ERR, "create ems device error!!!!");
            }
        }
        if(!created)
        {
            
            if(!ems_dev_json)
            {
                ems_dev_json = create_ems_device(EMS_TEMP_MASTER_FILE);
            }
            if(strcmp(ems_dev_json,ems_dev_json_save) != 0)
            {
                write_file_data(CONFIG_PATH"/"EMS_DEVICE_MASTER_FILE, ems_dev_json, strlen(ems_dev_json));
            } 

        }

        created = false;
        ems_temp_json_save = read_file_data(TEMPLATE_DIR"/"EMS_TEMP_MASTER_FILE);
        if(!ems_temp_json_save)
        {
            temp_json = create_ems_template(&info,base_var_p,size);
            if (temp_json)
            {
                created = true;
                write_file_data(TEMPLATE_DIR"/"EMS_TEMP_MASTER_FILE, temp_json, strlen(temp_json));
                free(temp_json);
                temp_json = NULL;
            }
        }
        if(!created)
        {   
            if(!temp_json)temp_json = create_ems_template(&info,base_var_p,size);
            if(strcmp(temp_json,ems_temp_json_save) != 0)
            {
                write_file_data(TEMPLATE_DIR"/"EMS_TEMP_MASTER_FILE, temp_json, strlen(temp_json));
            }        
        }

        if(temp_json!=NULL)
        {
            free(temp_json);
            temp_json = NULL;
        }
        if(ems_temp_json_save!=NULL)
        {
            free(ems_temp_json_save);
            ems_temp_json_save = NULL;
        }

        if(ems_dev_json!=NULL)
        {
            free(ems_dev_json);
            ems_dev_json = NULL;
        }
        if(ems_dev_json_save!=NULL)
        {
            free(ems_dev_json_save);
            ems_dev_json_save = NULL;
        }
        //ems_base_variant_free();内部释放不完全，加上只会调用一次所以直接不释放
    }
    return ret;
}

#define POWER_OFF_LOCK_TIME 5
static void fast_stop_fire_cb(char *nod,tag_value *data_value)
{
    if (dev_get_dev_tag_int(DEV_NO_EMS, EMS_POWER) == 1 && time(NULL) - get_pcs_ctrl_var()->power_down_time < POWER_OFF_LOCK_TIME)
        return;
    
    int val = ALARM_STATUS_NO;
    static int hold_status = 0;
    static int status = 0;

    if(data_value->type == TYPE_TAG_INT)
        val = data_value->value.to_int;
    else 
        val = data_value->value.to_float;
    


    if (strcmp(nod,DEV_NO_EMS"."EXTERN_FIRE)==0)
    {
        if(val == ALARM_STATUS_TRIG)
        {
            SET_AMASK_BIT(status,0);
        }
        else
        {
            CLR_AMASK_BIT(status,0);
        }
    }
    else if (strcmp(nod,DEV_NO_EMS"."FIRE)==0)
    {
        if (dev_get_dev_tag_int(DEV_NO_EMS, EN_IO_PROTECT) == 0)
        {
            return;
        }
        if(val == ALARM_STATUS_TRIG)
        {
            SET_AMASK_BIT(status,1);
        }
        else
        {
            CLR_AMASK_BIT(status,1);
        }
    }
    ems_syslog(LOG_WARNING, "fast stop fast_stop_fire [nod:%s] -> [%d]",nod,val);
    if(status != hold_status)
    {
        hold_status = status;
        if (hold_status > 0)
        {
            dev_set_dev_tag_int(DEV_NO_EMS, ALL_FIRE, 1);
            do_trip(); //触发脱扣
                    // 是否切换为手动模式
            dev_set_dev_tag_int(DEV_NO_EMS, CONTROL_MODE, 0);
            dev_set_dev_tag_float(DEV_NO_EMS, SET_ACT_POWER, 0);
        }
        else
        {
            dev_set_dev_tag_int(DEV_NO_EMS, ALL_FIRE,  0);
        }
    }
}

static void fast_stop_power_cb(char* nod, tag_value* data_value)
{
    int val = 0;

    if (data_value->type == TYPE_TAG_INT)
        val = data_value->value.to_int;
    else
        val = data_value->value.to_float;


    if (val == 0)
    {
        ems_syslog(LOG_DEBUG, "ems power change : %d", val);
        get_pcs_ctrl_var()->power_down_time = 0;
    }
    else
    {
        ems_syslog(LOG_ERR, "ems power change : %d", val);
        get_pcs_ctrl_var()->power_down_time = time(NULL);
    }

}

void lc_fire_cb(int fireNum) //局域网内触发消防的数量不包含自己
{
    if (fireNum>0)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, EXTERN_FIRE,1);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS, EXTERN_FIRE,0);
    }
}

/*需要管理同步参数仅需在此处增减tag*/
const char* lc_server_sync_tag[] = {
    DEV_NO_EMS "." ON_OFF_STATE,
    DEV_NO_EMS "." SET_CONNECT,
    DEV_NO_EMS "." BMS_UPPER,
    DEV_NO_EMS "." EN_PROTECT_DOD,
    DEV_NO_EMS "." EN_BMS_ALARM,
    DEV_NO_EMS "." BMS_POWER_LIMIT,
    DEV_NO_EMS "." EMS_SOCMAX,
    DEV_NO_EMS "." EMS_SOCMIN,
    DEV_NO_EMS "." PCS_AUTO_TURN,
    DEV_NO_EMS "." PCS_AUTO_TURN_OFF_TIME,
    DEV_NO_EMS "." WORK_START_HOT_TEMP,
    DEV_NO_EMS "." WORK_END_HOT_TEMP,
    DEV_NO_EMS "." WORK_START_COOL_TEMP,
    DEV_NO_EMS "." WORK_END_COOL_TEMP,
    DEV_NO_EMS "." WORK_DIST_HOT_TEMP,
    DEV_NO_EMS "." WORK_DIST_COOL_TEMP,
    DEV_NO_EMS "." TMS_CTRL_DIST_TEMP_COLD,
    DEV_NO_EMS "." TMS_CTRL_DIST_TEMP_HOT,
    DEV_NO_EMS "." AIR_COND_STARTSTOP_CTRL,
    DEV_NO_EMS "." EN_SELF_LOOP_DIFF,
    DEV_NO_EMS "." SELF_LOOP_DIFF,
    DEV_NO_EMS "." ENABLE_AIR_COND,
    DEV_NO_EMS "." ENABLE_AUTO_COOL,
    DEV_NO_EMS "." EN_CELL_VOLT_PROTECT,
    DEV_NO_EMS "." EN_DRAUG_FLOW_POWER,
    DEV_NO_EMS "." EN_DRAUG_FLOW_POWER_T,
    DEV_NO_EMS "." EN_DRAUG_FLOW_TEM,
    DEV_NO_EMS "." EN_DRAUG_FLOW_TEM_T,
    DEV_NO_EMS "." DRAUG_DEAE_TIME,
    DEV_NO_EMS "." SINGLE_MIN_VOLTAGE,
    DEV_NO_EMS "." SINGLE_MAX_VOLTAGE,
    DEV_NO_EMS "." SINGLE_MIN_VOLTAGE_ERR,
    DEV_NO_EMS "." SINGLE_MAX_VOLTAGE_ERR,
    DEV_NO_EMS "." EMS_SOCMAXERR,
    DEV_NO_EMS "." EMS_SOCMINERR,
    DEV_NO_EMS "." EN_RP_COMP,
    DEV_NO_EMS "." SET_POWER_FACTOR,
};

struct lc_server_sync_info{
    const char *tag;        // tag 来自于 lc_server_sync_tag
    int type;               // 类型: int:0 float:1 没找到该节点:-1
    union  {
        int to_int;
        double to_float;
    } data;                 // 存储数据
};

struct lc_server_sync_info *lc_server_sync_data_buf = NULL;      // 非外部初始化且规避内存泄漏

void lc_server_sync_data_init()
{

    int size = sizeof(lc_server_sync_tag) / sizeof(lc_server_sync_tag[0]);
    static struct lc_server_sync_info LC_data[sizeof(lc_server_sync_tag) / sizeof(lc_server_sync_tag[0])];
    for (size_t i = 0; i < size; i++)
    {
	const char *nod = NULL;
       	char *dot = NULL;

        double tmp_float = 0;
        int tmp_int = 0;
        nod = lc_server_sync_tag[i];
        dot = strchr(nod, '.');
        if (NULL == dot)
            continue;
        dot++;
        LC_data[i].tag = (const char *)dot;

        if (0 == collector_read_one_int_data(nod, &tmp_int)) // int查找，失败了转float 再失败则判定无此值
        {
            LC_data[i].type = 0;
            LC_data[i].data.to_int = tmp_int;
        }
        else if (0 == collector_read_one_float_data(nod, &tmp_float))
        {
            LC_data[i].type = 1;
            LC_data[i].data.to_float = tmp_float;
        }
        else
        {
            LC_data[i].type = -1;
            ems_syslog(LOG_ERR, "Error in writing data, tag = %s:failed to register", nod); // 没找到
        }
    }
    lc_server_sync_data_buf = LC_data;
}
void parameterSyncFlush()
{
    pcs_ctrl_var_t *var = get_pcs_ctrl_var();
    int lc_cnt = var->lc_list_len;
    int lc_sync_cnt = sizeof(lc_server_sync_tag) / sizeof(lc_server_sync_tag[0]);
    lc_it *lc_list_tmp = var->lc_list;

    char string_no[NODE_SIZE] = "";
    microgrid_cfg_t *microgrid_cfg = get_microgrid_cfg_var();
    for (int i = 0; i < lc_cnt; i++)
    {
        for(int j = 0; j < lc_sync_cnt; j++)
        {
            snprintf(string_no, NODE_SIZE, "%s.%s", lc_list_tmp[i].no, lc_server_sync_data_buf[j].tag);

            if (microgrid_cfg->mode_microgrid != 0 && microgrid_cfg->en_sts == 1){  // 若微网模式+使能STS-->不发并离网操作
                if (strstr(string_no, "SetConnect")){
                    continue;
                }
            } 
            if (0 == lc_server_sync_data_buf[j].type)
            {
                collector_write_one_int_data(string_no, lc_server_sync_data_buf[j].data.to_int);
            }
            else if (1 == lc_server_sync_data_buf[j].type)
            {
                collector_write_one_float_data(string_no, lc_server_sync_data_buf[j].data.to_float);
            }
            else 
            {
                ems_syslog(LOG_ERR, "Error in writing data, tag = %s:failed to register", lc_server_sync_data_buf[j].tag);
            }
        }
    }
}
void *parameterSynchronizationThread(void *arg)
{

    while(1)
    {
        parameterSyncFlush();
        sleep(60);
    }
}
void parameterSynchronizationIni()
{

    if (NULL == lc_server_sync_data_buf)
    {
        lc_server_sync_data_init();
    }
    pthread_t threads;
    pthread_create(&threads, NULL, parameterSynchronizationThread, NULL);
    // XCl TODO

    // pthread_join(threads, NULL);
}

// 数据刷新缓冲区
void clServiceDataChange(char *nod, tag_value *data_value)
{
    int lc_sync_cnt = sizeof(lc_server_sync_tag) / sizeof(lc_server_sync_tag[0]);
    char *dot;

    // 遍历字符串数组
    for(int i = 0; i < lc_sync_cnt; i++)
    {
        dot = strchr(nod, '.');
        if (dot != NULL)
        {
            dot++;
            if (strcmp(dot, lc_server_sync_data_buf[i].tag) == 0)
            {
                if (data_value->type == 0)
                {
                    lc_server_sync_data_buf[i].type = 0;
                    lc_server_sync_data_buf[i].data.to_int = data_value->value.to_int;
                }
                else if (data_value->type == 1)
                {
                    lc_server_sync_data_buf[i].type = 1;
                    lc_server_sync_data_buf[i].data.to_float = data_value->value.to_float;
                }
                else
                {
                    ems_syslog(LOG_ERR, "Error in writing data, tag = %s:failed to register", lc_server_sync_data_buf[i].tag);
                }

                break;
            }
        }
    }
}

static void lcServiceDataSync(char *nod, tag_value *data_value)
{

    if (NULL == lc_server_sync_data_buf)
    {
        lc_server_sync_data_init();
    }
    clServiceDataChange(nod, data_value);
    // 缓冲区刷新LC数据
    pcs_ctrl_var_t *var = get_pcs_ctrl_var();
    int lc_cnt = var->lc_list_len;
    lc_it *lc_list_tmp = var->lc_list;
    char *tag_start = strchr(nod, '.');
    if (tag_start == NULL)
    {
        ems_syslog(LOG_ERR, "tag[%s] find char '.' is NULL", nod);
    }
    else
    {
        tag_start++;
        if (0 == data_value->type)
        {
            for (int i = 0; i < lc_cnt; i++)
            {
                dev_set_dev_tag_int(lc_list_tmp[i].no, tag_start, data_value->value.to_int);
            }
        }
        else if (1 == data_value->type)
        {
            for (int i = 0; i < lc_cnt; i++)
            {
                dev_set_dev_tag_float(lc_list_tmp[i].no, tag_start, data_value->value.to_float);
            }
        }
        else
        {
            ems_syslog(LOG_ERR, "tag[%s] type is error", nod);
        }
    }
}

int ems_config_point_register(pcs_ctrl_var_t *var,const ems_cfg_info_t *node, size_t size,
                                  void* tag_get_data_cb_t)
{
    int ret = -1;
    for (size_t i = 0; i < size; i++)
    {
        ret = collector_variant_monitor_add_one(var,node[i].nod,tag_get_data_cb_t);
        if(ret == -1) break;
    }

    for(int i = 0;i<NUM_TOU_SEG_MAX;i++)//计划参数的回调默认注册  最大6段
    {       
        char name[64];

        snprintf(name,sizeof(name),"%s.S%dStHour",DEV_NO_EMS,i+1);
        ret = collector_variant_monitor_add_one(var,name,tag_get_data_cb_t);
        if(ret == -1) break;
        snprintf(name,sizeof(name),"%s.S%dStMin",DEV_NO_EMS,i+1);
        ret = collector_variant_monitor_add_one(var,name,tag_get_data_cb_t);
        if(ret == -1) break;
        snprintf(name,sizeof(name),"%s.S%dEnHour",DEV_NO_EMS,i+1);
        ret = collector_variant_monitor_add_one(var,name,tag_get_data_cb_t);
        if(ret == -1) break;
        snprintf(name,sizeof(name),"%s.S%dEnMin",DEV_NO_EMS,i+1);
        ret = collector_variant_monitor_add_one(var,name,tag_get_data_cb_t);
        if(ret == -1) break;
        snprintf(name,sizeof(name),"%s.S%dPower",DEV_NO_EMS,i+1);
        ret = collector_variant_monitor_add_one(var,name,tag_get_data_cb_t);
        if(ret == -1) break;      
    }
    return  ret;
}
static void reset_reflux_state_times_init(void);
int variant_cb_register(pcs_ctrl_var_t *var)
{
//********************回调函数不要做耗时操作(一定不要做不确定的阻塞操作)
//到内部点位中注册点位
    int ret = 0;
    ret = ems_config_point_register(var,ems_config_tab,ems_config_tab_count_g,ems_config_data_change_cb);
    if(ret != 0)
    ems_syslog(LOG_ERR, "load ems_config_tab table error");
    if (cabinet_info.ctrl_mode == EMS_MODE_ZC)
    {
        ret = collector_variant_monitor_add(var,ems_contrl_tab, ARRAY_SIZE(ems_contrl_tab),ems_contrl_data_change_cb_lc_ctr_mode);
        if(ret != 0)
            ems_syslog(LOG_ERR, "load ems_contrl_tab table error");
    }
    else
    {
        ret = collector_variant_monitor_add(var, ems_contrl_tab, ARRAY_SIZE(ems_contrl_tab), ems_contrl_data_change_cb);
        if (ret != 0)
            ems_syslog(LOG_ERR, "load ems_contrl_tab table error");
    }

//TODO 不用到UA注册的
    // ret = collector_variant_monitor_add(var,check_rack_max_temp_tab, ARRAY_SIZE(check_rack_max_temp_tab),check_rack_max_temp_cb);
    // if(ret != 0)
    //     ems_syslog(LOG_ERR, "load check_rack_max_temp_tan table error");
    ret = collector_variant_monitor_add(var,fast_stop_dod_protect_tab, ARRAY_SIZE(fast_stop_dod_protect_tab),fast_stop_dod_protect_cb);
    if(ret != 0)
        ems_syslog(LOG_ERR, "load fast_stop_dod_protect_tab table error");
    ret = collector_variant_monitor_add(var,fast_stop_charge_tab, ARRAY_SIZE(fast_stop_charge_tab),fast_stop_charge_tab_cb);
    if(ret != 0)
        ems_syslog(LOG_ERR, "load fast_stop_charge_tab table error");
    ret = collector_variant_monitor_add(var,fast_stop_discharge_tab, ARRAY_SIZE(fast_stop_discharge_tab),fast_stop_discharge_tab_cb);
    if(ret != 0)
        ems_syslog(LOG_ERR, "load fast_stop_discharge_tab table error"); 

    ret = collector_variant_monitor_add(var,fast_stop_system_state_tab, ARRAY_SIZE(fast_stop_system_state_tab),fast_stop_system_state_cb);
    if(ret != 0)
        ems_syslog(LOG_ERR, "load ffast_stop_system_state_tab error");

    ret = collector_variant_monitor_add(var,fast_stop_system_state_tab_not, ARRAY_SIZE(fast_stop_system_state_tab_not),fast_stop_system_state_cb);
    if(ret != 0)
        ems_syslog(LOG_ERR, "load fast_stop_system_state_tab_not error");


    ret = collector_variant_monitor_add(var,fast_stop_temp_ctrl_tab, ARRAY_SIZE(fast_stop_temp_ctrl_tab),fast_stop_temp_ctrl_cb);
    if(ret != 0)
        ems_syslog(LOG_ERR, "load fast_stop_temp_ctrl_tab table error");
    ret = collector_variant_monitor_add(var,fast_stop_dc_voltage_unusual_tab, ARRAY_SIZE(fast_stop_dc_voltage_unusual_tab),fast_stop_dc_voltage_unusual_cb);
    if(ret != 0)
        ems_syslog(LOG_ERR, "load fast_stop_dc_voltage_unusual_tab table error");
    
    ret = collector_variant_monitor_add(var, lc_server_sync_tag, ARRAY_SIZE(lc_server_sync_tag), lcServiceDataSync);
    if (ret != 0)
        ems_syslog(LOG_ERR, "load lc_server_sync_tag table error");





    //消防回调
#if 1 /* removed for CC/LC */
    register_fire_cb(lc_fire_cb);  //通过广播获取消防其他设备消防信息
#endif
    ret = collector_variant_monitor_add(var,fast_stop_fire_tab, ARRAY_SIZE(fast_stop_fire_tab),fast_stop_fire_cb);
    if(ret != 0)
        ems_syslog(LOG_ERR, "load fast_fire_tab table error");

    ret = collector_variant_monitor_add(var, fast_stop_power_tab, ARRAY_SIZE(fast_stop_power_tab), fast_stop_power_cb);
    if (ret != 0)
        ems_syslog(LOG_ERR, "load fast_power_tab table error");

    ret = collector_variant_monitor_add(var,fast_stop_PCS_power_limit_tab, ARRAY_SIZE(fast_stop_PCS_power_limit_tab),fast_stop_PCS_power_limit_cb);
    if(ret != 0)
        ems_syslog(LOG_ERR, "load dev_online_state_change table error");  

    // ret = collector_variant_monitor_add(var, fast_stop_cell_voltage_unusual_tab, ARRAY_SIZE(fast_stop_cell_voltage_unusual_tab), fast_stop_cell_voltage_unusual_cb);
    // if(ret != 0)
    //     ems_syslog(LOG_ERR, "load fast_stop_cell_voltage_unusual table error");
    tag_trigger_init(var);  
    automatic_init(CONFIG_PATH"/"AUTOMATIC_CONFIG);
    //check_pack_temp(var);
    reset_reflux_state_times_init();
    return ret;
}

void require_cabinet_count(pcs_ctrl_var_t *var)
{
    if (var->enable_group_ctrl)
    {
        int cnt = get_same_type_devs_cnt(DEV_NO_PCS);
        if(cnt <=0 )
        {
            var->cabinet_cnt = 1; //缺省1组
            ems_syslog(LOG_ERR, "System must be included PCS device");
        }
        else {
            var->cabinet_cnt = cnt; 
            ems_syslog(LOG_NOTICE, "Find %d PCS in device list",var->cabinet_cnt);
        }
        cnt = get_same_type_devs_cnt(DEV_NO_PV);
        if(cnt <=0 )
        {
            var->pv_pcs_num = 1; //缺省1组
            ems_syslog(LOG_ERR, "System must be included PV device");
        }
        else {
            var->pv_pcs_num = cnt; 
            ems_syslog(LOG_NOTICE, "Find %d PV in device list",var->pv_pcs_num);
        }
    }
    else
    {
        var->cabinet_cnt = 1;//不是分组控制的时候使用1
    }
}

#if 1 // 多柜控制回调
const char *fast_stop_dod_protect_tab_mul[] =
    {
        DEV_NO_BMS "%d." CURRENT_SOC,
};

// 处理欠压故障
const char *fast_stop_discharge_tab_mul[] =
    {
        DEV_NO_BMS "%d." CELL_UNDER_VOL2,
        DEV_NO_BMS "%d." CELL_UNDER_VOL3,
        DEV_NO_BMS "%d." RACK_UNDER_VOL2,
        DEV_NO_BMS "%d." RACK_UNDER_VOL3,
        DEV_NO_BMS "%d." CURRENT_SOC,

};

// 处理过压故障
const char *fast_stop_charge_tab_mul[] =
    {
        DEV_NO_BMS "%d." CELL_OVER_VOL2,
        DEV_NO_BMS "%d." CELL_OVER_VOL3,
        DEV_NO_BMS "%d." RACK_OVER_VOL2,
        DEV_NO_BMS "%d." RACK_OVER_VOL3,
        DEV_NO_BMS "%d." CURRENT_SOC,
};

// 系统状态
const char *fast_stop_system_state_tab_mul[] =
    {
        DEV_NO_EMS "." IO_PROTECT,
        DEV_NO_EMS "." METER_ERROR,
        DEV_NO_EMS "." USER_STOP1,
        DEV_NO_EMS "." USER_STOP2,
        DEV_NO_EMS "." USER_STOP3,
        DEV_NO_EMS "." TEMP_CTRL_ERR, // 温控系统异常
        DEV_NO_EMS "." ALL_FIRE,      // 消防系统触发
        DEV_NO_EMS "." REFLUX_STATE
};

const char *fast_stop_system_state_tab_mul2[] =
    {
        DEV_NO_EMS "." CAB_SYSTEM_STATUS "1",
        DEV_NO_EMS "." CAB_SYSTEM_STATUS "2",
        DEV_NO_EMS "." CAB_SYSTEM_STATUS "3",
        DEV_NO_EMS "." CAB_SYSTEM_STATUS "4",
        DEV_NO_EMS "." CAB_SYSTEM_STATUS "5",
};

const char *fast_stop_cab_state_tab_mul[] =
    {
        DEV_NO_BMS "%d." STATE_ONLINE,
        DEV_NO_BMS "%d." CELL_OVER_VOL3,
        DEV_NO_BMS "%d." RACK_OVER_VOL3,
        DEV_NO_BMS "%d." CELL_UNDER_VOL3,
        DEV_NO_BMS "%d." RACK_UNDER_VOL3,
        DEV_NO_PCS "%d." PCS_FAULT,
        DEV_NO_PCS "%d." STATE_ONLINE,
};

const char *fast_stop_cab_state_tab2_mul[] =
    {
        DEV_NO_EMS "."CAB_DC_TOTAL_VOL_LOW"%d",
};

// 温控系统异常
const char *fast_stop_temp_ctrl_tab_mul[2] =
    {
        DEV_NO_AIRCOND "%d." STATE_ONLINE,
        DEV_NO_AIRCOND "%d." DEV_FAULT,
};

// PCS功率反馈异常
const char *fast_stop_PCS_power_limit_tab_mul[] =
    {
        DEV_NO_PCS "%d." ACTIVE_POWER,
};

// 直流总压异常异常
const char *fast_stop_dc_voltage_unusual_tab_mul[] =
    {
        DEV_NO_BMS "%d." RACL_VOLTAGE,
};

const char *fast_stop_cell_voltage_unusual_tab_mul[] =
    {
        DEV_NO_BMS "%d." RACK_MAX_VOLT,
        DEV_NO_BMS "%d." RACK_MIN_VOLT,
};
static void fast_stop_temp_ctrl_mul_cb(char *nod, tag_value *data_value)
{
    int val = ALARM_STATUS_NO;
    static int status[MAX_RACK_SUM_SUPPORT] = {0};
    static int status_all = 0;
    char tag_name[TAG_NAME_LEN] = {0};

    if (data_value->type == TYPE_TAG_INT)
        val = data_value->value.to_int;
    else
        val = data_value->value.to_float;

    int who = get_nod_index(nod, DEV_NO_AIRCOND);
    if (who >= 0) // 空调
    {
        if (strstr(nod, STATE_ONLINE) != NULL)
        {
            if (val == 0)
            {
                SET_AMASK_BIT(status[who], 0);
            }
            else
            {
                CLR_AMASK_BIT(status[who], 0);
            }
        }
        else if (strstr(nod, DEV_FAULT) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(status[who], 1);
            }
            else
            {
                CLR_AMASK_BIT(status[who], 1);
            }
        }

        goto set_tag;
    }

    who = get_nod_index(nod, DEV_NO_LIQUID);
    if (who >= 0) // 液冷
    {
        if (strstr(nod, STATE_ONLINE) != NULL)
        {
            if (val == 0)
            {
                SET_AMASK_BIT(status[who], 2);
            }
            else
            {
                CLR_AMASK_BIT(status[who], 2);
            }
        }
        else if (strstr(nod, DEV_FAULT) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(status[who], 3);
            }
            else
            {
                CLR_AMASK_BIT(status[who], 3);
            }
        }
        goto set_tag;
    }

    ems_syslog(LOG_ERR, "the tag %s config error", nod);
    return;

set_tag:

    snprintf(tag_name, sizeof(tag_name), TEMP_CTRL_ERR "%d", who + 1);
    if (status[who] > 0)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 1);
        SET_AMASK_BIT(status_all, who);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 0);
        CLR_AMASK_BIT(status_all, who);
    }
    dev_set_dev_tag_int(DEV_NO_EMS, TEMP_CTRL_ERR, status_all);
    //
    ems_syslog(LOG_WARNING, "fast stop temp_ctrl [nod:%s] -> [%d]", nod, status_all);
    ems_syslog(LOG_WARNING, "fast stop temp_ctrl [nod:%s] -> [%d]", nod, val);
}

// static void fast_stop_dod_protect_mul_cb(char *nod, tag_value *data_value)
// {
//     static int status[MAX_RACK_SUM_SUPPORT] = {0};
//     static int all_status = 0;
//     char tag_name[TAG_NAME_LEN] = {0};
//     int who = get_nod_index(nod, DEV_NO_BMS);
//     if (who < 0)
//     {
//         ems_syslog(LOG_ERR, "the tag %s config error", nod);
//         return;
//     }
//     int en_dod = dev_get_dev_tag_int(DEV_NO_EMS, EN_PROTECT_DOD);
//     snprintf(tag_name, sizeof(tag_name), CAB_DOD_LIMITED "%d", who + 1);

//     if (en_dod)
//     {
//         int value = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
//         if ((value >= dev_get_dev_tag_int(DEV_NO_EMS, EMS_SOCMAX)) || (value <= dev_get_dev_tag_int(DEV_NO_EMS, EMS_SOCMIN)))
//         {
//             status[who] = 1;
//         }
//         else
//         {
//             status[who] = 0;
//         }
//         dev_set_dev_tag_int(DEV_NO_EMS, tag_name, status[who]);
//     }
//     else
//     {
//         status[who] = 0;
//         if (dev_get_dev_tag_int(DEV_NO_EMS, tag_name) != 0)
//         {
//             dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 0);
//         }
//     }

//     if (status[who] != 0)
//     {
//         SET_AMASK_BIT(all_status, who);
//     }
//     else
//     {
//         CLR_AMASK_BIT(all_status, who);
//     }

//     if (get_all_fault_value(get_pcs_ctrl_var()->cabinet_cnt) == all_status)
//     {
//         dev_set_dev_tag_int(DEV_NO_EMS, DOD_LIMITED, 1);
//     }
//     else
//     {
//         dev_set_dev_tag_int(DEV_NO_EMS, DOD_LIMITED, 0);
//     }

//     ems_syslog(LOG_WARNING, "fast stop dod protect [nod:%s] -> [%d]", tag_name, status[who]);
//     ems_syslog(LOG_WARNING, "fast stop dod protect [nod:%s] -> [%d]", DOD_LIMITED, all_status);
// }

static int stop_charge_status[MAX_RACK_SUM_SUPPORT] = {0};
static int stop_charge_status_all = 0;
static int stop_discharge_status[MAX_RACK_SUM_SUPPORT] = {0};
static int stop_discharge_status_all = {0};

int clean_prohibite_chag_and_dischag(int cur_rack_sum)
{
    char buff[TAG_NAME_LEN] = {0};
    //
    stop_charge_status_all = 0;
    stop_discharge_status_all = 0;
    memset(stop_charge_status, 0, sizeof(stop_charge_status));
    memset(stop_discharge_status, 0, sizeof(stop_discharge_status));
    //
    ems_syslog(LOG_NOTICE, "CLEAR CHARGE PROTO!!");
    dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_CHARGE, 0);
    dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_DISCHARGE, 0);

    for (int i = 0; i < cur_rack_sum; i++)
    {
        snprintf(buff, sizeof(buff), CAB_PROHIBITE_CHARGE "%d", i + 1);
        ems_syslog(LOG_NOTICE, "CLEAR:%s", buff);
        dev_set_dev_tag_int(DEV_NO_EMS, buff, 0);
        //
        snprintf(buff, sizeof(buff), CAB_PROHIBITE_DISCHARGE "%d", i + 1);
        ems_syslog(LOG_NOTICE, "CLEAR:%s", buff);
        dev_set_dev_tag_int(DEV_NO_EMS, buff, 0);
    }
    return 0;
}

static void fast_stop_charge_tab_mul_cb(char *nod, tag_value *data_value)
{
    int who = 0;
    int val = ALARM_STATUS_NO;
    char tag_name[TAG_NAME_LEN] = {0};

    if (strstr(nod, DEV_NO_BMS) == NULL)
    {
        ems_syslog(LOG_ERR, "the tag %s shouldn't appear here", nod);
        return;
    }

    who = get_nod_index(nod, DEV_NO_BMS);
    if (who < 0)
    {
        ems_syslog(LOG_ERR, "the tag %s config error", nod);
        return;
    }

    if (data_value->type == TYPE_TAG_INT)
    {
        val = data_value->value.to_int;
    }
    else
    {
        val = data_value->value.to_float;
    }

    if (strstr(nod, CURRENT_SOC) != NULL)
    {
        int en_dod = dev_get_dev_tag_int(DEV_NO_EMS, EN_PROTECT_DOD);
        snprintf(tag_name, sizeof(tag_name), CAB_DOD_LIMITED "%d", who + 1);
        if (en_dod && (val >= dev_get_dev_tag_int(DEV_NO_EMS, EMS_SOCMAX)))
        {
            SET_AMASK_BIT(stop_charge_status[who], 4);
            dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 1);
        }
        else
        {
            CLR_AMASK_BIT(stop_charge_status[who], 4);
            dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 0);
        }
    }
    else
    {
        if (dev_get_dev_tag_int(DEV_NO_EMS, EN_BMS_ALARM) == 0)
        {
            ems_syslog(LOG_WARNING, "bms dlarm not enable");
            return;
        }

        if (strstr(nod, CELL_OVER_VOL2) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(stop_charge_status[who], 0);
            }
            else
            {
                CLR_AMASK_BIT(stop_charge_status[who], 0);
            }
        }
        else if (strstr(nod, CELL_OVER_VOL3) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(stop_charge_status[who], 1);
            }
            else
            {
                CLR_AMASK_BIT(stop_charge_status[who], 1);
            }
        }
        else if (strstr(nod, RACK_OVER_VOL2) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(stop_charge_status[who], 2);
            }
            else
            {
                CLR_AMASK_BIT(stop_charge_status[who], 2);
            }
        }
        else if (strstr(nod, RACK_OVER_VOL3) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(stop_charge_status[who], 3);
            }
            else
            {
                CLR_AMASK_BIT(stop_charge_status[who], 3);
            }
        }
    }

    snprintf(tag_name, sizeof(tag_name), CAB_PROHIBITE_CHARGE "%d", who + 1);
    if (stop_charge_status[who] > ALARM_STATUS_NO)
    {
        SET_AMASK_BIT(stop_charge_status_all, who);
        dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 1);
    }
    else
    {
        CLR_AMASK_BIT(stop_charge_status_all, who);
        dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 0);
    }

    if (get_all_fault_value(get_pcs_ctrl_var()->cabinet_cnt) == stop_charge_status_all)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_CHARGE, 1);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_CHARGE, 0);
    }
    //
    ems_syslog(LOG_WARNING, "fast stop charge [nod:%s] -> [%d]", nod, stop_charge_status[who]);
    ems_syslog(LOG_WARNING, "fast stop charge [nod:%s] -> [%d]", PROHIBITE_CHARGE, stop_charge_status_all);
}

static void fast_stop_discharge_tab_mul_cb(char *nod, tag_value *data_value)
{
    int val = ALARM_STATUS_NO;
    int who = 0;
    char tag_name[TAG_NAME_LEN] = {0};

    if (strstr(nod, DEV_NO_BMS) == NULL)
    {
        ems_syslog(LOG_ERR, "the tag %s shouldn't appear here", nod);
        return;
    }

    who = get_nod_index(nod, DEV_NO_BMS);
    if (who < 0)
    {
        ems_syslog(LOG_ERR, "the tag %s config error", nod);
        return;
    }

    if (data_value->type == TYPE_TAG_INT)
    {
        val = data_value->value.to_int;
    }
    else
    {
        val = data_value->value.to_float;
    }

    if (strstr(nod, CURRENT_SOC) != NULL)
    {
        int en_dod = dev_get_dev_tag_int(DEV_NO_EMS, EN_PROTECT_DOD);
        snprintf(tag_name, sizeof(tag_name), CAB_DOD_LIMITED "%d", who + 1);
        if (en_dod && (val <= dev_get_dev_tag_int(DEV_NO_EMS, EMS_SOCMIN)))
        {
            SET_AMASK_BIT(stop_discharge_status[who], 4);
            dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 1);
        }
        else
        {
            CLR_AMASK_BIT(stop_discharge_status[who], 4);
            dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 0);
        }
    }
    else
    {
        if (dev_get_dev_tag_int(DEV_NO_EMS, EN_BMS_ALARM) == 0)
        {
            ems_syslog(LOG_WARNING, "bms dlarm not enable");
            return;
        }

        if (strstr(nod, CELL_UNDER_VOL2) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(stop_discharge_status[who], 0);
            }
            else
            {
                CLR_AMASK_BIT(stop_discharge_status[who], 0);
            }
        }
        else if (strstr(nod, CELL_UNDER_VOL3) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(stop_discharge_status[who], 1);
            }
            else
            {
                CLR_AMASK_BIT(stop_discharge_status[who], 1);
            }
        }
        else if (strstr(nod, RACK_UNDER_VOL2) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(stop_discharge_status[who], 2);
            }
            else
            {
                CLR_AMASK_BIT(stop_discharge_status[who], 2);
            }
        }
        else if (strstr(nod, RACK_UNDER_VOL3) != NULL)
        {
            if (val == ALARM_STATUS_TRIG)
            {
                SET_AMASK_BIT(stop_discharge_status[who], 3);
            }
            else
            {
                CLR_AMASK_BIT(stop_discharge_status[who], 3);
            }
        }
    }

    snprintf(tag_name, sizeof(tag_name), CAB_PROHIBITE_DISCHARGE "%d", who + 1);
    if (stop_discharge_status[who] > ALARM_STATUS_NO)
    {
        SET_AMASK_BIT(stop_discharge_status_all, who);
        dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 1);
    }
    else
    {
        CLR_AMASK_BIT(stop_discharge_status_all, who);
        dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 0);
    }

    if (get_all_fault_value(get_pcs_ctrl_var()->cabinet_cnt) == stop_discharge_status_all)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_DISCHARGE, 1);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS, PROHIBITE_DISCHARGE, 0);
    }
    //
    ems_syslog(LOG_WARNING, "fast stop discharge [who:%s] -> [%d]", nod, stop_discharge_status[who]);
    ems_syslog(LOG_WARNING, "fast stop discharge [nod:%s] -> [%d]", PROHIBITE_DISCHARGE, stop_discharge_status_all);
}

static void fast_stop_cab_state_tab_mul_cb(char *nod, tag_value *data_value)
{
    int val = ALARM_STATUS_NO;
    static int status[MAX_RACK_SUM_SUPPORT] = {0};
    char tag_name[TAG_NAME_LEN] = {0};
    if (data_value->type == TYPE_TAG_INT)
        val = data_value->value.to_int;
    else
        val = data_value->value.to_float;
    int enableBmsAlarm = dev_get_dev_tag_int(DEV_NO_EMS, EN_BMS_ALARM);
    char *tmp1=NULL ;
    int who = get_nod_index(nod, DEV_NO_BMS); // BMS
    if (who >= 0)
    {
        if (strstr(nod, STATE_ONLINE) != NULL)
        {
            if (val == 0)
            {
                SET_AMASK_BIT(status[who], 0);
            }
            else
            {
                CLR_AMASK_BIT(status[who], 0);
            }
        }
        else // if ((strstr(nod, RACK_OVER_VOL3) != NULL) || (strstr(nod, RACK_UNDER_VOL3) != NULL) || (strstr(nod, CELL_UNDER_VOL3) != NULL) || (strstr(nod, CELL_OVER_VOL3) != NULL))
        {
            const char *p_arr[] = {RACK_OVER_VOL3, RACK_UNDER_VOL3, CELL_UNDER_VOL3, CELL_OVER_VOL3};
            if (enableBmsAlarm != 0)
            {
                for (int i = 0; i < sizeof(p_arr) / sizeof(p_arr[0]); i++)
                {
                    if (strstr(nod, p_arr[i]) != NULL)
                    {
                        if (enableBmsAlarm && val)
                        {
                            SET_AMASK_BIT(status[who], 4 + i);
                        }
                        else
                        {
                            CLR_AMASK_BIT(status[who], 4 + i);
                        }
                    }
                }
            }
            else
            {
                for (int i = 0; i < sizeof(p_arr) / sizeof(p_arr[0]); i++)
                {
                    if (strstr(nod, p_arr[i]) != NULL)
                    {
                        CLR_AMASK_BIT(status[who], 4 + i);
                    }
                }
            }
        }
        goto set_nod;
    }

    who = get_nod_index(nod, DEV_NO_PCS);
    if (who >= 0)
    {
        if (strstr(nod, STATE_ONLINE) != NULL)
        {
            if (val == 0)
            {
                SET_AMASK_BIT(status[who], 2);
            }
            else
            {
                CLR_AMASK_BIT(status[who], 2);
            }
        }
        else if (strstr(nod, PCS_FAULT) != NULL)
        {
            if (val != 0)
            {
                SET_AMASK_BIT(status[who], 3);
            }
            else
            {
                CLR_AMASK_BIT(status[who], 3);
            }
        }
        goto set_nod;
    }

    if ((tmp1 = strstr(nod, DEV_NO_EMS "." CAB_DC_TOTAL_VOL_LOW)))
    {
        who = atoi(tmp1 + strlen(DEV_NO_EMS "." CAB_DC_TOTAL_VOL_LOW)) - 1;
        if (who >= 0)
        {
            if (val > 0)
            {
                SET_AMASK_BIT(status[who], 9);
            }
            else
            {
                CLR_AMASK_BIT(status[who], 9);
            }
            goto set_nod;
        }
    }

    ems_syslog(LOG_ERR, "the tag %s config error", nod);
    return;
set_nod:
    ems_syslog(LOG_WARNING, "fast stop [nod:%s] -> int:%ld, float:%f", nod, data_value->value.to_int, data_value->value.to_float);
    snprintf(tag_name, sizeof(tag_name), CAB_SYSTEM_STATUS "%d", who + 1);
    ems_syslog(LOG_WARNING, "fast stop SYSTEM_STATUS [nod:%s] -> [%d]", tag_name, status[who]);
    dev_set_dev_tag_int(DEV_NO_EMS, tag_name, status[who]);
    //
}

static void fast_stop_system_state_mul_cb(char *nod, tag_value *data_value)
{
    int val = ALARM_STATUS_NO;
    static int status = 0;
    static int cab_status = 0;
    int cur_rack_sum = get_pcs_ctrl_var()->cabinet_cnt;
    if (data_value->type == TYPE_TAG_INT)
        val = data_value->value.to_int;
    else
        val = data_value->value.to_float;

    // for (size_t i = 0; i < sizeof(fast_stop_system_state_tab_mul) / sizeof(fast_stop_system_state_tab_mul[0]); i++)
    // {
    //     if (strcmp(nod, fast_stop_system_state_tab_mul[i]) == 0)
    //     {
    //         if (val != 0)
    //         {
    //             SET_AMASK_BIT(status, i);
    //         }
    //         else
    //         {
    //             CLR_AMASK_BIT(status, i);
    //         }
    //         goto nod_set;
    //     }
    // }
    if (strcmp(nod, DEV_NO_EMS "." IO_PROTECT) == 0)
    {
        if (val != 0)
        {
            SET_AMASK_BIT(status, 0);
        }
        else
        {
            CLR_AMASK_BIT(status, 0);
        }
    }

    if (strcmp(nod, DEV_NO_EMS "." METER_ERROR) == 0)
    {
        if (val != 0)
        {
            SET_AMASK_BIT(status, 1);
        }
        else
        {
            CLR_AMASK_BIT(status, 1);
        }
    }

    if (strcmp(nod, DEV_NO_EMS "." USER_STOP1) == 0)
    {
        if (val != 0)
        {
            SET_AMASK_BIT(status, 2);
        }
        else
        {
            CLR_AMASK_BIT(status, 2);
        }
    }

    if (strcmp(nod, DEV_NO_EMS "." USER_STOP2) == 0)
    {
        if (val != 0)
        {
            SET_AMASK_BIT(status, 3);
        }
        else
        {
            CLR_AMASK_BIT(status, 3);
        }
    }

    if (strcmp(nod, DEV_NO_EMS "." USER_STOP3) == 0)
    {
        if (val != 0)
        {
            SET_AMASK_BIT(status, 4);
        }
        else
        {
            CLR_AMASK_BIT(status, 4);
        }
    }

    if (strcmp(nod, DEV_NO_EMS "." TEMP_CTRL_ERR) == 0)
    {
        if (val != 0)
        {
            SET_AMASK_BIT(status, 5);
        }
        else
        {
            CLR_AMASK_BIT(status, 5);
        }
    }

    if (strcmp(nod, DEV_NO_EMS "." ALL_FIRE) == 0)
    {
        if (val != 0)
        {
            SET_AMASK_BIT(status, 6);
        }
        else
        {
            CLR_AMASK_BIT(status, 6);
        }
    }

    if (strcmp(nod, DEV_NO_EMS "." REFLUX_STATE) == 0)
    {
        if (val == EMS_REFLUX_STATE_FAILED)
        {
            // 逆流时间超过62秒, 必须手动复位
            SET_AMASK_BIT(status, 7);
        }
        else
        {
            CLR_AMASK_BIT(status, 7);
        }
    }

    for (size_t i = 0; i < cur_rack_sum; i++)
    {
        if (strcmp(nod, fast_stop_system_state_tab_mul2[i]) == 0)
        {
            if (val != 0)
            {
                SET_AMASK_BIT(cab_status, i);
            }
            else
            {
                CLR_AMASK_BIT(cab_status, i);
            }
            break;
        }
    }

    ems_syslog(LOG_WARNING, "fast stop [nod:%s] -> [%d]", nod, cab_status);

    val = 0;
    for (; val < cur_rack_sum; val++)
    {
        if ((cab_status & (0x01 << val)) == 0)
        {
            CLR_AMASK_BIT(status, 16);
            break;
        }
    }
    if (val == cur_rack_sum) // 单柜状态全部都设置了异常
    {
        SET_AMASK_BIT(status, 16);
    }

nod_set:
    ems_syslog(LOG_WARNING, "fast stop SYSTEM_STATUS [nod:%s] -> [%d]", SYSTEM_STATUS, status);
    dev_set_dev_tag_int(DEV_NO_EMS, SYSTEM_STATUS, status);
}

static void fast_stop_dc_voltage_unusual_mul_cb(char *nod, tag_value *data_value)
{
    static int status = 0;
    char tag_name[TAG_NAME_LEN] = {0};
    int who = get_nod_index(nod, DEV_NO_BMS);
    if (who < 0)
    {
        ems_syslog(LOG_ERR, "the tag %s config error", nod);
        return;
    }

    snprintf(tag_name, sizeof(tag_name), CAB_DC_TOTAL_VOL_LOW "%d", who + 1);
    int val = (data_value->type == 0) ? data_value->value.to_int : data_value->value.to_float;
    if (val < DC_TOTAL_VOL_LIMIT)
    {
        SET_AMASK_BIT(status, who);
        dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 1);
    }
    else
    {
        CLR_AMASK_BIT(status, who);
        dev_set_dev_tag_int(DEV_NO_EMS, tag_name, 0);
    }

    if (get_all_fault_value(get_pcs_ctrl_var()->cabinet_cnt) == status)
    {
        dev_set_dev_tag_int(DEV_NO_EMS, DC_TOTAL_VOL_LOW, 1);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS, DC_TOTAL_VOL_LOW, 0);
    }

    ems_syslog(LOG_WARNING, "fast stop discharge [who:%s] -> [%d]", nod, status);
}


static void fast_stop_PCS_power_limit_mul_cb(char *nod, tag_value *data_value)
{
#if 0
    //float val = data_value->value.to_float;
    float val = dev_get_dev_tag_float(DEV_NO_PCS, ACTIVE_POWER);
    float expectActPower = dev_get_dev_tag_float(DEV_NO_EMS, EXPECT_ACT_POWER);
    float SOC = dev_get_dev_tag_float(DEV_NO_BMS, "SOC");

    //SOC到一定值，BMS限制导致的功率不对，不判断
    int flag = ( (SOC >= 90.0 && expectActPower < 0 ) || ( SOC <= 10.0 && expectActPower > 0)) ? 0 : 1; 
    if ( ((fabs(expectActPower) >= 1.0f) && (fabs((fabs(expectActPower) - fabs(val))) > fabs(expectActPower)*0.2)) && flag )
    {
        dev_set_dev_tag_int(DEV_NO_EMS, PCS_POWER_ERR,  1);
    }
    else
    {
        dev_set_dev_tag_int(DEV_NO_EMS, PCS_POWER_ERR,  0);
    }
#endif
}

int variant_cb_register_mul(pcs_ctrl_var_t* var)
{
    //********************回调函数不要做耗时操作(一定不要做不确定的阻塞操作)
    // 到内部点位中注册点位
    int cur_rack_sum = var->cabinet_cnt;
    int ret = 0;
    ret = ems_config_point_register(var, ems_config_tab, ems_config_tab_count_g, ems_config_data_change_cb);
    if (ret != 0)
        ems_syslog(LOG_ERR, "load ems_config_tab table error");
    ret = collector_variant_monitor_add(var, ems_contrl_tab, ARRAY_SIZE(ems_contrl_tab), ems_contrl_data_change_cb);
    if (ret != 0)
        ems_syslog(LOG_ERR, "load ems_contrl_tab table error");

    // TODO 不用到UA注册的
    //  ret = collector_variant_monitor_add(var,check_rack_max_temp_tab, ARRAY_SIZE(check_rack_max_temp_tab),check_rack_max_temp_cb);
    //  if(ret != 0)
    //      ems_syslog(LOG_ERR, "load check_rack_max_temp_tan table error");
    // collector_variant_monitor_add_mul(var, fast_stop_dod_protect_tab_mul, ARRAY_SIZE(fast_stop_dod_protect_tab_mul), fast_stop_dod_protect_mul_cb, cur_rack_sum);
    collector_variant_monitor_add_mul(var, fast_stop_charge_tab_mul, ARRAY_SIZE(fast_stop_charge_tab_mul), fast_stop_charge_tab_mul_cb, cur_rack_sum);
    collector_variant_monitor_add_mul(var, fast_stop_discharge_tab_mul, ARRAY_SIZE(fast_stop_discharge_tab_mul), fast_stop_discharge_tab_mul_cb, cur_rack_sum);
    ret = collector_variant_monitor_add(var, fast_stop_system_state_tab_mul, ARRAY_SIZE(fast_stop_system_state_tab_mul), fast_stop_system_state_mul_cb);
    if (ret != 0)
        ems_syslog(LOG_ERR, "load fast_stop_system_state_tab_mul error");
    ret = collector_variant_monitor_add(var, fast_stop_system_state_tab_mul2, cur_rack_sum, fast_stop_system_state_mul_cb);
    if (ret != 0)
        ems_syslog(LOG_ERR, "load fast_stop_system_state_tab_mul2 error");

    collector_variant_monitor_add_mul(var, fast_stop_cab_state_tab_mul, ARRAY_SIZE(fast_stop_cab_state_tab_mul), fast_stop_cab_state_tab_mul_cb, cur_rack_sum);
    collector_ems_variant_monitor_add_mul(var, fast_stop_cab_state_tab2_mul, ARRAY_SIZE(fast_stop_cab_state_tab2_mul), fast_stop_cab_state_tab_mul_cb, cur_rack_sum);
    collector_variant_monitor_add_mul(var, fast_stop_temp_ctrl_tab_mul, ARRAY_SIZE(fast_stop_temp_ctrl_tab_mul), fast_stop_temp_ctrl_mul_cb, cur_rack_sum);

    collector_variant_monitor_add_mul(var, fast_stop_dc_voltage_unusual_tab_mul, ARRAY_SIZE(fast_stop_dc_voltage_unusual_tab_mul), fast_stop_dc_voltage_unusual_mul_cb, cur_rack_sum);

#if 0
    // 消防回调
    register_fire_cb(lc_fire_cb); // 通过广播获取消防其他设备消防信息
    ret = collector_variant_monitor_add(var, fast_stop_fire_tab, ARRAY_SIZE(fast_stop_fire_tab), fast_stop_fire_cb);
    if (ret != 0)
        ems_syslog(LOG_ERR, "load fast_fire_tab table error");
#endif
    collector_variant_monitor_add_mul(var, fast_stop_PCS_power_limit_tab_mul, ARRAY_SIZE(fast_stop_PCS_power_limit_tab_mul), fast_stop_PCS_power_limit_mul_cb, cur_rack_sum);

    collector_variant_monitor_add_mul(var, fast_stop_cell_voltage_unusual_tab_mul, ARRAY_SIZE(fast_stop_cell_voltage_unusual_tab_mul), fast_stop_cell_voltage_unusual_cb, cur_rack_sum);

    tag_trigger_init(var);
    automatic_init(CONFIG_PATH "/" AUTOMATIC_CONFIG);
    // check_pack_temp(var);
    reset_reflux_state_times_init();
    return ret;
}

#endif

#if 1 // tag条件触发代码
struct trigger_group
{
    int sum;
    tag_trigger trigger[0];
} *trg_group;
static const char *con_str_arr[] = {"==", ">", "<", ">=", "<="};
tag_trigger_condition string_to_tag_trigger_condition(char *con)
{
    tag_trigger_condition ret = TAG_TRIGGER_EQ;
    for (; ret < TAG_TRIGGER_MAX; ret++)
    {
        if (strcmp(con_str_arr[ret], con) == 0)
        {
            return ret;
        }
    }

    return TAG_TRIGGER_MAX;
}

const char *trigger_condition_to_string(tag_trigger_condition con)
{
    if (con < TAG_TRIGGER_MAX)
    {
        return con_str_arr[con];
    }
    else
    {
        return NULL;
    }
}
int tag_trigger_is_triggered(tag_trigger *tri, tag_value_t *tag_cur_v)
{
    int ret = 0;
    switch (tri->condition)
    {
    case TAG_TRIGGER_GT:
        ret = (tag_cur_v->type == 0) ? (tag_cur_v->value.to_int > (int)tri->cmp_value) : (tag_cur_v->value.to_float > tri->cmp_value);
        break;

    case TAG_TRIGGER_LT:
        ret = (tag_cur_v->type == 0) ? (tag_cur_v->value.to_int < (int)tri->cmp_value) : (tag_cur_v->value.to_float < tri->cmp_value);
        break;

    case TAG_TRIGGER_EQ:
        ret = (tag_cur_v->type == 0) ? (tag_cur_v->value.to_int == (int)tri->cmp_value) : (fabsf(tag_cur_v->value.to_float - tri->cmp_value) < 0.0001);
        break;

    case TAG_TRIGGER_GE:
        ret = (tag_cur_v->type == 0) ? (tag_cur_v->value.to_int >= (int)tri->cmp_value) : (tag_cur_v->value.to_float >= tri->cmp_value);
        break;

    case TAG_TRIGGER_LE:
        ret = (tag_cur_v->type == 0) ? (tag_cur_v->value.to_int <= (int)tri->cmp_value) : (tag_cur_v->value.to_float <= tri->cmp_value);
        break;

    default:
        ems_syslog(LOG_ERR, "trigger condition error");
        ret = -1;
        break;
    }
    return ret;
}

static void tag_trigger_cb(char *nod, tag_value *data_value)
{
    int cur_int = 0;


    for (size_t i = 0; i < trg_group->sum; i++)
    {
        if (strcmp(nod, trg_group->trigger[i].sponsor) == 0)
        {
            ems_syslog(LOG_NOTICE, "trigger[%s]:cmp_value=%f, tag_value:{int[%ld],float[%f]}", trg_group->trigger[i].sponsor, trg_group->trigger[i].cmp_value, data_value->value.to_int, data_value->value.to_float);
            if (tag_trigger_is_triggered(&trg_group->trigger[i], data_value))
            {
                if ((0 == collector_read_one_int_data(trg_group->trigger[i].recipient, &cur_int)) && ((cur_int & trg_group->trigger[i].bit_mask) == 0))
                {
                    cur_int = cur_int | trg_group->trigger[i].bit_mask;
                    collector_write_one_int_data(trg_group->trigger[i].recipient, cur_int);
                }
            }
            else if (trg_group->trigger[i].self_restoring != 0)
            {
                if ((0 == collector_read_one_int_data(trg_group->trigger[i].recipient, &cur_int)) && ((cur_int & trg_group->trigger[i].bit_mask) != 0))
                {
                    cur_int = cur_int & (~trg_group->trigger[i].bit_mask);
                    collector_write_one_int_data(trg_group->trigger[i].recipient, cur_int);
                }
            }
        }
    }
}

int tag_trigger_init(pcs_ctrl_var_t *var)
{
    char *cfg_name = TRIGGER_CFG_FILE;
    char *f_data = read_file_data(cfg_name);
    if (f_data == NULL)
    {
        ems_syslog(LOG_ERR, "load trigger config file error, file:%s", cfg_name);
        return -1;
    }

    int ret = 0;
    cJSON *root = json_parse_string_with_comments(f_data);
    if (!root)
    {
        ems_syslog(LOG_ERR, "parse pdata %s error", f_data);
        ret = -2;
        goto tag_trigger_init_exit;
    }

    cJSON *triggers = cJSON_GetObjectItemCaseSensitive(root, "triggers");
    if (triggers == NULL)
    {
        ems_syslog(LOG_ERR, "get temp triggers fail:%s", cfg_name);
        ret = -3;
        goto tag_trigger_init_exit;
    }

    int trigger_sum = cJSON_GetArraySize(triggers);
    cJSON *t = NULL;
    cJSON *tmp = NULL;
    ems_syslog(LOG_NOTICE, "===================================trigger_sum = %d", trigger_sum);
    if (trigger_sum > 0)
    {
        trg_group = calloc(trigger_sum, sizeof(tag_trigger) + sizeof(*trg_group));
        if (trg_group == NULL)
        {
            ems_syslog(LOG_ERR, "calloc failed");
            ret = -4;
        }
        else
        {
            trg_group->sum = trigger_sum;
            for (int i = 0; i < trigger_sum; i++)
            {
                t = cJSON_GetArrayItem(triggers, i);
                tmp = cJSON_GetObjectItemCaseSensitive(t, "sponsor");
                trg_group->trigger[i].sponsor = strdup(tmp->valuestring);

                tmp = cJSON_GetObjectItemCaseSensitive(t, "condition");
                trg_group->trigger[i].condition = string_to_tag_trigger_condition(tmp->valuestring);

                tmp = cJSON_GetObjectItemCaseSensitive(t, "cmp_value");
                trg_group->trigger[i].cmp_value = tmp->valuedouble;

                tmp = cJSON_GetObjectItemCaseSensitive(t, "recipient");
                trg_group->trigger[i].recipient = strdup(tmp->valuestring);

                tmp = cJSON_GetObjectItemCaseSensitive(t, "bit");
                trg_group->trigger[i].bit_mask = 0x01 << tmp->valueint;

                tmp = cJSON_GetObjectItemCaseSensitive(t, "self_restoring");
                trg_group->trigger[i].self_restoring = (tmp->valueint != 0) ? 1 : 0;

                ems_syslog(LOG_NOTICE, "tag trigger[%d]:%s %s %f -> %s[0x%x], self_restoring:%d", i, trg_group->trigger[i].sponsor,
                          trigger_condition_to_string(trg_group->trigger[i].condition), trg_group->trigger[i].cmp_value,
                          trg_group->trigger[i].recipient, trg_group->trigger[i].bit_mask, trg_group->trigger[i].self_restoring);
            }
            // 注册回调
            for (int i = 0; i < trigger_sum; i++)
            {
                if (0 == collector_variant_monitor_add(var, &trg_group->trigger[i].sponsor, 1, tag_trigger_cb))
                {
                    ems_syslog(LOG_NOTICE, "tag[%s] register trigger success", trg_group->trigger[i].sponsor);
                }
            }
        }
    }
tag_trigger_init_exit:
    free(f_data);
    cJSON_Delete(root);
    return ret;
}

#endif

static void reset_reflux_state_times_init(void)
{
    int      times            = 0;
    char*    data             = NULL;
    cJSON*   root             = NULL;
    cJSON*   item             = NULL;
    char*    str              = NULL;
    uint64_t cur_ms           = get_time_stamp_ms();
    uint64_t reflux_time      = 0;
    data                      = read_file_data(REFLUX_TIMES_FILE);
    if (data != NULL)
    {
        root = cJSON_Parse(data);
        if (root != NULL)
        {
            item = cJSON_GetObjectItem(root, REFLUX_FEILD);
            if (item != NULL && cJSON_IsArray(item))
            {
                int len = cJSON_GetArraySize(item);
                for (int i = 0; i < len; i++)
                {
                    str         = cJSON_GetArrayItem(item, i)->valuestring;
                    reflux_time = strtoull(str, NULL, 10);
                    if (cur_ms - reflux_time < CHECK_INTERVAL_MS)
                    {
                        times++;
                    }
                }
            }
        }
        cJSON_Delete(root);
        free(data);
    }
    if (dev_get_dev_tag_int(DEV_NO_EMS, REFLUX_STATE) == 2)
        times--;
    
    dev_set_dev_tag_int(DEV_NO_EMS, RESET_REFLUX_STATE_TIMES, times);
}
