#include "pub_fun.h"
#include "../opcua/ua-server.h"
#include <sys/statvfs.h>
#include <sys/sysinfo.h>
#include "../cmdclient.h"
#include "collector-api.h"
#include "dy_utils/led.h"
#include <openssl/md5.h>
#include "define.h"
#include "../ems/utils.h"
#include <math.h>
#include <libgen.h>
#include <ctype.h>
#include <stdio.h>

#include "../multilanguage.h"
char *create_ems_template(template_info_t *info_p, ems_base_variant_t **tags, int tags_size)
{
    cJSON *root = NULL;
    char *string = NULL;
    root = cJSON_CreateObject();
    if (root == NULL)
    {
        ems_syslog(LOG_NOTICE, "cJSON_CreateObject root NULL");
        goto end;
    }
    cJSON *info = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "info", info);
    cJSON *proto = NULL;
    cJSON *proto_arr = cJSON_CreateArray();
    char **pp = info_p->protocol;
    while (*pp != NULL)
    {
        proto = cJSON_CreateString(*pp++);
        cJSON_AddItemToArray(proto_arr, proto);
    }
    cJSON_AddItemToObject(info, "protocol", proto_arr);
    cJSON_AddStringToObject(info, "vendor", info_p->vendor);
    cJSON_AddStringToObject(info, "model", info_p->model);
    cJSON_AddStringToObject(info, "type", "EMS");
    cJSON_AddStringToObject(info, "version", info_p->version);
    // cJSON *poll_list = cJSON_CreateArray();
    // cJSON_AddItemToObject(root, "poll_list", poll_list);
    cJSON *read_reg = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "read_reg", read_reg);

    cJSON *write_reg = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "write_reg", write_reg);
    ems_syslog(LOG_NOTICE, "tags_size %d", tags_size);
    for (int i = 0; i < tags_size; i++)
    {
        cJSON *node = cJSON_CreateObject();
        // char tag[64] = {0};
        // sprintf(tag,"%s.%s",tags[i]->dev,tags[i]->variant);
        cJSON_AddStringToObject(node, "tag", tags[i]->variant);
        cJSON_AddStringToObject(node, "name", tags[i]->name);
        cJSON_AddStringToObject(node, "property", get_string_property(tags[i]->point_type));

        if (tags[i]->data_type == 0)
            cJSON_AddStringToObject(node, "type", "int");
        else
            cJSON_AddStringToObject(node, "type", "float");
        // ems_syslog(LOG_NOTICE, "dev:%s var:%s tag :%s name :%s flag:%d",tags[i]->dev,tags[i]->variant,tag,tags[i]->name,tags[i]->flag);
        //  cJSON_AddNumberToObject(node, "scale", tou->StMin);
        //  cJSON_AddNumberToObject(node, "offset", tou->EnHour);
        cJSON_AddStringToObject(node, "history", tags[i]->history_type);
        cJSON_AddStringToObject(node, "desc", tags[i]->desc);
        cJSON_AddItemToObject(node, "offset", cJSON_CreateNumber(0));
        cJSON_AddItemToObject(node, "scale", cJSON_CreateNumber(1));
        cJSON_AddStringToObject(node, "prot", ""); // 保证模板统一
        if (tags[i]->min < tags[i]->max)
        {
            cJSON *limit_arr = cJSON_CreateArray();
            if (limit_arr)
            {
                cJSON_AddItemToArray(limit_arr, cJSON_CreateNumber(tags[i]->min));
                cJSON_AddItemToArray(limit_arr, cJSON_CreateNumber(tags[i]->max)); 
                cJSON_AddItemToObject(node, "limit", limit_arr);
            }
        }
        if (tags[i]->flag == O_WRONLY)
        {
            cJSON_AddItemToArray(write_reg, node);
        }
        else if (tags[i]->flag == O_RDONLY)
        {
            cJSON_AddItemToArray(read_reg, node);
        }
        else
        {
            ems_syslog(LOG_NOTICE, "type error!!");
        }
    }

    string = cJSON_Print(root);
end:
    if (root != NULL)
    {
        cJSON_Delete(root);
    }
    // ems_syslog(LOG_NOTICE,"%s",string);
    return string;
}

int get_nod_index(const char *nod, const char *head)
{
    if (strstr(nod, head) != nod) // 头不匹配
    {
        return -2;
    }

    int who = strlen(head); // 根据nod[stelen(head)]字符判断是那个设备
    if (nod[who] == '.')
    {
        who = 0;
    }
    else
    {
        if (nod[who] > '9' || nod[who] < '0')
        {
            who = -3;
        }
        else
        {
            who = atoi(&nod[who]) - 1;
        }
        if ((who < 1) || (who >= MAX_RACK_SUM_SUPPORT))
        {
            who = -1;
        }
    }

    return who;
}

uint32_t get_all_fault_value(int rack_sum)
{
    const static uint32_t fault[MAX_RACK_SUM_SUPPORT] = {0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff};
    return fault[rack_sum - 1];
}

int getIccid(char *iccid, int size)
{
    memset(iccid, 0, size);
    char dist[64] = {0};
    char *buf_iccid;

    const char *board_name = get_board_name();
    if (strstr(board_name, "WooLink2F6280_W_4G"))
    {
        if (getCommandReturn("gcom -d \"/dev/ttyUSB2\" -s /etc/gcom/geticcid.gcom", dist, sizeof(dist)) == 0)
        {
            if (strstr(dist, "ICCID:") != NULL)
            {
                strncpy(iccid, dist + 7, size);
                return 0;
            }
        }
    }
    else
    {
        buf_iccid = read_file_data("/tmp/simcard_iccid");
        if (buf_iccid)
        {
            strncpy(iccid, buf_iccid + 7, size);
            free(buf_iccid);
            return 0;
        }
    }
    return -1;
}

int getInternalIccid(char *iccid, int size)
{
    memset(iccid, 0, size);
    char *buf_iccid;

    buf_iccid = read_file_data("/tmp/.modem_4ginfo.json");
    if (buf_iccid)
    {
        cJSON *result = cJSON_Parse(buf_iccid);
        cJSON *iccid1 = cJSON_GetObjectItemCaseSensitive(result, "ICCID1");
        free(buf_iccid);
        if (iccid1)
        {
            buf_iccid = cJSON_Print(iccid1);
            cJSON_Delete(result);

            strncpy(iccid, buf_iccid + 1, strlen(buf_iccid) - 2);
            ems_syslog(LOG_INFO, "get Internal ICCID: %s", iccid);
            free(buf_iccid);
            return 0;
        }
        else
        {
            cJSON_Delete(result);
        }
    }

    return -1;
}

int getExternalIccid(char *iccid, int size)
{
    memset(iccid, 0, size);
    char *buf_iccid;

    buf_iccid = read_file_data("/tmp/.modem_4ginfo.json");
    if (buf_iccid)
    {
        cJSON *result = cJSON_Parse(buf_iccid);
        cJSON *iccid0 = cJSON_GetObjectItemCaseSensitive(result, "ICCID0");
        free(buf_iccid);
        if (iccid0)
        {
            buf_iccid = cJSON_Print(iccid0);
            cJSON_Delete(result);

            strncpy(iccid, buf_iccid + 1, strlen(buf_iccid) - 2);
            ems_syslog(LOG_INFO, "get External ICCID: %s", iccid);
            free(buf_iccid);
            return 0;
        }
        else
        {
            cJSON_Delete(result);
        }
    }

    return -1;
}

int read_sim_cb(char *name, void *value_v)
{
    char tmp_ch[128] = {0};
    getIccid(tmp_ch, sizeof(tmp_ch));
    strcpy((char *)value_v, tmp_ch);
    return 0;
}

int read_sim_In(char *name, void *value_v)
{
    char tmp_ch[128] = {0};
    getInternalIccid(tmp_ch, sizeof(tmp_ch));
    strcpy((char *)value_v, tmp_ch);
    return 0;
}

int read_sim_Ex(char *name, void *value_v)
{
    char tmp_ch[128] = {0};
    getExternalIccid(tmp_ch, sizeof(tmp_ch));
    strcpy((char *)value_v, tmp_ch);
    return 0;
}

void get_disk_usage(const char *path, double *mem_used_persent, unsigned long long *mem_used, unsigned long long *mem_totle)
{
    struct statvfs disk_info;
    // const char *path = "/";  // 要获取信息的挂载点路径

    if (statvfs(path, &disk_info) == 0)
    {
        unsigned long long block_size = disk_info.f_bsize;    // 块大小
        unsigned long long total_blocks = disk_info.f_blocks; // 总块数
        // unsigned long long free_blocks = disk_info.f_bfree;      // 空闲块数
        unsigned long long available_blocks = disk_info.f_bavail; // 可用块数
        unsigned long long total_size = block_size * total_blocks;
        // unsigned long long free_size = block_size * free_blocks;
        unsigned long long available_size = block_size * available_blocks;
        unsigned long long used_size = total_size - available_size;
        if (mem_used_persent != NULL)
            *mem_used_persent = used_size * 100.0f / total_size;
        if (mem_used != NULL)
            *mem_used = used_size;
        if (mem_totle != NULL)
            *mem_totle = total_size;
    }
    else
    {
        perror("Failed to retrieve disk information");
    }
}

void get_mem_usage(double *mem_used)
{

    struct sysinfo info;
    if (sysinfo(&info) != 0)
    {
        ems_syslog(LOG_ERR, "get sys_info failed");
        exit(-123456);
    }

    FILE *fd;
    /************
    ** /proc / meminfo中包含系统允许内存信息
    **
    ****/

    fd = fopen("/proc/meminfo", "r");
    if (fd == NULL)
    {
        perror("open /proc/meminfo failed\n");
        exit(0);
    }

    size_t bytes_read;
    size_t read;
    char *line = NULL;
    /************
     *mem_used = total-memavailable
     *******/
    int avimem = 0;

    while ((read = getline(&line, &bytes_read, fd)) != -1)
    {
        if (strstr(line, "MemAvailable") != NULL)
        {
            sscanf(line, "%*s%d%*s", &avimem);
            break;
        }
    }
    if (line != NULL)
    {
        free(line);
    }
    int t = info.totalram / 1024.0;
    *mem_used = (t - avimem) * 100 / t;

    fclose(fd);
}

int nod_cmp_int(const char *dev_id, const char *var_id, int cmp)
{
    int tmp = dev_get_dev_tag_int(dev_id, var_id);
    if (cmp > tmp)
    {
        return 1;
    }
    else if (cmp < tmp)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}

double cal_occupy(CPU_OCCUPY *c1, CPU_OCCUPY *c2)
{
    double t1, t2;
    double id, sd;
    double cpu_used;

    t1 = (double)(c1->user + c1->nice + c1->system + c1->idle);
    t2 = (double)(c2->user + c2->nice + c2->system + c2->idle);

    id = (double)(c2->user - c1->user);
    sd = (double)(c2->system - c1->system);

    cpu_used = (100 * (id + sd) / (t2 - t1));
    return cpu_used;
}

void get_cpuoccupy(CPU_OCCUPY *cpu)
{

    char buff[512];
    FILE *fd;

    /* /proc/stat 包含了系统启动以来的许多关于kernel和系统的统计信息，
    其中包括CPU运行情况、中断统计、启动时间、上下文切换次数、
    运行中的进程等等信息 */
    fd = fopen("/proc/stat", "r");
    if (fd == NULL)
    {
        perror("open /proc/stat failed\n");
        exit(0);
    }
    fgets(buff, sizeof(buff), fd);

    sscanf(buff, "%s %u %u %u %u", cpu->name, &cpu->user, &cpu->nice,
           &cpu->system, &cpu->idle);
    fclose(fd);
}

void get_cpu_usage(double *cpu_used)
{
    CPU_OCCUPY cpu_0, cpu_1;

    get_cpuoccupy(&cpu_0);
    /*   while (1)  {
    //实现循环监控CPU状态，间隔时间1s */
    sleep(1);
    get_cpuoccupy(&cpu_1);

    *cpu_used = cal_occupy(&cpu_0, &cpu_1);
#if DEBUG
    printf("cpu: %.2f%%\n", *cpu_used);
#endif
    /*   cpu_0 = cpu_1;//循环监控打开，结构体拷贝赋值语句
  } */
}

static pthread_once_t s_factory_product_once = PTHREAD_ONCE_INIT;
static char s_factory_product[64];

static void ems_init_factory_product_once(void)
{
    FILE *fp;
    char line[256];

    s_factory_product[0] = '\0';
    fp = popen("factory get", "r");
    if (fp == NULL)
    {
        ems_syslog(LOG_ERR, "factory get popen failed");
        return;
    }
    while (fgets(line, sizeof(line), fp) != NULL)
    {
        char *p;
        line[strcspn(line, "\r\n")] = '\0';
        if (strncmp(line, "PRODUCT=", 8) != 0)
            continue;
        p = line + 8;
        while (*p != '\0' && isspace((unsigned char)*p))
            p++;
        strncpy(s_factory_product, p, sizeof(s_factory_product) - 1);
        s_factory_product[sizeof(s_factory_product) - 1] = '\0';
        p = s_factory_product + strlen(s_factory_product);
        while (p > s_factory_product && isspace((unsigned char)p[-1]))
            *--p = '\0';
        break;
    }
    pclose(fp);
    if (s_factory_product[0] != '\0')
        ems_syslog(LOG_INFO, "cached factory PRODUCT: %s", s_factory_product);
}

const char *ems_get_cached_product_model(void)
{
    pthread_once(&s_factory_product_once, ems_init_factory_product_once);
    return s_factory_product;
}

void *get_cycle_info(void *data)
{
    // pthread_detach(pthread_self());
    extern SYSTEM_INFO system_info;
    UA_SERVER_PARAM *ua_param = data;
    double tmp;
    unsigned long long mem_used, mem_total;
    char tmp_c[1024] = {0};
    // int ret;
#define LOOP_T 5
    while (1)
    {
        sleep(LOOP_T);
        // open_sysinfo();
        get_disk_usage("/", &tmp, &mem_used, &mem_total);
        sprintf(tmp_c, "%lld/%lld", mem_used / (1024 * 1024), mem_total / (1024 * 1024));
        set_char_to_node(ua_param->ua_server, system_info.stateInfo.diskNodeId, tmp_c);
        get_mem_usage(&tmp);
        sprintf(tmp_c, "%.2f%%", tmp);
        set_char_to_node(ua_param->ua_server, system_info.stateInfo.memNodeId, tmp_c);
        get_cpu_usage(&tmp);
        sprintf(tmp_c, "%.2f%%", tmp);
        set_double_to_node(ua_param, system_info.stateInfo.CPUNodeId, tmp, 0);
        extern void check_ems_heartbeat(UA_SERVER_PARAM * ua_param, int call_cycle);
        check_ems_heartbeat(ua_param, LOOP_T);
#ifdef EN_EMS
        int tmp_i, tmp_fd = -1;
        extern void get_net_usage(int *net_status);
        get_net_usage(&tmp_i);
        ems_syslog(LOG_INFO, "[net_status]:%d\n", tmp_i);
        set_int_to_node(ua_param->ua_server, system_info.stateInfo.NetStaNodeId, tmp_i, 0);
        // if (tmp_i != 0)
        // {
        //     snprintf(tmp_c, sizeof(tmp_c), "%s", _("Network Online"));
        // }
        // else
        // {
        //     snprintf(tmp_c, sizeof(tmp_c), "%s", _("Network Offline"));
        // }
        // set_char_to_node(ua_param->ua_server, system_info.stateInfo.NetStaNodeId, tmp_c);

        tmp_fd = open("/etc/signal_4g.txt", O_RDONLY | O_CLOEXEC);
        if (tmp_fd != -1)
        {
            char *nextp;
            ssize_t ret;
            ret = read(tmp_fd, tmp_c, sizeof(tmp_c) - 1);
            close(tmp_fd);
            tmp_fd = -1;

            tmp_i = 0;
            if (ret > 0)
            {
                tmp_c[ret] = '\0';
                nextp = NULL;
                tmp_i = (int)strtol(tmp_c, &nextp, 0);
                if (tmp_i == -1 || nextp == tmp_c)
                    tmp_i = 0;
            }

            tmp = (double)tmp_i;
            set_double_to_node(ua_param, system_info.stateInfo.SignalLevel4GId, tmp, 0);
        }

        mem_used = 0;
        mem_total = 0;
        const char *disk_path = get_disk_path();
        snprintf(tmp_c, sizeof(tmp_c), "mountpoint -q %s", disk_path);
        if (cmd_call(tmp_c, 10, NULL, 1) == 0)
        {
            get_disk_usage(disk_path, &tmp, &mem_used, &mem_total);
        }
        sprintf(tmp_c, "%lld/%lld", mem_used / (1024 * 1024), mem_total / (1024 * 1024));
        set_char_to_node(ua_param->ua_server, system_info.stateInfo.ExdiskNodeId, tmp_c);
#endif
    }
}
int Ping_Test_IP(char *testip, int timeout_s)
{
    char dataBuff[256] = {0};
    sprintf(dataBuff, "ping %s -w%d -c1 > /dev/null", testip, timeout_s);
    if (cmd_call(dataBuff, timeout_s, NULL, 1) != 0)
        return -1;

    return 0;
}

#if (SUPPORT_LED == 1)
static int net_status_led; // 用于获取NET状态
void get_net_usage(int *net_status)
{
    *net_status = net_status_led;
}

void *net_led_task(void *arg)
{
    post_net_led(LED_TWINKLE);
    int state = -1;
    while (1)
    {
        if (Ping_Test_IP("frps.lnxall.com", 5) == 0 || Ping_Test_IP("mqtt.lnxall.com", 5) == 0 || Ping_Test_IP("8.8.8.8", 5) == 0)
        {
            if (state != LED_LIGHT_ON)
            {
                state = LED_LIGHT_ON;
                post_net_led(LED_TWINKLE_OFF);
                post_net_led(LED_LIGHT_ON);
                net_status_led = 1;
            }
        }
        else
        {
            if (state != LED_TWINKLE)
            {
                state = LED_TWINKLE;
                post_net_led(LED_LIGHT_OFF);
                post_net_led(LED_TWINKLE);
                net_status_led = 0;
            }
        }
        sleep(5);
    }
}
void net_led_check()
{
    pthread_t tip;
    if (0 == pthread_create(&tip, NULL, net_led_task, NULL))
    {
        pthread_setname_np(tip, "netLedTask");
    }
}
#endif

static int dev_class_apend(cJSON *dev_class, const char *type, cJSON *devs)
{
    cJSON *obj = NULL;
    cJSON *type_obj = NULL;
    cJSON *tar_obj = NULL;
    int cnt = cJSON_GetArraySize(dev_class);
    for (int i = 0; i < cnt; i++)
    {
        obj = cJSON_GetArrayItem(dev_class, i);
        if (obj == NULL)
        {
            return -1;
        }
        type_obj = cJSON_GetObjectItemCaseSensitive(obj, "type");
        if (type_obj == NULL)
        {
            return -2;
        }
        if (strcmp(type_obj->valuestring, type) == 0)
        {
            tar_obj = cJSON_GetObjectItemCaseSensitive(obj, "devs");
            break;
        }
    }
    if (tar_obj == NULL)
    {
        cJSON *new_obj = cJSON_CreateObject();
        tar_obj = cJSON_CreateArray();
        cJSON_AddItemToObject(new_obj, "type", cJSON_CreateString(type));
        cJSON_AddItemToObject(new_obj, "devs", tar_obj);
        cJSON_AddItemToArray(dev_class, new_obj);
    }
    //
    int sum = cJSON_GetArraySize(devs);
    while (sum > 0)
    {
        obj = cJSON_DetachItemFromArray(devs, 0);
        cJSON_AddItemToArray(tar_obj, obj);
        sum = cJSON_GetArraySize(devs);
    }
    return 0;
}

int dev_cfg_merge(cJSON *dev_cfg1, cJSON *dev_cfg2, int need_cell)
{
    cJSON *obj = NULL;
    cJSON *type = NULL;
    cJSON *tar_obj = NULL;
    cJSON *dev_class1 = cJSON_GetObjectItemCaseSensitive(dev_cfg1, "dev_class");
    cJSON *dev_class2 = cJSON_GetObjectItemCaseSensitive(dev_cfg2, "dev_class");
    int cnt = cJSON_GetArraySize(dev_class2);
    for (int i = 0; i < cnt; i++)
    {
        obj = cJSON_GetArrayItem(dev_class2, i);
        if (obj == NULL)
        {
            return -1;
        }
        type = cJSON_GetObjectItemCaseSensitive(obj, "type");
        tar_obj = cJSON_GetObjectItemCaseSensitive(obj, "devs");
        if ((type != NULL) && (tar_obj != NULL))
        {
            if ((need_cell != 0) || (strcmp(type->valuestring, "CELL") != 0)) // 电芯不需要
            {
                dev_class_apend(dev_class1, type->valuestring, tar_obj);
            }
        }
        else
        {
            return -2;
        }
    }
    return 0;
}

void *trip_task(void *args)
{
#ifdef EN_EMS
    char *dev_no = DEV_NO_EMS;
#else
    char *dev_no = DEV_NO_LC;
#endif
    if (args != NULL)
    {
        dev_no = (char*)args;
    }
    pthread_detach(pthread_self());
    ems_syslog(LOG_WARNING, "trip!!!!!");
    dev_set_dev_tag_int(dev_no, TRIP, 1);
    usleep(900 * 1000);
    usleep(200 * 1000);
    dev_set_dev_tag_int(dev_no, TRIP, 0);
    return NULL;
}
void do_trip(void) // 脱扣
{
    pthread_t tip;
    if (0 == pthread_create(&tip, NULL, trip_task, NULL))
    {
        pthread_setname_np(tip, "trip task");
    }
}

int cal_md5(char *str, char *out)
{
    unsigned char md[MD5_DIGEST_LENGTH];
    MD5_CTX md5;
    MD5_Init(&md5);
    MD5_Update(&md5, str, strlen(str));
    MD5_Final(md, &md5);
    for (size_t i = 0; i < MD5_DIGEST_LENGTH; i++)
    {
        sprintf(out + i * 2, "%02x", md[i]);
    }
    return 0;
}

char *cal_file_md5(const char *f_name)
{
    char *md5_buff = calloc(1, MD5_DIGEST_LENGTH * 2 + 1);

    if (md5_buff != NULL)
    {
        char *p = read_file_data(f_name);
        if (p != NULL)
        {
            cal_md5(p, md5_buff);
            free(p);
        }
    }
    return md5_buff;
}

static char *get_dido_define_config(void)
{
    const char *board = get_board_name();
    if (board == NULL)
        return NULL;

    char *config = NULL;
    if (strcmp(board, BOARD_NAME_RK3568) == 0 || strcmp(board, BOARD_NAME_EMS1000) == 0 || strcmp(board, BOARD_NAME_RK3568_XDDL) == 0 || strcmp(board, BOARD_NAME_BAU_B30_003) == 0)
    {
        config = strdup(DIDO_RK3568_DEFINE_CONFIG);
    }
    else if (strcmp(board, BOARD_NAME_6ULL) == 0)
    {
        config = strdup(DIDO_6ULL_DEFINE_CONFIG);
    }
    else if( strcmp(board, BOARD_NAME_ECU1170) == 0)
    {
        config = strdup(DIDO_ECU1170_DEFINE_CONFIG);
    }
    return config;
}

int set_dido_common_config(cJSON *root)
{
    if (root == NULL)
        return -1;

    cJSON *dev_cfg = cJSON_GetObjectItem(root, "dev_cfg");
    if (dev_cfg == NULL)
        return -1;

    cJSON *dido_cfg = NULL;
    char *dido_file = NULL;

    char* file_name = DIDO_TEMPLATE_CONFIG;

    char temp_file[256] = {0};

    if (file_name[0] == '/') // 绝对路径
    {
        snprintf(temp_file, sizeof(temp_file), "%s", file_name);
    }
    else
    {
        snprintf(temp_file, sizeof(temp_file), "%s/%s", TEMPLATE_DIR, file_name);
    }

    if (access(temp_file, F_OK) == 0)
    {
        dido_file = read_file_data(temp_file);
    }
    else
    {
        dido_file = get_dido_define_config();
    }

    if (dido_file == NULL)
        return -1;

    dido_cfg = json_parse_string_with_comments(dido_file);
    free(dido_file);

    if (dido_cfg == NULL)
        return -1;

    cJSON *di = cJSON_GetObjectItem(dev_cfg, "di");
    if (di != NULL)
    {
        cJSON *attr = cJSON_GetObjectItem(di, "attr");
        cJSON *read_reg = cJSON_GetObjectItem(dido_cfg, "read_reg");

        if (read_reg == NULL) // 如果模板中没有read_reg, 则创建一个, 理论上不会没有
        {
            read_reg = cJSON_CreateArray();
            cJSON_AddItemToObject(dido_cfg, "read_reg", read_reg);
        }

        if (attr != NULL) // 理论上不可能没有
        {
            int di_nums = cJSON_GetArraySize(attr); // 获取DI数量, 不启用的DI可以不配置
            for (int i = 0; i < di_nums; i++)
            {
                cJSON *di_attr = cJSON_GetArrayItem(attr, i);
                if (di_attr == NULL)
                    continue;

                cJSON *di_tag = cJSON_GetObjectItem(di_attr, "name");
                cJSON *di_name = cJSON_GetObjectItem(di_attr, "disp_name");
                cJSON *ncno = cJSON_GetObjectItem(di_attr, "ncno");

                if (di_tag == NULL || strlen(di_tag->valuestring) <= 0 ||
                    di_name == NULL || strlen(di_name->valuestring) <= 0)
                    continue;

                int read_reg_cnt = cJSON_GetArraySize(read_reg);
                for (int j = 0; j < read_reg_cnt; j++) // 根据 DIx 找到reg中的对应项
                {
                    cJSON *read_reg_item = cJSON_GetArrayItem(read_reg, j);
                    if (read_reg_item == NULL)
                        continue;

                    cJSON *reg_tag = cJSON_GetObjectItem(read_reg_item, "tag");

                    if (reg_tag == NULL || strcmp(reg_tag->valuestring, di_tag->valuestring) != 0)
                        continue;

                    cJSON_ReplaceItemInObject(read_reg_item, "name", cJSON_CreateString(di_name->valuestring));

                    cJSON *read_reg_port = cJSON_GetObjectItem(read_reg_item, "prot");
                    int port_nums = cJSON_GetArraySize(read_reg_port);

                    if (port_nums == 7 && ncno != NULL) // read_reg port里必须有7项, 不做容错处理
                    {
                        cJSON_ReplaceItemInArray(read_reg_port, 6, cJSON_CreateNumber(ncno->valueint));
                    }
                }
            }
        }
    }

    cJSON *do_ = cJSON_GetObjectItem(dev_cfg, "do");
    if (do_ != NULL)
    {
        cJSON *attr = cJSON_GetObjectItem(do_, "attr");
        cJSON *write_reg = cJSON_GetObjectItem(dido_cfg, "write_reg");

        if (attr != NULL) // 理论上不能为空
        {
            int do_nums = cJSON_GetArraySize(attr);
            for (int i = 0; i < do_nums; i++)
            {
                cJSON *do_attr = cJSON_GetArrayItem(attr, i);
                if (do_attr == NULL)
                    continue;

                cJSON *do_tag = cJSON_GetObjectItem(do_attr, "name");
                cJSON *do_name = cJSON_GetObjectItem(do_attr, "disp_name");

                if (do_tag == NULL || strlen(do_tag->valuestring) <= 0 ||
                    do_name == NULL || strlen(do_name->valuestring) <= 0)
                    continue;

                int write_reg_cnt = cJSON_GetArraySize(write_reg);
                for (int j = 0; j < write_reg_cnt; j++)
                {
                    cJSON *write_reg_item = cJSON_GetArrayItem(write_reg, j);
                    if (write_reg_item == NULL)
                        continue;

                    cJSON *reg_tag = cJSON_GetObjectItem(write_reg_item, "tag");
                    if (reg_tag == NULL || strcmp(reg_tag->valuestring, do_tag->valuestring) != 0)
                    {
                        continue;
                    }
                    cJSON_ReplaceItemInObject(write_reg_item, "name", cJSON_CreateString(do_name->valuestring));
                }
            }
        }
    }

    char *file = cJSON_Print(dido_cfg);
    if (file != NULL)
    {
        write_file_data(temp_file, file, strlen(file));
        free(file);
    }

    cJSON_Delete(dido_cfg);

    return 0;
}

static int get_dio_reg_addr_by_no(const char *no)
{
    struct no_addr_map {
        char *no;
        int addr;
    }no_addr[] = {
        {.no = "DI1", .addr = 0x80},
        {.no = "DI2", .addr = 0x81},
        {.no = "DI3", .addr = 0x82},
        {.no = "DI4", .addr = 0x83},
        {.no = "DI5", .addr = 0x84},
        {.no = "DI6", .addr = 0x85},
        {.no = "DI7", .addr = 0x86},
        {.no = "DI8", .addr = 0x87},
        {.no = "DI9", .addr = 0x88},
        {.no = "DI10", .addr = 0x89},
        {.no = "DI11", .addr = 0x8a},
        {.no = "DI12", .addr = 0x8b},
        {.no = "DI13", .addr = 0x8c},
        {.no = "DI14", .addr = 0x8d},
        {.no = "DI15", .addr = 0x8e},
        {.no = "DI16", .addr = 0x8f},
        //
        {.no = "DO1", .addr = 0x05},
        {.no = "DO2", .addr = 0x04},
        {.no = "DO3", .addr = 0x03},
        {.no = "DO4", .addr = 0x02},
        {.no = "DO5", .addr = 0x01},
        {.no = "DO6", .addr = 0x00},
        {.no = "NC1", .addr = 0x06},
        {.no = "NC2", .addr = 0x07},
    };

    for (int i = 0; i < sizeof(no_addr) / sizeof(no_addr[0]); i++)
    {
        if (strcmp(no_addr[i].no, no) == 0)
        {
            return  no_addr[i].addr;
        }
    }

    return -1;
}

static cJSON *creat_read_reg_item(cJSON *di_attr, int index)
{
    cJSON *di_tag = cJSON_GetObjectItem(di_attr, "name");
    cJSON *di_name = cJSON_GetObjectItem(di_attr, "disp_name");
    cJSON *ncno = cJSON_GetObjectItem(di_attr, "ncno");
    //
    cJSON *return_item = cJSON_CreateObject();
    cJSON_AddItemToObject(return_item, "tag", cJSON_CreateString(di_tag->valuestring));
    cJSON_AddItemToObject(return_item, "name", cJSON_CreateString(di_name->valuestring));
    cJSON_AddItemToObject(return_item, "desc", cJSON_CreateString(""));
    cJSON_AddItemToObject(return_item, "property", cJSON_CreateString("realdata"));
    cJSON_AddItemToObject(return_item, "history", cJSON_CreateString("both"));
    cJSON_AddItemToObject(return_item, "type", cJSON_CreateString("int"));
    cJSON_AddItemToObject(return_item, "offset", cJSON_CreateNumber(0));
    cJSON_AddItemToObject(return_item, "scale", cJSON_CreateNumber(1));
    // cJSON_AddItemToObject(return_item, "no_fetch", cJSON_CreateNumber(0));
    //
    cJSON *port = cJSON_CreateArray();
    char reg_addr[32] = {0};
    snprintf(reg_addr, sizeof(reg_addr), "0x%02x", get_dio_reg_addr_by_no(di_tag->valuestring));
    cJSON_AddItemToArray(port, cJSON_CreateNumber(1));
    cJSON_AddItemToArray(port, cJSON_CreateString(reg_addr));
    cJSON_AddItemToArray(port, cJSON_CreateString("bool"));
    cJSON_AddItemToArray(port, cJSON_CreateString("A"));
    cJSON_AddItemToArray(port, cJSON_CreateNumber(0));
    cJSON_AddItemToArray(port, cJSON_CreateNumber(1));
    cJSON_AddItemToArray(port, cJSON_CreateNumber(ncno->valueint));
    //
    cJSON_AddItemToObject(return_item, "port", port);

    return return_item;
}

static cJSON *creat_wriye_reg_item(cJSON *di_attr, int index)
{
    cJSON *do_tag = cJSON_GetObjectItem(di_attr, "name");
    cJSON *do_name = cJSON_GetObjectItem(di_attr, "disp_name");
    //
    cJSON *return_item = cJSON_CreateObject();
    cJSON_AddItemToObject(return_item, "tag", cJSON_CreateString(do_tag->valuestring));
    cJSON_AddItemToObject(return_item, "name", cJSON_CreateString(do_name->valuestring));
    cJSON_AddItemToObject(return_item, "desc", cJSON_CreateString(""));
    cJSON_AddItemToObject(return_item, "property", cJSON_CreateString("realdata"));
    cJSON_AddItemToObject(return_item, "history", cJSON_CreateString("both"));
    cJSON_AddItemToObject(return_item, "type", cJSON_CreateString("int"));
    cJSON_AddItemToObject(return_item, "offset", cJSON_CreateNumber(0));
    cJSON_AddItemToObject(return_item, "scale", cJSON_CreateNumber(1));
    cJSON_AddItemToObject(return_item, "no_fetch", cJSON_CreateNumber(0));
    //
    cJSON *port = cJSON_CreateArray();
    char reg_addr[32] = {0};
    snprintf(reg_addr, sizeof(reg_addr), "0x%02x", get_dio_reg_addr_by_no(do_tag->valuestring));
    cJSON_AddItemToArray(port, cJSON_CreateNumber(1));
    cJSON_AddItemToArray(port, cJSON_CreateString(reg_addr));
    cJSON_AddItemToArray(port, cJSON_CreateString("bool"));
    cJSON_AddItemToArray(port, cJSON_CreateString("A"));
    //
    cJSON_AddItemToObject(return_item, "port", port);

    return return_item;
}

int port_cfg_2_dio_template(cJSON *port_cfg, const char *save_name) // 和set_dido_common_config函数逻辑功能一致，为了保证接口不改变新加这个函数
{
    const char *dido_template_str = "{\"info\":{\"protocol\":[\"modbus_tcp\",\"modbus_rtu\"],\"model\":\"DIDO_LNXALL\",\"vendor\":\"JJIOT\",\"compatible\":\"5.312.0000\",\"version\":\"0.0.0-alpha\",\"type\":\"OTHERS\"},\"poll_list\":[{\"scan_rate\":500,\"prot\":[1,\"0x0080\",16]},{\"scan_rate\":2000,\"prot\":[1,\"0x0000\",6]}],\"read_reg\":[],\"write_reg\":[]}";
    cJSON *dido_temp = cJSON_Parse(dido_template_str);
    cJSON *read_reg = cJSON_GetObjectItem(dido_temp, "read_reg");
    cJSON *write_reg = cJSON_GetObjectItem(dido_temp, "write_reg");

    cJSON *dev_cfg = cJSON_GetObjectItem(port_cfg, "dev_cfg");
    if (dev_cfg == NULL)
    {
        cJSON_Delete(dido_temp);
        return -1;
    }

    cJSON *di = cJSON_GetObjectItem(dev_cfg, "di");
    cJSON *tmp = NULL;
    if (di != NULL)
    {
        cJSON *attr = cJSON_GetObjectItem(di, "attr");
        if (attr != NULL) // 理论上不可能没有
        {
            int di_nums = cJSON_GetArraySize(attr); // 获取DI数量, 不启用的DI可以不配置
            for (int i = 0; i < di_nums; i++)
            {
                cJSON *di_attr = cJSON_GetArrayItem(attr, i);
                if (di_attr == NULL)
                    continue;

                tmp = creat_read_reg_item(di_attr, i);
                if (tmp != NULL)
                {
                    cJSON_AddItemToArray(read_reg, tmp);
                }
            }
        }
    }

    cJSON *do_ = cJSON_GetObjectItem(dev_cfg, "do");
    if (do_ != NULL)
    {
        cJSON *attr = cJSON_GetObjectItem(do_, "attr");

        if (attr != NULL) // 理论上不能为空
        {
            int do_nums = cJSON_GetArraySize(attr);
            for (int i = 0; i < do_nums; i++)
            {
                cJSON *do_attr = cJSON_GetArrayItem(attr, i);
                if (do_attr == NULL)
                    continue;
                tmp = creat_wriye_reg_item(do_attr, i);
                if (tmp != NULL)
                {
                    cJSON_AddItemToArray(write_reg, tmp);
                }
            }
        }
    }

    char *file = cJSON_Print(dido_temp);
    if (file != NULL)
    {
        write_file_data(save_name, file, strlen(file));
        free(file);
    }

    cJSON_Delete(dido_temp);

    return 0;
}

static void heat_management_ac(float power, int cur_rack_sum) // 空调管理
{
#if EN_EMS
    check_aircond_startstop(power); // 主机根据策略或当前功率检测空调是否开启,然后将空调设置同步到从机相同测点
#else
    int tval = dev_get_dev_tag_int(DEV_NO_LC, AIR_COND_STARTSTOP_CTRL); // 从机被动的获取空调设置,然后写入数采
    ac_set_mode_api(tval);                                              // 1:开机; 0:停机
#endif
}

// 00:待机;01:制冷;02:制热;03:自循环;04:全自动;
static void heat_management_tms(float power, const char *lc) // 液冷机热管理,主从逻辑相同  todo 优化写入数采改为回调
{
    char dev_no[TAG_NAME_LEN] = {0};
    float currue_temp_cool = -DEFAULT_TEMP;
    float currue_temp_hot = DEFAULT_TEMP;
    int StartHot = 0;
    int EndHot = 0;
    int StartCool = 0;
    int EndCool = 0;
    int DistCool = 0;
    int DistHot = 0;
    int online = 0;
    int tms_state = 0;
    float StartCycleDiffTemp = 0;
    float EndCycleDiffTemp = 0;
    char bms_dev_no[TAG_NAME_LEN] = {0};
    TMS_MODE chMode = TMS_STANDBY;
    //
    if (lc == NULL)
    {
        snprintf(bms_dev_no, sizeof(bms_dev_no), "%s", DEV_NO_BMS);
        snprintf(dev_no, sizeof(dev_no), "%s", DEV_NO_EMS);
    }
    else
    {
        snprintf(bms_dev_no, sizeof(bms_dev_no), "%s.%s", lc, DEV_NO_BMS);
        snprintf(dev_no, sizeof(dev_no), "%s", lc);
    }
    //
    if (dev_get_dev_tag_int_with_result(bms_dev_no, STATE_ONLINE, &online) != 0)
    {
        ems_syslog(LOG_ERR, "%s device not found", bms_dev_no);
        return;
    }
    //
    if (online == 0)
    {
        chMode = TMS_AUTO;
        dev_set_dev_tag_int(dev_no, TMS_CTRL_STATE, chMode); // 全自动
        tms_set_mode_api(chMode, lc);
        ems_syslog(LOG_ERR, "%s offline, TMS Current WorkMode:[%d](0:待机; 1:制冷; 2:制热; 3:自循环; 4:全自动)", bms_dev_no,chMode);
        return;
    }

    currue_temp_cool = MAX(dev_get_dev_tag_float(bms_dev_no, RACK_MAX_TEMP), currue_temp_cool);
    currue_temp_hot = MIN(dev_get_dev_tag_float(bms_dev_no, RACK_MIN_TEMP), currue_temp_hot);
    StartCycleDiffTemp = dev_get_dev_tag_float(dev_no, START_SELF_LOOP_DIFF);
    EndCycleDiffTemp = dev_get_dev_tag_float(dev_no, END_SELF_LOOP_DIFF);
    if ((currue_temp_cool == -DEFAULT_TEMP) || (currue_temp_hot == DEFAULT_TEMP))
    {
        chMode = TMS_STANDBY;
        dev_set_dev_tag_int(dev_no, TMS_CTRL_STATE, chMode); // 待机
        tms_set_mode_api(chMode, lc);
        ems_syslog(LOG_ERR, "BMS error ");
        return;
    }
    if (IS_IDLE(power))
    {
        // 空闲状态
        StartHot = dev_get_dev_tag_int(dev_no, STOP_START_HOT_TEMP);
        EndHot = dev_get_dev_tag_int(dev_no, STOP_END_HOT_TEMP);
        StartCool = dev_get_dev_tag_int(dev_no, STOP_START_COOL_TEMP);
        EndCool = dev_get_dev_tag_int(dev_no, STOP_END_COOL_TEMP);
        DistCool = dev_get_dev_tag_int(dev_no, STOP_DIST_COOL_TEMP);
        DistHot = dev_get_dev_tag_int(dev_no, STOP_DIST_HOT_TEMP);
        tms_state = 0;
    }
    else
    {
        // 工作状态
        StartHot = dev_get_dev_tag_int(dev_no, WORK_START_HOT_TEMP);
        EndHot = dev_get_dev_tag_int(dev_no, WORK_END_HOT_TEMP);
        StartCool = dev_get_dev_tag_int(dev_no, WORK_START_COOL_TEMP);
        EndCool = dev_get_dev_tag_int(dev_no, WORK_END_COOL_TEMP);
        DistCool = dev_get_dev_tag_int(dev_no, WORK_DIST_COOL_TEMP);
        DistHot = dev_get_dev_tag_int(dev_no, WORK_DIST_HOT_TEMP);
        tms_state = 1;
    }

    int currueState = 0;
    int ret = dev_get_dev_tag_int_with_result(dev_no, TMS_CTRL_STATE, &currueState);
    if (ret != 0)
        currueState = 0;

    chMode = currueState;
    ems_syslog(LOG_INFO, "TMS Work Flag:[%d](0:空闲;1:工作), 电芯最高温度:[%.2f], 电芯最低温度:[%.2f], 制热启动温度:[%d], 制热停止温度:[%d], 制冷启动温度:[%d], 制冷停止温度:[%d], 电芯温差:[%.2f], 自循环开始温差:[%.2f], 自循环停止温差:[%.2f]",tms_state,currue_temp_cool,currue_temp_hot,StartHot,EndHot,StartCool,EndCool,currue_temp_cool-currue_temp_hot,StartCycleDiffTemp,EndCycleDiffTemp);
    switch (currueState)
    {
        case TMS_COOL: // 制冷
        {
            if (currue_temp_cool <= EndCool)
            {
                goto CycleOrStandby;
            }
            else
            {
                dev_set_dev_tag_int(dev_no, TMS_CTRL_STATE, chMode); // 制冷
                tms_set_temp_api(chMode, DistCool, lc);
                tms_set_mode_api(chMode, lc);
            }
            break;
        }
        case TMS_HEAT: // 2制热
        {
            if (currue_temp_hot >= EndHot)
            {
                goto CycleOrStandby;
            }
            else
            {
                dev_set_dev_tag_int(dev_no, TMS_CTRL_STATE, chMode); // 制热
                tms_set_temp_api(chMode, DistHot, lc);
                tms_set_mode_api(chMode, lc);
            }
            break;
        }

        case 4:           // 4待机 测点 0,1,2,3,4 修改为 0,1,2,3 之后需删除, 此处为避免升级后存在bug
        case TMS_CYCLE:   // 3自循环
        case TMS_STANDBY: // 0待机
        {
            if (currue_temp_cool >= StartCool)
            {
                chMode = TMS_COOL;
                dev_set_dev_tag_int(dev_no, TMS_CTRL_STATE, chMode); // 制冷
                tms_set_temp_api(chMode, DistCool, lc);
                tms_set_mode_api(chMode, lc);
            }
            else if (currue_temp_hot <= StartHot)
            {
                chMode = TMS_HEAT;
                dev_set_dev_tag_int(dev_no, TMS_CTRL_STATE, chMode); // 制热
                tms_set_temp_api(chMode, DistHot, lc);
                tms_set_mode_api(chMode, lc);
            }
            else
            {
CycleOrStandby:                
                if ((chMode == TMS_CYCLE)||(dev_get_dev_tag_int(dev_no, EN_SELF_LOOP_DIFF) && (currue_temp_cool - currue_temp_hot) >= StartCycleDiffTemp))
                {
                    chMode = TMS_CYCLE;
                    dev_set_dev_tag_int(dev_no, TMS_CTRL_STATE, chMode); // 自循环
                    tms_set_mode_api(chMode, lc);
                    if ((currue_temp_cool - currue_temp_hot) <= EndCycleDiffTemp)
                    {
                        chMode = TMS_STANDBY;
                        dev_set_dev_tag_int(dev_no, TMS_CTRL_STATE, chMode); // 待机
                        tms_set_mode_api(chMode, lc);
                    }
                }
                else
                {
                    chMode = TMS_STANDBY;
                    dev_set_dev_tag_int(dev_no, TMS_CTRL_STATE, chMode); // 待机
                    tms_set_mode_api(chMode, lc);
                }
            }
            break;
        }

        default:
            break;
    }
    ems_syslog(LOG_INFO,"TMS Current WorkMode:[%d](0:待机; 1:制冷; 2:制热; 3:自循环; 4:全自动)",chMode);
}

void *heat_management_task(void *arg)
{
#ifdef EN_EMS
    char *dev_no = DEV_NO_EMS;
    int lc_sum = get_pcs_ctrl_var()->lc_list_len;
    lc_it *it = get_pcs_ctrl_var()->lc_list;
    int cur_rack_sum = get_pcs_ctrl_var()->cabinet_cnt;
    if (lc_sum == 0) //非主从模式
    {
        while (1)
        {
            float ExpectPower = dev_get_dev_tag_float(dev_no, EXPECT_ACT_POWER);
            if (dev_get_dev_tag_int(dev_no, ENABLE_AUTO_COOL))
            {
                heat_management_tms(ExpectPower, NULL); // 液冷机
            }

            if (dev_get_dev_tag_int(dev_no, ENABLE_AIR_COND))
            {
                heat_management_ac(ExpectPower, cur_rack_sum); // 空调
            }
            fan_ctrl(NULL);
            int sleep_time = dev_get_dev_tag_int(DEV_NO_EMS, HEAT_CTR_CYCLE);
            sleep(sleep_time);
        }
    }
    else
    {
        while (1)
        {
            float ExpectPower = dev_get_dev_tag_float(dev_no, EXPECT_ACT_POWER);
            if (dev_get_dev_tag_int(dev_no, ENABLE_AUTO_COOL))
            {
                for (int i = 0; i < lc_sum; i++)
                {
                    if (it[i].type == eLC_TYPE_VLC)
                    {
                        heat_management_tms(ExpectPower, it[i].no); // 液冷机
                    }
                }
            }

            if (dev_get_dev_tag_int(dev_no, ENABLE_AIR_COND))
            {
                heat_management_ac(ExpectPower, cur_rack_sum); // 空调
            }
            for (int i = 0; i < lc_sum; i++)
            {
                if (it[i].type == eLC_TYPE_VLC)
                {
                    fan_ctrl(it[i].no);
                }
            }
            int sleep_time = dev_get_dev_tag_int(DEV_NO_EMS, HEAT_CTR_CYCLE);
            sleep(sleep_time);
        }
    }
#else
    char *dev_no = DEV_NO_LC;
    while (1)
    {
        float ExpectPower = dev_get_dev_tag_float(dev_no, EXPECT_ACT_POWER);
        int cur_rack_sum = get_pcs_ctrl_var()->cabinet_cnt;

        if (dev_get_dev_tag_int(dev_no, ENABLE_AUTO_COOL))
        {
            heat_management_tms(ExpectPower, NULL); // 液冷机
        }

        if (dev_get_dev_tag_int(dev_no, ENABLE_AIR_COND))
        {
            heat_management_ac(ExpectPower, cur_rack_sum); // 空调
        }
        fan_ctrl(NULL);
        int sleep_time = dev_get_dev_tag_int(DEV_NO_EMS, HEAT_CTR_CYCLE);
        sleep(sleep_time);
    }
#endif

    
    return 0;
}

int heat_management_main(int ems_en_lc)
{
    pthread_t tid;
    int en_lc = ems_en_lc;

    if (pthread_create(&tid, NULL, heat_management_task, &en_lc) != 0)
    {
        ems_syslog(LOG_ERR, "create heat_management_task failed");
    }
    else
    {
        pthread_setname_np(tid, "heat_task");
    }
    return 0;
}

// 模式设置API todo 改为回调
int tms_set_mode_api(TMS_MODE mode, const char *dev_no)
{
    char device_id[TAG_NAME_LEN] = {0};
    if(dev_no)
    {
        snprintf(device_id, sizeof(device_id), "%s.%s", dev_no, DEV_NO_TMS);
    }
    else
    {
        snprintf(device_id, sizeof(device_id), "%s",DEV_NO_TMS);
    }

    int ret = pe_set_value(device_id, TMS_SET_MODE_FUNCTION, (double)mode);
    if (ret != 0)
    {
        ems_syslog(LOG_ERR, "tms_set_mode_api failed, ret=%d, dev_no=%s, function=%s, value=%d", ret, device_id, TMS_SET_MODE_FUNCTION, mode);
    }
    return ret;
}

// 温度设置API todo 改为回调
int tms_set_temp_api(TMS_MODE mode, int value, const char *dev_no)
{
    char *function = NULL;
    char device_id[TAG_NAME_LEN] = {0};

    switch (mode)
    {
    case TMS_HEAT:
        function = TMS_SET_HEAT_FUNCTION;
        break;

    case TMS_COOL:
        function = TMS_SET_COOL_FUNCTION;
        break;
    default:
        ems_syslog(LOG_ERR, "tms_set_temp_api failed, unsupported mode=%d", mode);
        return -1;
    }

    if(dev_no)
    {
        snprintf(device_id, sizeof(device_id), "%s.%s", dev_no, DEV_NO_TMS);
    }
    else
    {
        snprintf(device_id, sizeof(device_id), "%s",DEV_NO_TMS);
    }

    int ret = pe_set_value(device_id, function, (double)value);
    if (ret != 0)
    {
        ems_syslog(LOG_ERR, "tms_set_temp_api failed, ret=%d, dev_no=%s, function=%s, value=%d", ret, device_id, function, value);
    }
    return ret;
}

int ac_set_mode_api(AC_MODE mode) // 空调设置开关机
{
    int ret = 0;
    if (mode != AC_START && mode != AC_STOP) // 判断模式
    {
        ems_syslog(LOG_ERR, "AC input Error not the mode:%d", mode);
        ret = -2;
        goto ERR;
    }
    ret = pe_set_value(DEV_NO_AC, AC_SET_ONOFF_FUNCTION, (double)mode);
    if (ret != 0)
    {
        ems_syslog(LOG_ERR, "tms_set_temp_api failed, ret=%d, dev_no=%s, function=%s, value=%d", ret, DEV_NO_AC, AC_SET_ONOFF_FUNCTION, mode);
    }
    return ret;
ERR:
    return ret;
}

int fan_ctrl(const char *ems_or_lc)
{
    static time_t last_time;
    char pcs_no[TAG_NAME_LEN] = { 0 };
    char ems_no_or_lc_no[DEV_NO_LEN] = { 0 };
    if (ems_or_lc == NULL)
    {
        snprintf(pcs_no, sizeof(pcs_no), "%s", DEV_NO_PCS);
#if EN_EMS
        snprintf(ems_no_or_lc_no, sizeof(ems_no_or_lc_no), "%s", DEV_NO_EMS);
#else
        snprintf(ems_no_or_lc_no, sizeof(ems_no_or_lc_no), "%s", DEV_NO_EMS);
#endif
    }
    else
    {
        snprintf(pcs_no, sizeof(pcs_no), "%s.%s", ems_or_lc, DEV_NO_PCS);
        snprintf(ems_no_or_lc_no, sizeof(ems_no_or_lc_no), "%s", ems_or_lc);
    }

    time_t time_now = time(NULL);
    if (dev_get_dev_tag_int(ems_no_or_lc_no, EN_DRAUG_FLOW_POWER) == 1)
    {
        double val = dev_get_dev_tag_float(pcs_no, ACTIVE_POWER);
        if (fabs(val) >= dev_get_dev_tag_int(ems_no_or_lc_no, EN_DRAUG_FLOW_POWER_T))
        {
            last_time = time_now;
        }
        ems_syslog(LOG_NOTICE, "%s.%s enable,%s."ACTIVE_POWER"=%f", ems_no_or_lc_no, EN_DRAUG_FLOW_POWER, pcs_no, val);
    }
    else
    {
        ems_syslog(LOG_NOTICE, "%s.%s disable", ems_no_or_lc_no, EN_DRAUG_FLOW_POWER);
    }
    if (last_time != time_now) // 功率没有打开风扇，再判断温度了，否则不用判断温度，直接下去控制风扇
    {
        if (dev_get_dev_tag_int(ems_no_or_lc_no, EN_DRAUG_FLOW_TEM) == 1)
        {
            double val = dev_get_dev_tag_float(pcs_no, PCS_IGBT_TEMP);
            if (val >= dev_get_dev_tag_int(ems_no_or_lc_no, EN_DRAUG_FLOW_TEM_T))
            {
                last_time = time_now;
            }
             ems_syslog(LOG_NOTICE, "%s.%s enable,%s."PCS_IGBT_TEMP"=%f", ems_no_or_lc_no, EN_DRAUG_FLOW_TEM, pcs_no, val);
        }
        else
        {
            ems_syslog(LOG_NOTICE, "%s.%s disable", ems_no_or_lc_no, EN_DRAUG_FLOW_TEM);
        }
    }


    if ((time_now - last_time) > dev_get_dev_tag_int(ems_no_or_lc_no, DRAUG_DEAE_TIME))
    {
        if (dev_get_dev_tag_int(ems_no_or_lc_no, FAN) != 0)
        {
            dev_set_dev_tag_int(ems_no_or_lc_no, FAN, 0); // 关闭风机
        }
    }
    else
    {
        if (dev_get_dev_tag_int(ems_no_or_lc_no, FAN) == 0)
        {
            dev_set_dev_tag_int(ems_no_or_lc_no, FAN, 1); // 打开风机
        }
    }

    return 0;
}


void set_total_bms_connect(int connect)
{
    proto_forward_t* p = get_proto_forward_var();
    // 遍历找到所有的BMS
    int ret = 0;
    for (int i = 0; i < p->channels_size; i++)
    {
        channel_t* c = p->channels[i];
        if (c == NULL) continue;

        for (int j = 0; j < c->devs_size; j++)
        {
            device_t* d = c->devs[j];
            if (d == NULL || strcmp(d->dev_type, DEV_NO_BMS) != 0) continue;

            if (( ret = pe_set_value(d->no, FUNCTION_SET_CONNECT, connect)) != 0)
            {
                ems_syslog(LOG_ERR, "BMS set connect fail, dev:%s ret:%d", d->no, ret);
            }
        }
    }
}
// 设置无功功率补偿方式
void set_total_pcs_repower_comp(int func)
{
    proto_forward_t* p   = get_proto_forward_var();
    int              ret = -5;
    // 遍历找到所有的PCS
    for (int i = 0; i < p->channels_size; i++)
    {
        channel_t* c = p->channels[i];
        if (c == NULL) continue;
        for (int j = 0; j < c->devs_size; j++)
        {
            device_t* d = c->devs[j];
            if (d == NULL || strcmp(d->dev_type, DEV_NO_PCS) != 0 || func < 0 || func > 1) continue;

            if ((ret = pe_set_value(d->no, FUNCTION_SET_REPOWER_COMP, func)) != 0)
            {
                ems_syslog(LOG_ERR, "PCS set power repower compensation  fail, dev:%s ret:%d", d->no, ret);
            }
        }
    }
}

void set_total_pcs_power_factor(float factor)
{
    float real = factor;
    if (factor > 0 && factor < 0.85)
    {
        ems_syslog(LOG_WARNING, "invail power factor, factor:%f, set to 0.85", factor);
        real = 0.85;
    }
    else if (factor > 1.0)
    {
        ems_syslog(LOG_WARNING, "invail power factor, factor:%f, set to 1.0", factor);
        real = 1.0;
    }
    else if (factor < -1)
    {
        ems_syslog(LOG_WARNING, "invail power factor, factor:%f, set to -1.0", factor);
        real = -1.0;
    }
    else if (factor > -0.85 && factor < 0)
    {
        ems_syslog(LOG_WARNING, "invail power factor, factor:%f, set to -0.85", factor);
        real = -0.85;
        return;
    }

    proto_forward_t* p = get_proto_forward_var();
    if (real != factor)
    {
#if EN_LC
        dev_set_dev_tag_float(DEV_NO_LC, SET_POWER_FACTOR, real);
#else
        dev_set_dev_tag_float(DEV_NO_EMS, SET_POWER_FACTOR, real);
#endif
        return;
    }
    // 遍历找到所有的PCS
    int ret = 0;
    for (int i = 0; i < p->channels_size; i++)
    {
        channel_t* c = p->channels[i];
        if (c == NULL) continue;
        for (int j = 0; j < c->devs_size; j++)
        {
            device_t* d = c->devs[j];
            if (d == NULL || strcmp(d->dev_type, DEV_NO_PCS) != 0) continue;

            if ((ret = pe_set_value(d->no, FUNCTION_SET_POWERFACTOR, factor)) != 0)
            {
                ems_syslog(LOG_ERR, "PCS set power factor fail, dev:%s ret:%d", d->no, ret);
            }
        }
    }
}

void set_total_pcs_fault_reset(int reset)
{
    if(reset != 1)
        return;
    proto_forward_t* p = get_proto_forward_var();
    // 遍历找到所有的PCS
    for (int i = 0; i < p->channels_size; i++)
    {
        channel_t* c = p->channels[i];
        if (c == NULL) continue;
        for (int j = 0; j < c->devs_size; j++)
        {
            device_t* d = c->devs[j];
            if (d == NULL || strcmp(d->dev_type, DEV_NO_PCS) != 0) continue;

            if ((pe_set_fault_rst(d->no, reset)) != 0)
            {
                ems_syslog(LOG_ERR, "PCS set fault reset fail, dev:%s", d->no);
            }
        }
    }
}

const char *get_disk_path(void)
{
    const char *path = NULL;
    struct path_map
    {
        const char *board_name;
        const char *path;
    } path_map[] =
        {
            {.board_name = "WLOSANY_LC100", .path = "/mnt/sdisk"},
            {.board_name = "WLOSANY_MA35D1", .path = "/mnt/sdisk"},
            {.board_name = "WLOSANY", .path = "/mnt/ssd"}};

    if (path == NULL)
    {
        const char *board = get_board_name();
        if (board == NULL)
        {
            ems_syslog(LOG_ERR, "get board name failed");
            exit(456); // 失败退出
        }
        for (int i = 0; i < sizeof(path_map) / sizeof(path_map[0]); i++)
        {
            if (strstr(board, path_map[i].board_name) != NULL)
            {
                path = path_map[i].path;
                break;
            }
        }

        if (path == NULL)
        {
            ems_syslog(LOG_ERR, "get disk path failed");
            exit(567); // 失败退出
        }

        const char *linkpath = "/mnt/ssd";  // 软链接路径，为了屏控导出历史数据路径固定为:/mnt/ssd/history
        if ((strcmp(path, linkpath) != 0) && (access(linkpath, F_OK) != 0))
        {
            if (symlink(path, linkpath) != 0)
            {
                ems_syslog(LOG_ERR, "%s->%s err", linkpath, path);
                exit(5678); // 失败退出
            }
        }
    }

    return path;
}

int check_and_update_emsd_version(const char *json_path) {
    char dir_path[256];
    strncpy(dir_path, json_path, sizeof(dir_path));
    dir_path[sizeof(dir_path)-1] = 0;
    char *dir = dirname(dir_path);

    struct stat st = {0};
    if (stat(dir, &st) == -1) {
        mkdir(dir, 0755);
    }

    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "name", "emsd");
    cJSON_AddStringToObject(root, "version", SW_VERSION);
    char *new_json = cJSON_Print(root);
    write_file_data(json_path, new_json, strlen(new_json));
    free(new_json);
    cJSON_Delete(root);
    return 0;
}


int sync_north_cfg_domain(domain_list_t *domain_list, const char *north_cfg_path) {
    cJSON *north_root = NULL;
    char *north_data = read_file_data(north_cfg_path);
    if (!north_data) {
        ems_syslog(LOG_ERR, "read north config failed");
        return -1;
    }
    north_root = cJSON_Parse(north_data);
    free(north_data);
    if (!north_root) {
        ems_syslog(LOG_ERR, "parse north config failed");
        return -2;
    }

    int max_count = 0;
    cJSON *arrs[2] = {cJSON_GetObjectItem(north_root, "standard"), cJSON_GetObjectItem(north_root, "custom")};
    for (int k = 0; k < 2; ++k) {
        cJSON *arr = arrs[k];
        if (arr && cJSON_IsArray(arr)) {
            max_count += cJSON_GetArraySize(arr);
        }
    }
    if (max_count == 0) {
        cJSON_Delete(north_root);
        return 0;
    }

    domain_info_t *tmp_list = (domain_info_t *)calloc(max_count, sizeof(domain_info_t));
    int domain_count = 0;

    for (int k = 0; k < 2; ++k) {
        cJSON *arr = arrs[k];
        if (!arr || !cJSON_IsArray(arr)) continue;
        int size = cJSON_GetArraySize(arr);
        for (int i = 0; i < size; ++i) {
            cJSON *item = cJSON_GetArrayItem(arr, i);
            cJSON *enable = cJSON_GetObjectItem(item, "enable");
            if (!enable || !cJSON_IsNumber(enable) || !enable->valueint) continue;
            cJSON *host = cJSON_GetObjectItem(item, "host");
            if (host && cJSON_IsString(host) && strlen(host->valuestring) > 0) {
                snprintf(tmp_list[domain_count].domain, sizeof(tmp_list[domain_count].domain), "%s", host->valuestring);
                cJSON *port = cJSON_GetObjectItem(item, "port");
                if (port && cJSON_IsNumber(port)) {
                    tmp_list[domain_count].port = (uint16_t)port->valueint;
                } else if (port && cJSON_IsString(port)) {
                    tmp_list[domain_count].port = (uint16_t)atoi(port->valuestring);
                } else {
                    tmp_list[domain_count].port = 0;
                }
                tmp_list[domain_count].status = 0;
                domain_count++;
            }
        }
    }

    if (domain_list->list) free(domain_list->list);
    domain_list->list = NULL;
    domain_list->count = 0;

    if (domain_count > 0) {
        domain_list->list = (domain_info_t *)calloc(domain_count, sizeof(domain_info_t));
        memcpy(domain_list->list, tmp_list, domain_count * sizeof(domain_info_t));
        domain_list->count = domain_count;
    }
    free(tmp_list);
    cJSON_Delete(north_root);
    return 0;
}

int update_sys_cfg_domain(const domain_list_t *domain_list,const char *sys_cfg_path) {
    cJSON *sys_root = NULL;
    char *sys_data = read_file_data(sys_cfg_path);
    if (!sys_data) {
        ems_syslog(LOG_ERR, "read sys config failed");
        return -1;
    }
    sys_root = cJSON_Parse(sys_data);
    free(sys_data);
    if (!sys_root) {
        ems_syslog(LOG_ERR, "parse sys config failed");
        return -2;
    }

    cJSON *host_list = cJSON_CreateArray();
    for (int i = 0; i < domain_list->count; ++i) {
        int exists = 0;
        for (int j = 0; j < cJSON_GetArraySize(host_list); ++j) {
            cJSON *item = cJSON_GetArrayItem(host_list, j);
            if (item && strcmp(item->valuestring, domain_list->list[i].domain) == 0) {
                exists = 1;
                break;
            }
        }
        if (!exists) {
            cJSON_AddItemToArray(host_list, cJSON_CreateString(domain_list->list[i].domain));
        }
    }

    cJSON *ipw = cJSON_GetObjectItem(sys_root, "ipw");
    if (ipw) cJSON_ReplaceItemInObject(sys_root, "ipw", host_list);
    else cJSON_AddItemToObject(sys_root, "ipw", host_list);

    char *new_sys = cJSON_Print(sys_root);
    write_file_data(sys_cfg_path, new_sys, strlen(new_sys));
    free(new_sys);

    cJSON_Delete(sys_root);
    return 0;
}

int Nc_Test_Port(const char *host, uint16_t port, int timeout_s)
{
    char dataBuff[256] = {0};
    snprintf(dataBuff, sizeof(dataBuff), "nc -z -w%d %s %u > /dev/null 2>&1", timeout_s, host, port);
    int ret = cmd_call(dataBuff, timeout_s, NULL, 1);
    if (ret != 0)
        return -1;
    return 0;
}

int check_domain_connectivity(const domain_list_t *domain_list)
{
    if (!domain_list || !domain_list->list) return -1;
    int alarm_flag = 0;
    for (int i = 0; i < domain_list->count; ++i) {
        int ret = Nc_Test_Port(domain_list->list[i].domain, domain_list->list[i].port, 5);
        if (ret != 0) {
            alarm_flag = 1;
        }
        domain_list->list[i].status = (ret != 0) ? 1 : 0;
    }
    dev_set_dev_tag_int(DEV_NO_EMS, WHITELIST_ALARM, alarm_flag);
    return alarm_flag;
}

//根据vlc数量判断返回值，有vlc返回1，无vlc返回0
int get_en_lc_to_lc_list(char *json_path)
{
    char *json_data = read_file_data(json_path);
    if (!json_data) {
        ems_syslog(LOG_ERR, "Failed to read JSON file: %s", json_path);
        return -1;
    }

    cJSON *root = cJSON_Parse(json_data);
    free(json_data);
    if (!root) {
        ems_syslog(LOG_ERR, "Failed to parse JSON file: %s", json_path);
        return -2;
    }

    cJSON *devices = cJSON_GetObjectItem(root, "devices");
    if (!devices || !cJSON_IsArray(devices)) {
        ems_syslog(LOG_ERR, "Invalid JSON structure: 'devices' not found or not an array");
        cJSON_Delete(root);
        return -3;
    }

    int vlc_count = 0;
    int size = cJSON_GetArraySize(devices);
    for (int i = 0; i < size; ++i) {
        cJSON *device = cJSON_GetArrayItem(devices, i);
        if (!device) continue;

        cJSON *type = cJSON_GetObjectItem(device, "type");

        if (type && cJSON_IsString(type) && strcmp(type->valuestring, "vlc") == 0) {
            vlc_count++;
        }
    }

    cJSON_Delete(root);

    return vlc_count > 0 ? 1 : 0;
}

int clean_4g_cfg_ipw(char *mode4gcfgpath)
{
    // 读取4G配置文件
    char *json_data = read_file_data(mode4gcfgpath);
    if (!json_data) {
        ems_syslog(LOG_ERR, "Failed to read 4G config: %s", mode4gcfgpath);
        return -1;
    }

    cJSON *root = cJSON_Parse(json_data);
    free(json_data);
    if (!root) {
        ems_syslog(LOG_ERR, "Failed to parse 4G config: %s", mode4gcfgpath);
        return -2;
    }

    // 找到ipw数组并清空
    cJSON *ipw = cJSON_GetObjectItem(root, "ipw");
    if (ipw && cJSON_IsArray(ipw)) {
        // 删除数组中所有元素（如果存在旧元素）
        int size = cJSON_GetArraySize(ipw);
        for (int i = size - 1; i >= 0; i--) {
            cJSON_DeleteItemFromArray(ipw, i);
        }
    } else {
        // ipw 不存在或不是数组，创建新的空数组替换/添加
        cJSON *empty_array = cJSON_CreateArray();
        if (ipw) {
            cJSON_ReplaceItemInObject(root, "ipw", empty_array);
        } else {
            cJSON_AddItemToObject(root, "ipw", empty_array);
        }
    }

    // 写回文件
    char *new_json = cJSON_Print(root);
    if (new_json) {
        write_file_data(mode4gcfgpath, new_json, strlen(new_json));
        free(new_json);
    }

    cJSON_Delete(root);
    return 0;
}
