#include "proto_forward.h"
#include "adapter/pe_adapter.h"
#include "common.h"
#include "tag.h"
#include "opcua/ua-server.h"
#include "modbus_tcp_ser.h"
#include "sqlite3.h"
#include <dirent.h>
#include <stdio.h>
#include "signal.h"
#include "define.h"
#include "iec104_server/iec104_server.h"
#include "proto_can_adp.h"
#include "multilanguage.h"
#include "pub_fun.h"
#include "opcua/ua-server.h"
#include "../business_log/business_log.h"
#ifdef EN_EMS
#include "ems/jsonct.h"
#include "ems/utils.h"
#include "bms_map.h"
#include "virtual_bau.h"
#endif
static UA_SERVER_PARAM *bms_connect_ua_par;
static proto_forward_t *this_p = NULL;
static int default_sample_rate = 10;
int north_control = -1;
#define EMS_CHARGE_TAGV_THRESHOLD 2

static ydt1363_code_des_t ydt1363_code_tab[] =
{
    {CUSTOM, "CUSTOM"},
	{GROUP, "GROUP"},
	{ID, "ID"},
	{TYPE, "TYPE"},
	{TIME, "TIME"},
	{DATAI, "DATAI"},
	{DATAF, "DATAF"},
};
static const int g_ydt1363_code_count = sizeof(ydt1363_code_tab) / sizeof(ydt1363_code_des_t);

// 0:静置;1:放电;2:充电;
static int enum_cha_n(const tag_value_t * value)
{
    if (value == NULL)
        return 0;

    if (value->type == 0) { // int
        if (value->value.to_int < -EMS_CHARGE_TAGV_THRESHOLD)
            return 1;
        if (value->value.to_int > EMS_CHARGE_TAGV_THRESHOLD)
            return 2;
        return 0;
    }

    if (value->value.to_float < -EMS_CHARGE_TAGV_THRESHOLD)
        return 1;
    if (value->value.to_float > EMS_CHARGE_TAGV_THRESHOLD)
        return 2;
    return 0;
}

// 0:静置;1:放电;2:充电;
static int enum_cha_p(const tag_value_t * value)
{
    if (value == NULL)
        return 0;

    if (value->type == 0) { // int
        if (value->value.to_int < -EMS_CHARGE_TAGV_THRESHOLD)
            return 2;
        if (value->value.to_int > EMS_CHARGE_TAGV_THRESHOLD)
            return 1;
        return 0;
    }

    if (value->value.to_float < -EMS_CHARGE_TAGV_THRESHOLD)
        return 2;
    if (value->value.to_float > EMS_CHARGE_TAGV_THRESHOLD)
        return 1;
    return 0;
}

static int fault_code(const tag_value_t* value)
{
    if (value == NULL) return 0;

    if (value->type == 0) // int
        return value->value.to_int == 0 ? 0 : 1;
    else
        return value->value.to_float > 0.1 ? 1 : 0;
}

static const handle_funptr handle_fptrs[] = {
    DECLARE_HANDLE_FUNPTR(enum_cha_n),
    DECLARE_HANDLE_FUNPTR(enum_cha_p),
    DECLARE_HANDLE_FUNPTR(fault_code),
    { NULL,                    NULL },
};

proto_forward_t *get_proto_forward_var(void)
{
    return this_p;
}

void set_default_sample_rare(int rate)
{
    default_sample_rate = rate;
}

proto_forward_t *proto_forward_creat(proto_forward_param_t *proto_forward_param)
{
    proto_forward_t *proto_forward = calloc(1,sizeof(proto_forward_t));
    this_p = proto_forward;
    #ifdef EN_OPCUA
    if(proto_forward_param->enable_open_ua)
    {
        bms_connect_ua_par =  proto_forward->ua_server_param = ua_server_init(&proto_forward_param->ua_base_data);
    }
    #endif

    if (proto_forward != NULL) {
        proto_forward->northdevs = calloc(1, sizeof(northdev_t)); // 假设 northdevs_t 是一个结构体
        if (proto_forward->northdevs == NULL) {
            free(proto_forward);
            return NULL;
        }
    }
    if (proto_forward_param->ua_base_data.data_persistence != 0)
    {
        proto_forward->gv = new_data_presistence(SHM_NAME, proto_forward_param->db_update_interval);
    }
    else
    {
        proto_forward->gv = NULL;
    }
    if(proto_forward_param->enable_modbus_server)
    {
        proto_forward->modbus_server_param = modbus_tcp_ser_init(&proto_forward_param->modbus_ser_data);
    }
    //proto_syslog(LOG_WARNING, "proto_forward:%p",proto_forward);
    return proto_forward;
}

static void channel_thread_start(proto_forward_t *proto_forward_p)
{
    for (size_t i = 0; i < proto_forward_p->channels_size; i++)
    {
        pthread_mutex_init(&proto_forward_p->channels[i]->channel_lock,NULL);
        int ret = pthread_create(&proto_forward_p->channels[i]->thread, NULL, prot_transf_loop, proto_forward_p->channels[i]);
        if (ret != 0)
        {
            proto_syslog(LOG_WARNING, "failed to created pthread for data process %s", strerror(errno));
            BUSINESS_LOG(BLOG_ERR, DAS_ID_0001, NULL, "[数采]: 创建数据处理线程失败 %s", strerror(errno));
        }
        else
        {
            char name_buff[16] = {0};
            int len = strlen(proto_forward_p->channels[i]->channel);
            if (len < sizeof(name_buff))
            {
                memcpy(name_buff, proto_forward_p->channels[i]->channel, len);
            }
            else
            {
                memcpy(name_buff, proto_forward_p->channels[i]->channel, 7);
                memcpy(&name_buff[7], "...", 3);
                memcpy(&name_buff[7 + 3], &proto_forward_p->channels[i]->channel[len - 5], 5);
            }
            pthread_setname_np(proto_forward_p->channels[i]->thread, name_buff);
        }
    }
}
static void channel_thread_stop(proto_forward_t *proto_forward_p)
{
    for (size_t i = 0; i < proto_forward_p->channels_size; i++)
    {
        if (proto_forward_p->channels[i]->thread == PTHREAD_NULL)
        {
            proto_syslog(LOG_CRIT, "channel thread is null, channel = %s", proto_forward_p->channels[i]->channel);
            continue;
        }

        int kill_rc = pthread_kill(proto_forward_p->channels[i]->thread, 0);
        if (kill_rc == ESRCH)
            proto_syslog(LOG_CRIT, "the specified thread did not exists or already quit, channel = %s", proto_forward_p->channels[i]->channel);
        else if (kill_rc == EINVAL)
            proto_syslog(LOG_CRIT, "signal is invalid, , channel = %s", proto_forward_p->channels[i]->channel);
        else
        {
            proto_syslog(LOG_CRIT, "the channel thread is alive, channel = %s", proto_forward_p->channels[i]->channel);
            int ret = pthread_cancel(proto_forward_p->channels[i]->thread);
            if(0 == ret)
            {
                pthread_join(proto_forward_p->channels[i]->thread, NULL);
                proto_forward_p->channels[i]->thread = PTHREAD_NULL;
                proto_syslog(LOG_CRIT, "channel thread kill success, channel = %s", proto_forward_p->channels[i]->channel);
            }
            else
            {
                proto_syslog(LOG_CRIT, "channel thread kill default, channel = %s code:%d", proto_forward_p->channels[i]->channel,ret);
            }
        }
    }
}

static void modbus_tcp_thread_start(proto_forward_t *proto_forward_p)
{
    if(proto_forward_p->modbus_server_param != NULL)
        modbus_tcp_ser_start(proto_forward_p);
}

static void modbus_tcp_thread_stop(proto_forward_t *proto_forward_p)
{
    if((proto_forward_p != NULL) && (proto_forward_p->modbus_server_param != NULL))
        modbus_tcp_ser_stop(proto_forward_p);
}

static void param_iec_and_modbus(proto_forward_t *proto_forward_p)
{
    int cnt = 0;
    char *data = NULL;
    cJSON *root = NULL;
    data = read_file_data(N_MQTT_NORTH_CFG_FILE);
    if (data == NULL)
    {
        ems_syslog(LOG_ERR, "read cfg file %s error", N_MQTT_NORTH_CFG_FILE);
        goto out;
    }
    cJSON *objs = NULL;
    cJSON *obj = NULL;
    root = cJSON_Parse(data);
    if (!root)
    {
        ems_syslog(LOG_ERR, "parse cfg file error");
        goto out;
    }
    if (cJSON_GetObjectItem(root, "north_control") != NULL)
    {
        GET_JSON_VALUE_INT(root, "north_control", north_control);
        ems_syslog(LOG_ERR,"north_control:%d",north_control);
    }
    objs = cJSON_GetObjectItem(root, "standard");
    if (!objs)
    {
        ems_syslog(LOG_ERR, "get standard failed");
        goto out;
    }
    cnt = cJSON_GetArraySize(objs);
    for (int i = 0; i < cnt; i++)
    {
        char channel_name[128] = {0};
        obj = cJSON_GetArrayItem(objs, i);
        if (cJSON_GetObjectItem(obj, "name") != NULL)
        {
            GET_JSON_VALUE_STRING(obj, "name", channel_name);
        }
        if(strcmp(channel_name,"iec104")==0)
        {
            GET_JSON_VALUE_INT(obj,"enable",proto_forward_p->iec104_enable);
        }
        else if(strcmp(channel_name,"modbus")==0)
        {
            GET_JSON_VALUE_INT(obj,"enable",proto_forward_p->modbus_enable);
        }
        else
        {
            continue;
        }
    }
    free(data);
    cJSON_Delete(root);
    return;
out:
    proto_forward_p->iec104_enable = 0;
    proto_forward_p->modbus_enable = 0;
    if (data != NULL)
    {
        free(data);
    }
    cJSON_Delete(root);
}


int proto_forward_schedule(proto_forward_t *proto_forward_p)
{
    #ifdef EN_OPCUA
    ua_server_thread_start((UA_SERVER_PARAM *)proto_forward_p->ua_server_param);
    #endif
    param_iec_and_modbus(proto_forward_p);
    channel_thread_start(proto_forward_p);
    if(proto_forward_p->modbus_enable == 1)
    {
        proto_syslog(LOG_ERR,"modbus thread start");
        modbus_tcp_thread_start(proto_forward_p);
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0049, NULL, "[北向]:modbus tcp 功能使能，线程启动");
    }
    else
    {
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0049, NULL, "[北向]:modbus tcp 功能未使能");
    }
    return 0;
}


int proto_forward_schedule_stop(void)
{
    if (this_p != NULL)
    {
        channel_thread_stop(this_p);
        modbus_tcp_thread_stop(this_p);
        //UA一定要结束到最后
        #ifdef EN_OPCUA
        ua_server_thread_stop((UA_SERVER_PARAM *)this_p->ua_server_param);
        #endif
    }
    return 0;
}

int sem_wait_time(sem_t *sem, int timeout_s, int timeout_ms) // 等待信号量
{
    struct timespec now;
    struct timespec outtime;
    // gettimeofday(&now,NULL);
    clock_gettime(CLOCK_REALTIME, &now);
    outtime.tv_sec = now.tv_sec + timeout_ms / 1000 + timeout_s;
    outtime.tv_nsec = now.tv_nsec + (timeout_ms % 1000) * 1000 * 1000;
    if (outtime.tv_nsec >= 1000000000)
    {
        outtime.tv_nsec -= 1000000000;
        outtime.tv_sec += 1;
    }
    return sem_timedwait(sem, &outtime);
}

int check_list_num(channel_t *chan_ptr)//查通道列表中数据数量
{
    int num = 0;
    list_head_t *tmp_list;
    tmp_list = &chan_ptr->wirte_list;
    pthread_rwlock_wrlock(&chan_ptr->list_rwlock);
    while (tmp_list->next != &chan_ptr->wirte_list)
    {
        tmp_list=tmp_list->next;
        num++;
    }
    pthread_rwlock_unlock(&chan_ptr->list_rwlock);
    proto_syslog(LOG_DEBUG, "%s:channel write list num:%d", chan_ptr->channel, num);
    return num;
}

static int pcs_set_power_tag_set_write_type(char *dev_no)
{
    char dev_func_name[ADAPTER_FUNCTION_NAME_LEN] = {0};
    adapter_function_t *maph = NULL;
    char *function_name[] = {FUNCTION_SET_POWER, FUNCTION_SET_REPOWER};
    for (int i = 0; i < sizeof(function_name) / sizeof(function_name[0]); i++)
    {
        snprintf(dev_func_name, ADAPTER_FUNCTION_NAME_LEN, "%s.%s", dev_no, function_name[i]);
        int ret = hash_intptr_findptr(this_p->func_map_hash, dev_func_name, strlen(dev_func_name), (void **)&maph);
        if ((ret < 0) && (maph == NULL))
        {
            proto_syslog(LOG_ERR, "pcs_set_power_tag_set_write_type: %s not found", dev_func_name);
            continue;
        }
        tag_t *tg = NULL;
        for (int j = 0; j < maph->maps_num; j++)
        {
            adapter_var_map_t *map = &maph->maps[j];
            tg = find_tag_by_dev_and_node(this_p, dev_no, map->tagname);
            if (tg != NULL)
            {
                tg->write_type = 1;
                proto_syslog(LOG_NOTICE, "tag[%s] write_type = 1", tg->dev_tag_name);
            }
        }
    }
    //
    return 0;
}

int set_write_tag_write_type(proto_forward_t *var)
{
    /*
    [
        {
            "dev": "PCS",
            "tags": [
                "WorkMode",
                "StartStopCtr"
            ]
        },
        {
            "dev": "EMS",
            "tags": [
                "SetPower"
            ]
        }
    ]
     */
    const char *cfg_name = "/app/config/write_type_cfg.json";
    char *txt = read_file_data(cfg_name);
    if (txt != NULL)
    {
        cJSON *root = cJSON_Parse(txt);
        free(txt);
        if (root != NULL)
        {
            int len = cJSON_GetArraySize(root);
            cJSON *dev = NULL;
            cJSON *dev_no = NULL;
            cJSON *tags = NULL;
            tag_t *tg = NULL;
            for (int i = 0; i < len; i++)
            {
                dev = cJSON_GetArrayItem(root, i);
                dev_no = cJSON_GetObjectItem(dev, "dev");
                tags = cJSON_GetObjectItem(dev, "tags");
                if ((dev_no == NULL) || (tags == NULL))
                {
                    continue;
                }
                int tags_len = cJSON_GetArraySize(tags);
                cJSON *tag = NULL;
                for (int j = 0; j < tags_len; j++)
                {
                    tag = cJSON_GetArrayItem(tags, j);
                    tg = find_tag_by_dev_and_node(this_p, dev_no->valuestring, tag->valuestring);
                    if (tg != NULL)
                    {
                        tg->write_type = 1;
                        proto_syslog(LOG_NOTICE, "tag[%s] write_type = 1", tg->dev_tag_name);
                    }
                }
            }
            cJSON_Delete(root);
        }
    }
    // 指定PCS写有功和无功为实时类型
    for (size_t i = 0; i < var->channels_size; i++)
    {
        for (size_t j = 0; j < var->channels[i]->devs_size; j++)
        {
            device_t *dev = var->channels[i]->devs[j];
            if (strcmp(dev->dev_type, "PCS") == 0)
            {
                pcs_set_power_tag_set_write_type(dev->no);
            }
        }
    }
    return 0;
}

static int add_write_data_to_list(tag_t *tag, channel_t *chan_ptr)
{
    write_list_t *write_info = NULL;

    if (tag->write_type != 0)
    {
        pthread_rwlock_wrlock(&chan_ptr->list_rwlock);
        if (tag->write_back != NULL) // 已经有写副本
        {

            *tag->write_back = *tag;
        }
        else // 没有写副本
        {
            write_info = calloc(1, sizeof(write_list_t));
            write_info->w_tag = *tag;
            write_info->pw_tag = tag;
            list_add_tail(&write_info->list, &chan_ptr->wirte_list);
            tag->write_back = &write_info->w_tag;
        }
        pthread_rwlock_unlock(&chan_ptr->list_rwlock);
    }
    else
    {
        write_info = calloc(1, sizeof(write_list_t));
        write_info->w_tag = *tag;
        write_info->pw_tag = tag;
        pthread_rwlock_wrlock(&chan_ptr->list_rwlock);
        list_add_tail(&write_info->list, &chan_ptr->wirte_list);
        pthread_rwlock_unlock(&chan_ptr->list_rwlock);
    }
    return 1;
}

void awake_dev_sync(tag_t *tag)
{
    channel_t *chan_ptr = tag->chan_ptr;
    if (add_write_data_to_list(tag, chan_ptr) > 0)
    {
        if (sem_post(&chan_ptr->write_sem) != 0)
        {
            proto_syslog(LOG_ERR, "failed to awake pthread of channle:[%s] .errstr:%s", chan_ptr->channel, strerror(errno));
        }
    }
}

write_list_t* get_data_from_list(channel_t *chan_ptr)  //返回tag*成功 返回NULL列表已经没有项了
{
    write_list_t *write_info = NULL;
    pthread_rwlock_wrlock(&chan_ptr->list_rwlock);
    if(!list_empty(&chan_ptr->wirte_list))
    {
        write_info = list_first_entry(&chan_ptr->wirte_list, typeof(*write_info), list);
        list_del(&write_info->list);
        if (write_info->pw_tag->write_back != NULL)
        {
            write_info->pw_tag->write_back = NULL;
        }
    }
    //proto_syslog(LOG_WARNING, "list empty:%d\n",list_empty(&chan_ptr->wirte_list));
    pthread_rwlock_unlock(&chan_ptr->list_rwlock);
    return write_info;
}

//注册一个数据刷新callback
int register_data_flush_cb_by_tag(proto_forward_t *var,tag_t *tag,tag_get_data_cb tag_get_data_cb,void *context) 
{
    if (tag->tag_value_flushed_cb_num>MAX_CB_NUM)
    {
        proto_syslog(LOG_WARNING, "The TAG's callback must be less than %d!!\n",MAX_CB_NUM);
        return -1;
    }
    tag_cb_t *tag_cb=calloc(sizeof(tag_cb_t),1);
    tag_cb->context = context;
    tag_cb->tag_get_data = tag_get_data_cb;
    
    tag->tag_value_flushed_cb[tag->tag_value_flushed_cb_num++] = tag_cb;
    return 0;
}
int register_data_flush_cb_by_name(proto_forward_t *var,char *name,tag_get_data_cb tag_get_data_cb,void *context)
{
    tag_t *tag_finded = NULL;
    // 找tag
    proto_syslog(LOG_DEBUG, "register cb, tag name :%s \n",name);
    if(name == NULL) return -3;
    if (hash_intptr_findptr(var->tag_map_hash, name, strlen(name), (void **)&tag_finded) < 0)
    {
        proto_syslog(LOG_INFO, "error:not find write tag :%s \n",name);
        return -2;
    }
    return register_data_flush_cb_by_tag(var,tag_finded,tag_get_data_cb,context);
}

//注册数据变化回调
int register_data_collect_cb_by_tag(proto_forward_t *var,tag_t *tag,tag_get_data_cb tag_get_data_cb,void *context) //根据tag注册采集完成回调函数
{
    if (tag->tag_value_changed_cb_num>MAX_CB_NUM)
    {
        proto_syslog(LOG_WARNING, "The TAG's callback must be less than %d!!\n",MAX_CB_NUM);
        return -1;
    }
    tag_cb_t *tag_cb=calloc(sizeof(tag_cb_t),1);
    tag_cb->context = context;
    tag_cb->tag_get_data = tag_get_data_cb;
    
    tag->tag_value_changed_cb[tag->tag_value_changed_cb_num++] = tag_cb;
    return 0;
}

int register_data_collect_cb_by_name(proto_forward_t *var,char *name,tag_get_data_cb tag_get_data_cb,void *context) //根据name注册采集完成回调函数
{
    tag_t *tag_finded = NULL;
    // 找tag
    proto_syslog(LOG_DEBUG, "register cb, tag name :%s \n",name);
    if(name == NULL) return -3;
    if (hash_intptr_findptr(var->tag_map_hash, name, strlen(name), (void **)&tag_finded) < 0)
    {
        proto_syslog(LOG_INFO, "error:not find write tag :%s \n",name);
        return -2;
    }
    return register_data_collect_cb_by_tag(var,tag_finded,tag_get_data_cb,context);
}

/**
 * 统计字符串中分号 ';' 的个数
 * 
 * @param str 输入的字符串
 * @return 分号的数量
 */
int count_semicolons(const char *str) {
    // 检查空指针，防止程序崩溃
    if (str == NULL) {
        return 0;
    }

    int count = 0;
    
    // 遍历字符串直到遇到结束符 '\0'
    while (*str != '\0') {
        if (*str == ';') {
            count++;
        }
        str++;
    }

    return count;
}


static void analysis_user_def(device_t *dev_ptr)  //格式化user_def到user_def_list  
{
    const char s[2] = ";";
    char *token;

    /* 先初始化 */
    dev_ptr->user_def_list = NULL;
    dev_ptr->user_def_list_num = 0;

    /* 没有对应的字段，直接返回 */
    if ((dev_ptr->user_def == NULL) || (dev_ptr->user_def[0] == '\0')) {
        return;
    }

    int maxCount =  count_semicolons(dev_ptr->user_def) + 1;       /* 获取字符串中有几个; */

    dev_ptr->user_def_list = (char **)calloc(maxCount, sizeof(char *));
    if (dev_ptr->user_def_list == NULL) {
        proto_syslog(LOG_ERR, "calloc failed for user_def_list");
        return;
    }

    proto_syslog(LOG_DEBUG, "user_def %s", dev_ptr->user_def);

    char *new_str = strdup(dev_ptr->user_def);
    if (new_str == NULL) {
        free(dev_ptr->user_def_list);
        dev_ptr->user_def_list = NULL;
        proto_syslog(LOG_ERR, "strdup failed");
        return;
    }

    int i = 0;
    token = strtok(new_str, s);
    for (; i < maxCount && token != NULL; token = strtok(NULL, s)) {
        if (token[0] == '\0') {
            continue;
        }
        dev_ptr->user_def_list[i] = token;
        i++;
    }
    if (i == 0) { // 如果一个都没有就释放掉
        free(new_str);
        free(dev_ptr->user_def_list);
        dev_ptr->user_def_list = NULL;
    }
    dev_ptr->user_def_list_num = i;    
}

static void analysis_command_info(proto_ydt1363_poll *ydt1363_poll)  //格式化command_infof到command_info_list  
{
    const char s[2] = "+";
    char *token;
    char *new_str = strdup(ydt1363_poll->command_info);
    proto_syslog(LOG_DEBUG, "command_info %s", ydt1363_poll->command_info);
    token = strtok(new_str, s);
    while (token != NULL)
    {
        ydt1363_poll->command_info_list[ydt1363_poll->command_info_list_num] = token;
        proto_syslog(LOG_DEBUG, "get command_info_list[%d]: %s", ydt1363_poll->command_info_list_num, ydt1363_poll->command_info_list[ydt1363_poll->command_info_list_num]);
        ydt1363_poll->command_info_list_num++;
        token = strtok(NULL, s);
    }
    if (ydt1363_poll->command_info_list_num == 0) //如果一个都没有就释放掉
    {
        free(new_str);
    }
    // free(new_str); //不需要free
}


// char* find_char_in_user_def(device_t *dev_ptr, const char *name) //根据name获取char值从user_def 
// {
//     char *dist_p = NULL;
//     char tmp_p[32] = {0};
//     char *dist_char = NULL;
//     for (size_t i = 0; i < dev_ptr->user_def_list_num; i++)
//     {
//         if ((dist_p = strstr(dev_ptr->user_def_list[i], name)) != NULL)
//         {

//             sscanf(dist_p, "%[^=]=%s", tmp_p, dist_char);
//             proto_syslog(LOG_DEBUG, "find %s %s", name, dist_char);
//         }
//     }
//     return dist_char;
// }
static bool check_proto_change(tag_t *tag)
{
    if(tag==NULL)
    {
        proto_syslog(LOG_WARNING, "tag is NULL");
        return false;
    }
    if (tag->inited!=1)
    {
        tag->inited = 1;
        return true;
    }
    if(tag->data_type == TYPE_TAG_INT)
    {
        if(tag->read_cache.to_int == tag->last_read_cache.to_int)
            return false;
    }
    else 
    {
        if(tag->read_cache.to_float == tag->last_read_cache.to_float)
            return false;
    }
    return true;
}
#ifdef EN_LC
static int tag_sync_to_ua_client(tag_t *tag)
{
#if 0
    UA_Int32 int_value = 0;
    UA_Double double_value = 0;
    UA_Variant variant;
    UA_Variant_init(&variant);
    if (tag->data_type == 0)
    {
        int_value = tag->last_read_cache.to_int;
        UA_Variant_setScalar(&variant, &int_value, &UA_TYPES[UA_TYPES_INT32]);
    }
    else
    {
        double_value = tag->last_read_cache.to_float;
        UA_Variant_setScalar(&variant, &double_value, &UA_TYPES[UA_TYPES_DOUBLE]);
    }
    // 发送写请求
    UA_StatusCode statusCode = UA_Client_writeValueAttribute(this_p->ua_client, tag->node_rid, &variant);
    if (statusCode == UA_STATUSCODE_GOOD)
    {
        printf("Write successful!\n");
    }
    else
    {
        printf("Write failed with status code: %s\n", UA_StatusCode_name(statusCode));
    }
#endif

#if 0
    UA_Int32 int_value = 0;
     UA_Double double_value = 0;
    UA_WriteRequest wReq;
    UA_WriteRequest_init(&wReq);
    wReq.nodesToWrite = UA_WriteValue_new();
    wReq.nodesToWriteSize = 1;
    wReq.nodesToWrite[0].nodeId = tag->node_rid;
    wReq.nodesToWrite[0].attributeId = UA_ATTRIBUTEID_VALUE;
    wReq.nodesToWrite[0].value.hasValue = true;
    wReq.nodesToWrite[0].value.value.storageType = UA_VARIANT_DATA_NODELETE; /* do not free the integer on deletion */
    if (tag->data_type == 0)
    {
        int_value = tag->last_read_cache.to_int;
        wReq.nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_INT32];
        wReq.nodesToWrite[0].value.value.data = &int_value;
    }
    else
    {
        double_value = tag->last_read_cache.to_float;
        wReq.nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_DOUBLE];
        wReq.nodesToWrite[0].value.value.data = &double_value;
    }
    UA_WriteResponse wResp = UA_Client_Service_write(this_p->ua_client, wReq);
    if(wResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
    {
            printf("the new value is: %d ,%f\n", int_value, double_value);
    }
    else
    {
        printf("Write failed with status code: %s\n", UA_StatusCode_name(wResp.responseHeader.serviceResult));
    }
    // UA_WriteRequest_clear(&wReq);
    // UA_WriteResponse_clear(&wResp);
#endif

    return 0;
}
#endif
void reflush_data_up(tag_t* tag) //调用上行注册的回调函数
{
#ifdef EN_LC
    tag_sync_to_ua_client(tag);
#endif
    //数据改变才调用回调
    if (check_proto_change(tag))
    {
        for (size_t i = 0; i < tag->tag_value_changed_cb_num; i++)
        {
            tag_cb_t *tag_cb = tag->tag_value_changed_cb[i];
            tag_cb->tag_get_data(tag_cb->context,tag);
        }
    }
    //数据刷新后的回调
    for (size_t i = 0; i < tag->tag_value_flushed_cb_num; i++)
    {
        tag_cb_t *tag_cb = tag->tag_value_flushed_cb[i];
        tag_cb->tag_get_data(tag_cb->context,tag);
    }
}

int find_int_in_user_def(device_t *dev_ptr, const char *name, int default_val) // //根据name获取int值从user_def
{
    char *dist_p = NULL;
    char tmp_p[32] = {0};
    float dist_float = default_val;
    int dist_int = default_val;
    for (size_t i = 0; i < dev_ptr->user_def_list_num; i++)
    {
        if ((dist_p = strstr(dev_ptr->user_def_list[i], name)) != NULL)
        {
            if (strchr(dev_ptr->user_def_list[i], '.') != NULL)
            {
                sscanf(dist_p, "%[^=]=%f", tmp_p, &dist_float);
                dist_int = dist_float;
            }
            else
            {
                sscanf(dist_p, "%[^=]=%d", tmp_p, &dist_int);
            }
            proto_syslog(LOG_DEBUG, "find %s %d", name, dist_int);
        }
    }
    return dist_int;
}

float find_float_in_user_def(device_t *dev_ptr, const char *name, float default_val) //根据name获取float值从user_def
{
    char *dist_p = NULL;
    char tmp_p[32] = {0};
    float dist_float = default_val;
    int dist_int = default_val;
    for (size_t i = 0; i < dev_ptr->user_def_list_num; i++)
    {
        if ((dist_p = strstr(dev_ptr->user_def_list[i], name)) != NULL)
        {
            if (strchr(dev_ptr->user_def_list[i], '.') != NULL)
            {
                sscanf(dist_p, "%[^=]=%f", tmp_p, &dist_float);
            }
            else
            {
                sscanf(dist_p, "%[^=]=%d", tmp_p, &dist_int);
                dist_float = dist_int;
            }
            proto_syslog(LOG_DEBUG, "find %s %f", name, dist_float);
        }
    }
    return dist_float;
}

static void online_state_change_update(device_t *dev_ptr,int online_state) //变位置刷新
{
    if (dev_ptr->tags.sys_info_tags[DEV_ONLIEN_TAG_INDEX]->force_set_cache.valid == false)
    {
        dev_ptr->tags.sys_info_tags[DEV_ONLIEN_TAG_INDEX]->last_read_cache.to_int = dev_ptr->tags.sys_info_tags[DEV_ONLIEN_TAG_INDEX]->read_cache.to_int;
        dev_ptr->tags.sys_info_tags[DEV_ONLIEN_TAG_INDEX]->read_cache.to_int      = online_state;
        reflush_data_up(dev_ptr->tags.sys_info_tags[DEV_ONLIEN_TAG_INDEX]);
    }
}

void refresh_special_state(device_t *dev_ptr,int now_state,int tag_id) //变位置刷新
{
    if (dev_ptr->tags.sys_info_tags[tag_id]->force_set_cache.valid == false)
    {
        dev_ptr->tags.sys_info_tags[tag_id]->last_read_cache.to_int = dev_ptr->tags.sys_info_tags[tag_id]->read_cache.to_int;
        dev_ptr->tags.sys_info_tags[tag_id]->read_cache.to_int = now_state;
        reflush_data_up(dev_ptr->tags.sys_info_tags[tag_id]);
    }
}

int dev_is_online(device_t *dev_ptr)
{
    return dev_ptr->tags.sys_info_tags[DEV_ONLIEN_TAG_INDEX]->read_cache.to_int;
}

void check_online_state(device_t *dev_ptr) // 检查设备在线状态
{
    int online_state = ONLINE;
    uint64_t refresh_time = clock_get_ms();
    // if ((refresh_time - dev_ptr->dev_data.last_refresh_time) > (dev_ptr->timeout + dev_ptr->min_scan_rate * DEV_OFFLINE_COUNT))
    // 以前状态为超时时间加10倍最小轮询现在直接设置为2分钟判定离线，
    // 为了解决设备由于串口重新映射或者网络延时导致重连就出现设备离线的问题
    if ((refresh_time - dev_ptr->dev_data.last_refresh_time) > (2 * 60 * 1000) || dev_ptr->dev_data.last_refresh_time == 0)
    {
        online_state = OFFLINE;
    }
    online_state_change_update(dev_ptr,online_state);
}

void refresh_online_state(device_t *dev_ptr) // 刷新设备在线时间
{
    int online_state = ONLINE;
    dev_ptr->dev_data.last_refresh_time = clock_get_ms();
    online_state_change_update(dev_ptr,online_state);
}

void set_dev_offline(device_t *dev_ptr) // 直接设置设备离线
{
    int online_state = OFFLINE;
    online_state_change_update(dev_ptr,online_state);
}

int check_online(device_t *dev_ptr) //0离线 1在线
{
    return dev_ptr->tags.sys_info_tags[DEV_ONLIEN_TAG_INDEX]->read_cache.to_int;
}

static int change_float_to_int(double data_f)
{
    int data_tmp = 0;
    data_tmp = data_f;
    if((data_f - data_tmp)>0.5f)
        data_tmp = data_tmp+1;
    proto_syslog(LOG_DEBUG, "double:%f->int:%d \n",data_f,data_tmp);
    return data_tmp;
}


static void write_data_to_tag(int data_type, tag_t *tag, int data_i, double data_f) //给tag赋值
{
    if (data_type == TYPE_TAG_INT)
    {
        if (tag->data_type == TYPE_TAG_INT)
        {
            tag->write_cache.to_int = data_i;
        }
        else
        {
            tag->write_cache.to_float = data_i;
        }
    }
    else
    {
        if (tag->data_type == TYPE_TAG_INT)
        {
            tag->write_cache.to_int = change_float_to_int(data_f);
        }
        else
        {
            tag->write_cache.to_float = data_f;
        }
    }

    if (tag->data_type == TYPE_TAG_INT)
    {
        proto_syslog(LOG_NOTICE, "write tag:%s value:%ld", tag->name, tag->write_cache.to_int);
    }
    else
    {
        proto_syslog(LOG_NOTICE, "write tag:%s value:%f", tag->name, tag->write_cache.to_float);
    }
}

static void force_reflush_data_to_tag(int data_type, tag_t *tag, int data_i, double data_f) //给tag赋值
{
    if (data_type == TYPE_TAG_INT)
    {
        if (tag->data_type == TYPE_TAG_INT)
        {
            tag->force_set_cache.value.to_int = data_i;
        }
        else
        {
            tag->force_set_cache.value.to_float = data_i;
        }
    }
    else
    {
        if (tag->data_type == TYPE_TAG_INT)
        {
            tag->force_set_cache.value.to_int = change_float_to_int(data_f);
        }
        else
        {
            tag->force_set_cache.value.to_float = data_f;
        }
    }

    if (tag->data_type == TYPE_TAG_INT)
    {
        proto_syslog(LOG_NOTICE, "write tag:%s value:%ld\n", tag->name, tag->write_cache.to_int);
    }
    else
    {
        proto_syslog(LOG_NOTICE, "write tag:%s value:%f\n", tag->name, tag->write_cache.to_float);
    }
}

int reflush_data_by_name_force(proto_forward_t *var,char *name,tag_value_t *tag_val) 
{
    tag_t *tag_finded = NULL;
    // 找tag
    if((name == NULL||(tag_val == NULL)||(var == NULL))) return -3;
    if (hash_intptr_findptr(var->tag_map_hash, name, strlen(name), (void **)&tag_finded) < 0)
    {
        return -1;
    }
    tag_finded->force_set_cache.valid = true;
    proto_syslog(LOG_DEBUG, " find force tag:%s\n", tag_finded->name);
    force_reflush_data_to_tag(tag_val->type, tag_finded, tag_val->value.to_int, tag_val->value.to_float);
    return 0;
}

int do_some_action_foreach_tag(proto_forward_t *var,each_tag_cb each_tag_action,void *contex)
{
    int ret = 0;
    for (size_t i = 0; i < var->channels_size; i++)
    {
        for (size_t j = 0; j < var->channels[i]->devs_size; j++)
        {
            for (size_t k = 0; k < var->channels[i]->devs[j]->tags.sys_info_tags_size; k++)
            {
                ret |= each_tag_action(var->channels[i]->devs[j]->tags.sys_info_tags[k],contex);
            }
            for (size_t k = 0; k < var->channels[i]->devs[j]->tags.ro_tags_size; k++)
            {
                ret |= each_tag_action(var->channels[i]->devs[j]->tags.ro_tags[k],contex);
            }
            for (size_t k = 0; k < var->channels[i]->devs[j]->tags.rw_tags_size; k++)
            {
                ret |= each_tag_action(var->channels[i]->devs[j]->tags.rw_tags[k],contex);
            }
        }   
    }
    return ret;
}

int clean_each_tag_force(tag_t *tag_this,void *contex)
{
    tag_this->force_set_cache.valid = false;
    return 0;
}

int clean_force_tag(proto_forward_t *var,char *name) 
{
    if(var == NULL) return -3;
    if (name == NULL)
    {
        do_some_action_foreach_tag(var,clean_each_tag_force,NULL);
    }
    else
    {
        tag_t *tag_finded = NULL;
        if (hash_intptr_findptr(var->tag_map_hash, name, strlen(name), (void **)&tag_finded) < 0)
        {
            return -1;
        }
        tag_finded->force_set_cache.valid = false;
        proto_syslog(LOG_DEBUG, " find tag:%s\n", tag_finded->name);   
    }
    return 0;
}
/**
 * check_tag_by_name
 * 根据 name 判断tag是否存在
 * 
 * //返回值 true 存在 false不存在
 *
 * 
*/
bool check_tag_by_name(proto_forward_t *var,char* name)
{
    tag_t *tag_finded = NULL;
    if((name == NULL)||(var == NULL)) return -3;
    if (hash_intptr_findptr(var->tag_map_hash, name, strlen(name), (void **)&tag_finded) < 0)
        return false;
    proto_syslog(LOG_DEBUG, " find read tag:%s\n", tag_finded->name);
    return true;
}

/**
 * read_data_form_tag_by_name
 * 根据 name 读取tag值
 * 
 * //返回值 -1 未找到tag
 * data_type TYPE_TAG_INT 和TYPE_TAG_FLOAT 写入数据的类型 和tag的类型无关 会从tag的数据类型 自动转化需要的类型
 *
 * 
*/
int read_data_form_tag_by_name(proto_forward_t *var,char* name,int data_type,tag_value_t *tag_val)
{
    tag_t *tag_finded = NULL;
    if((name == NULL||(tag_val == NULL)||(var == NULL))) return -3;
    if (hash_intptr_findptr(var->tag_map_hash, name, strlen(name), (void **)&tag_finded) < 0)
    {
        proto_syslog(LOG_INFO, "error:not find read tag :%s \n",name);
        return -1;
    }
        
    // proto_syslog(LOG_DEBUG, " find read tag:%s\n", tag_finded->name);
    if(data_type == TYPE_TAG_INT)
    {
        if (tag_finded->data_type == TYPE_TAG_INT)
            tag_val->value.to_int = tag_finded->read_cache.to_int;                   
        else                                                
            tag_val->value.to_int = change_float_to_int(tag_finded->read_cache.to_float); 
        tag_val->type = TYPE_TAG_INT;
    }                          
    else
    {
        if (tag_finded->data_type == TYPE_TAG_INT)
            tag_val->value.to_float = tag_finded->read_cache.to_int;                      
        else                                                
            tag_val->value.to_float = tag_finded->read_cache.to_float; 
        tag_val->type = TYPE_TAG_FLOAT;
    }  
    return 0;
}

/**
 * 从tag读取数据
 * 
*/
int read_data_form_tag_by_p(tag_t *tag_finded,int data_type,tag_value_t *tag_val,float scale)
{
    if(data_type == TYPE_TAG_INT)
    {
        if(tag_finded == NULL)
        {
            proto_syslog(LOG_NOTICE, "read tag is NULL set value 0");
            tag_val->value.to_int = 0;
            return -1;
        }
        if (tag_finded->data_type == TYPE_TAG_INT)
        {
            if(check_float_is_1(scale))
                tag_val->value.to_int = tag_finded->read_cache.to_int;
            else
                tag_val->value.to_int = tag_finded->read_cache.to_int * scale; 
        }         
        else      
        {
            if(check_float_is_1(scale))
                tag_val->value.to_int = change_float_to_int(tag_finded->read_cache.to_float); 
            else
                tag_val->value.to_int = change_float_to_int(tag_finded->read_cache.to_float *scale);          
        }                                          
        tag_val->type = TYPE_TAG_INT;
        proto_syslog(LOG_NOTICE, "read %s value:%ld",tag_finded->name,tag_val->value.to_int); 
    }                          
    else
    {
        if(tag_finded == NULL)
        {
            proto_syslog(LOG_NOTICE, "read tag is NULL set value 0.0f");
            tag_val->value.to_float = 0.0f;
            return -1;
        }
        if (tag_finded->data_type == TYPE_TAG_INT)
            tag_val->value.to_float = tag_finded->read_cache.to_int * scale;                      
        else                                                
            tag_val->value.to_float = tag_finded->read_cache.to_float * scale; 
        tag_val->type = TYPE_TAG_FLOAT;
        proto_syslog(LOG_NOTICE, "read %s value:%f",tag_finded->name,tag_val->value.to_float); 
    }  
    return 0;
}

static int tag_value_changed(tag_t *tag, tag_value_t *tag_val)
{
    if(tag_val->type == TYPE_TAG_INT)
    {
        if (tag->read_cache.to_int != tag_val->value.to_int)
        {
            return 1;
        }
    }
    else
    {
        if (tag->read_cache.to_float != tag_val->value.to_float) // 不知道精度，所以浮点型必须完全相对才算相等
        {
            return 1;
        }
    }
    return 0;
}

/**
 * 写一个数据到tag
*/
int write_data_to_tag_by_p(tag_t *tag_finded,tag_value_t *tag_val,float scale) 
{
    if(tag_finded == NULL)
    {
        proto_syslog(LOG_WARNING, "tag is NULL don't write");
        return 0;
    }
#if 1
    // 如果队列消息太多就不更改tag的数据
    if (check_list_num(tag_finded->chan_ptr) > MAX_LIST_DATA_NUM)
    {
        proto_syslog(LOG_WARNING, "error:[%s] list have to many message write !!", tag_finded->name);
        return -2;
    }
#else
    int times = 0;
    
    while (check_list_num(tag_finded->chan_ptr) > MAX_LIST_DATA_NUM)
    {
        times++;
        if(times>MAX_WTIRE_TIMES)
        {
            proto_syslog(LOG_WARNING, "error:[%s] list have to many message write error!!",tag_finded->disp_name);
            return -2;
        }
        proto_syslog(LOG_WARNING, "warning:[%s] list have to many message write wait next write!!",tag_finded->disp_name);
        usleep(1000*10);
    }
#endif
    if (tag_val->type == TYPE_TAG_INT)
    {
        if(!check_float_is_1(scale))
            tag_val->value.to_int = tag_val->value.to_int / scale;  
    }         
    else      
    {
        if(!check_float_is_1(scale))
            tag_val->value.to_float = tag_val->value.to_float /scale; 
    }  
    if (tag_finded->force_set_cache.valid == true)
    {
        proto_syslog(LOG_WARNING, "tag %s have force set!",tag_finded->disp_name);
        return -3;
    }
    // 写数据
    write_data_to_tag(tag_val->type, tag_finded, tag_val->value.to_int, tag_val->value.to_float);
        
    // 写完后进行一些操作
    awake_dev_sync(tag_finded);
    return 0;
}

/**
 * write_data_to_tag_by_name
 * 根据 name 写入tag值
 * 
 * //返回值 -1 未找到tag -2队列数据太多
 * data_type TYPE_TAG_INT 和TYPE_TAG_FLOAT 写入数据的类型 和tag的类型无关 会自动转化为tag的数据类型的数据
 * 
*/
int write_data_to_tag_by_name(proto_forward_t *var,char *name,tag_value_t *tag_val) 
{
    tag_t *tag_finded = NULL;
    // 找tag
    if((name == NULL||(tag_val == NULL)||(var == NULL))) return -3;
    if (hash_intptr_findptr(var->tag_map_hash, name, strlen(name), (void **)&tag_finded) < 0)
    {
        proto_syslog(LOG_INFO, "error:not find read tag :%s \n",name);
        return -1;
    }
    if ((tag_val->no_change_no_set != 0) && (0 == tag_value_changed(tag_finded, tag_val)))
    {
        return 0;
    }
    // 如果队列消息太多就不更改tag的数据
#if 1
    if (check_list_num(tag_finded->chan_ptr) > MAX_LIST_DATA_NUM)
    {
        proto_syslog(LOG_WARNING, "error:[%s] list have to many message write !!", tag_finded->name);
        return -2;
    }
#else
    int times = 0;
    while (check_list_num(tag_finded->chan_ptr) > MAX_LIST_DATA_NUM)
    {
        times++;
        if(times>MAX_WTIRE_TIMES)
        {
            proto_syslog(LOG_WARNING, "error:[%s] list have to many message write error!!",tag_finded->disp_name);
            return -2;
        }
        proto_syslog(LOG_WARNING, "warning:[%s] list have to many message write wait next write!!",tag_finded->disp_name);
        usleep(1000*10);
    }
#endif
    if (tag_finded->force_set_cache.valid == true)
    {
        proto_syslog(LOG_WARNING, "tag %s have force set!",tag_finded->disp_name);
        return -3;
    }
    // 写数据
    write_data_to_tag(tag_val->type, tag_finded, tag_val->value.to_int, tag_val->value.to_float);
    // 写完后进行一些操作
    awake_dev_sync(tag_finded);
    return 0;
}

tag_t *find_tag_by_name(proto_forward_t *var,char *name) 
{
    tag_t *tag_finded = NULL;
    // 找tag
    if((name == NULL||(var == NULL))) return NULL;
    if (hash_intptr_findptr(var->tag_map_hash, name, strlen(name), (void **)&tag_finded) < 0)
    {
        proto_syslog(LOG_INFO, "error:not find tag :%s \n",name);
        return NULL;
    }
    proto_syslog(LOG_DEBUG, " find tag:%s\n", tag_finded->name);
    return tag_finded;
}

tag_t *find_tag_by_dev_and_node(proto_forward_t *var, char *dev_no, char *node)
{
    tag_t *tag_finded = NULL;
    char tag_name[TAG_NAME_LEN] = {0};
    snprintf(tag_name, sizeof(tag_name), "%s.%s", dev_no, node);
    if (hash_intptr_findptr(var->tag_map_hash, tag_name, strlen(tag_name), (void **)&tag_finded) < 0)
    {
        proto_syslog(LOG_INFO, "error:not find tag :%s \n", tag_name);
        return NULL;
    }
    proto_syslog(LOG_DEBUG, " find tag:%s\n", tag_finded->name);
    return tag_finded;
}
// 临时解决策略丢失问题，其他地方非必要不要调用这个函数
int write_int_to_tag_cache_directly(char *dev_no, char *node, long value)
{
    pcs_ctrl_var_t *var = get_pcs_ctrl_var();
    tag_t *tag = find_tag_by_dev_and_node(var->device_layer_ptr, dev_no, node);
    if (tag == NULL)
    {
        proto_syslog(LOG_INFO, "error:not find tag :%s \n", node);
        return -1;
    }
    if (tag->data_type == TYPE_TAG_INT)
    {
        tag->last_read_cache.to_int = tag->read_cache.to_int;
        tag->read_cache.to_int = value;
    }
    else
    {
        tag->last_read_cache.to_float = tag->read_cache.to_float;
        tag->read_cache.to_float = (double)value;
    }
    return 0;
}

static void get_tag_general_info(int num, cJSON *dev_list_item, reg_info_t *reg_info)
{
    cJSON * handle;
    const char * hstr = NULL;

    GET_JSON_VALUE_STRING(dev_list_item, "tag", reg_info->name);
    // proto_syslog(LOG_DEBUG, "tag:%s",ptr->write_tags[ptr->write_tags_size]->write_reg.name);

    GET_JSON_VALUE_STRING(dev_list_item, "name", reg_info->disp_name);

    GET_JSON_VALUE_INT(dev_list_item, "primary", reg_info->primary);
    // proto_syslog(LOG_DEBUG, "primary:%d",reg_info.primary);
    char Property[32] = {0};
    GET_JSON_VALUE_STRING(dev_list_item, "property", Property);

    reg_info->funptr = NULL;
    // parse function handle string
    handle = cJSON_GetObjectItem(dev_list_item, "handle");
    if (handle != NULL && cJSON_IsString(handle))
        hstr = handle->valuestring;

    // find handler function pointer via `hstr
    if (hstr && hstr[0]) {
        int found = 0;
        for (int i = 0; handle_fptrs[i].funname && handle_fptrs[i].funname[0] && handle_fptrs[i].funptr; i++)
        {
            if (strcmp(handle_fptrs[i].funname, hstr) == 0) {
                found = 1;
                reg_info->funptr = handle_fptrs[i].funptr;
                break;
            }
        }
        proto_syslog(LOG_INFO, "handle function found for %s: %d, %p\n", reg_info->name, found, (void *) reg_info->funptr);
    }
    
    reg_info->property = get_property_by_string(Property);
    proto_syslog(LOG_DEBUG, "property :%d",reg_info->property);
    cJSON *__item = cJSON_GetObjectItem(dev_list_item, "offset");
    if (__item)
    {
        reg_info->offset = __item->valuedouble;
        // proto_syslog(LOG_DEBUG, "offset:%f",reg_info.offset);
    }
    __item = cJSON_GetObjectItem(dev_list_item, "no_snapshoot");
    if (__item)
    {
        reg_info->no_snapshoot = __item->valueint;
        // proto_syslog(LOG_DEBUG, "offset:%f",reg_info.offset);
    }
    __item = cJSON_GetObjectItem(dev_list_item, "scale");
    if (__item)
    {
        reg_info->scale = __item->valuedouble;
        // proto_syslog(LOG_DEBUG, "scale:%f",reg_info.scale);
    }
    
    __item = cJSON_GetObjectItem(dev_list_item, "history");
    if (__item)
    {
        reg_info->history_type = get_history_type_by_string(__item->valuestring);
    }

    __item = cJSON_GetObjectItem(dev_list_item, "desc");
    if (__item)
    {
        reg_info->desc = (strlen(__item->valuestring) > 2) ? strdup(__item->valuestring) : NULL;
    }
    

    __item = cJSON_GetObjectItem(dev_list_item, "type");
    if (__item)
    {
        reg_info->data_type = get_tag_type_by_string(__item->valuestring);
    }

    __item = cJSON_GetObjectItem(dev_list_item, "extra");
    if (__item)
    {
        int extra_cnt = cJSON_GetArraySize(__item);
        for (size_t j = 0; j < MIN(extra_cnt, MAX_EXTRA_NUM); j++)
        {
            cJSON *p = cJSON_GetArrayItem(__item, j);
            if (p != NULL)
            {
                reg_info->extra[j] = strdup(p->valuestring);
                if (reg_info->extra[j] != NULL)
                {
                    reg_info->extra_num++;
                }
            }
        }
    }

    proto_syslog(LOG_DEBUG, "num :%d tag:%s primary:%d offset:%f scale:%f, hostory:%d, desc:%s", num, reg_info->name,
              reg_info->primary,
              reg_info->offset,
              reg_info->scale,
              reg_info->history_type,
              reg_info->desc);
    for (int j = 0; j < reg_info->extra_num; j++)
    {
        proto_syslog(LOG_DEBUG, "extra[%d]:%s", j, reg_info->extra[j]);
    }
}

can_parser_info_t *creat_can_parser_info(cJSON *prot_info_list)
{
    can_parser_info_t *can_info = calloc(sizeof(can_parser_info_t), 1);
    if (can_info == NULL)
    {
        proto_syslog(LOG_ERR, "calloc err for can_info");
        return NULL;
    }
    else
    {
        cJSON *id = cJSON_GetArrayItem(prot_info_list, 0);
        cJSON *index = cJSON_GetArrayItem(prot_info_list, 1);
        cJSON *type = cJSON_GetArrayItem(prot_info_list, 2);
        cJSON *order = cJSON_GetArrayItem(prot_info_list, 3);
        cJSON *bit_pos = cJSON_GetArrayItem(prot_info_list, 4);
        cJSON *bit_len = cJSON_GetArrayItem(prot_info_list, 5);
        cJSON *reverse = cJSON_GetArrayItem(prot_info_list, 6);
        cJSON *data_target = cJSON_GetArrayItem(prot_info_list, 7);
        cJSON *data_mask = cJSON_GetArrayItem(prot_info_list, 8);
        cJSON *send_id = cJSON_GetArrayItem(prot_info_list, 9);
        //
        can_info->id = analysis_addr(id->valuestring);
        if (strlen(id->valuestring) > 5)
        {
            can_info->id |= 0x80000000;
        }
        can_info->byte_index = index->valueint;
        can_info->data_type = get_type_by_string(type->valuestring);
        can_info->data_order = get_order_by_string(order->valuestring);
        can_info->bit_pos = bit_pos->valueint;
        can_info->bit_len = bit_len->valueint;
        can_info->reverse = reverse->valueint;
        //
        if(data_target != NULL)
        {
            if (strstr(data_target->valuestring, "0x") == data_target->valuestring)
            {
                sscanf(data_target->valuestring,"%"PRIx64, &can_info->data_target);
            }
            else if (strstr(data_target->valuestring, "0X") == data_target->valuestring)
            {
                sscanf(data_target->valuestring,"%"PRIX64, &can_info->data_target);
            }
            else
            {
                can_info->data_target = 0;
            }
            can_info->data_target = htobe64(can_info->data_target);
        }
        if(data_mask != NULL)
        {
            if (strstr(data_mask->valuestring, "0x") == data_mask->valuestring)
            {
                sscanf(data_mask->valuestring,"%"PRIx64, &can_info->data_mask);
            }
            else if (strstr(data_mask->valuestring, "0X") == data_mask->valuestring)
            {
                sscanf(data_mask->valuestring,"%"PRIX64, &can_info->data_mask);
            }
            else
            {
                can_info->data_mask = 0;
            }
            can_info->data_mask = htobe64(can_info->data_mask);
        }
        if (send_id != NULL)
        {
            can_info->send_id = analysis_addr(send_id->valuestring);
            if (strlen(send_id->valuestring) > 5)
            {
                can_info->send_id |= 0x80000000;
            }
        }
        else
        {
            can_info->send_id = CAN_ERR_ID;
        }
        //
        proto_syslog(LOG_DEBUG, "can_info:id=0x%08x, index=%d, type=%d, order=%d, bit_pos=%d, bit_len=%d, reverse=%d, data_target=0x%016"PRIx64", data_mask=0x%016"PRIx64", send_id=0x%08x",
                     can_info->id, can_info->byte_index, can_info->data_type, can_info->data_order, can_info->bit_pos, can_info->bit_len, can_info->reverse, can_info->data_target, can_info->data_mask, can_info->send_id);
    }

    return can_info;
}

custom_parser_info_t *creat_custom_parser_info(cJSON *prot_info_list)
{
    custom_parser_info_t *custom_info = calloc(sizeof(custom_parser_info_t), 1);
    if (custom_info == NULL)
    {
        proto_syslog(LOG_ERR, "calloc err for custom_info");
        return NULL;
    }
    else
    {
        cJSON *id = cJSON_GetArrayItem(prot_info_list, 0);
        cJSON *index = cJSON_GetArrayItem(prot_info_list, 1);
        cJSON *type = cJSON_GetArrayItem(prot_info_list, 2);
        cJSON *order = cJSON_GetArrayItem(prot_info_list, 3);
        cJSON *bit_pos = cJSON_GetArrayItem(prot_info_list, 4);
        cJSON *bit_len = cJSON_GetArrayItem(prot_info_list, 5);
        cJSON *reverse = cJSON_GetArrayItem(prot_info_list, 6);
        //
        custom_info->id = analysis_addr(id->valuestring);
        custom_info->byte_index = index->valueint;
        custom_info->data_type = get_type_by_string(type->valuestring);
        custom_info->data_order = get_order_by_string(order->valuestring);
        custom_info->bit_pos = bit_pos->valueint;
        custom_info->bit_len = bit_len->valueint;
        custom_info->reverse = reverse->valueint;
        //
        proto_syslog(LOG_DEBUG, "custom_info:id=0x%08x, index=%d, type=%d, order=%d, bit_pos=%d, bit_len=%d, reverse=%d",
                     custom_info->id, custom_info->byte_index, custom_info->data_type, custom_info->data_order, custom_info->bit_pos, custom_info->bit_len, custom_info->reverse);
    }

    return custom_info;
}

ydt1363_parser_info_t *creat_ydt1363_parser_info(cJSON *prot_info_list)
{
    ydt1363_parser_info_t *ydt1363_info = calloc(sizeof(ydt1363_parser_info_t), 1);
    if (ydt1363_info == NULL)
    {
        proto_syslog(LOG_ERR, "calloc err for ydt1363_info");
        return NULL;
    }
    else
    {
        cJSON *cid2 = cJSON_GetArrayItem(prot_info_list, 0);
        cJSON *offset = cJSON_GetArrayItem(prot_info_list, 1);
        cJSON *type = cJSON_GetArrayItem(prot_info_list, 2);
        cJSON *order = cJSON_GetArrayItem(prot_info_list, 3);
        cJSON *bit_st = cJSON_GetArrayItem(prot_info_list, 4);
        cJSON *bit_len = cJSON_GetArrayItem(prot_info_list, 5);
        cJSON *reversal = cJSON_GetArrayItem(prot_info_list, 6);
        cJSON *item = cJSON_GetArrayItem(prot_info_list, 7);
        cJSON *id = cJSON_GetArrayItem(prot_info_list, 8);
        //
        ydt1363_info->cid2 = analysis_addr(cid2->valuestring);
        ydt1363_info->offset = offset->valueint;
        ydt1363_info->data_type = get_type_by_string(type->valuestring);
        ydt1363_info->data_order = get_order_by_string(order->valuestring);
        ydt1363_info->bit_st = bit_st->valueint;
        ydt1363_info->bit_len = bit_len->valueint;
        ydt1363_info->reversal = reversal->valueint;
        if (!item){
            ydt1363_info->item = strdup("");
        }
        else{
            ydt1363_info->item = strdup(item->valuestring);
        }
        if (!id){
            ydt1363_info->id = 0x00;
        }
        else{
            ydt1363_info->id = analysis_addr(id->valuestring);
        }        
        //
        proto_syslog(LOG_DEBUG, "ydt1363_info: cid2=0x%02x, offset=%d, type=%d, order=%d, bit_st=%d, bit_len=%d, reversal=%d",
                     ydt1363_info->cid2, ydt1363_info->offset, ydt1363_info->data_type, ydt1363_info->data_order, ydt1363_info->bit_st, ydt1363_info->bit_len, ydt1363_info->reversal);
    }

    return ydt1363_info;
}

static template_t *get_read_reg(cJSON *root, device_t *dev,int prot_type, template_t *ptr,char *temp_name)
{
    cJSON *read_reg = cJSON_GetObjectItemCaseSensitive(root, "read_reg");
    if (!read_reg)
    {
        proto_syslog(LOG_ERR, "get_read_reg error");
        BUSINESS_LOG(BLOG_NOTICE, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 缺少 read_reg",temp_name);
        goto out;
    }

    proto_syslog(LOG_DEBUG, "READ_TAGS:");
    int read_reg_cnt = cJSON_GetArraySize(read_reg);
    for (int i = 0; i < read_reg_cnt; i++)
    {
        if (ptr->read_tags_size >= RO_TAGS_MAX)
        {
            proto_syslog(LOG_ERR, "read_reg_cnt overflow");
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位超过最大数量[%d]",temp_name,RO_TAGS_MAX);
            goto out;
        }

        cJSON *dev_list_item = cJSON_GetArrayItem(read_reg, i);
        cJSON *prot_info_list = cJSON_GetObjectItemCaseSensitive(dev_list_item, "prot");
        if (prot_info_list == NULL)
        {
            proto_syslog(LOG_ERR, "get_read_reg error[%d]", i);
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条缺少prot",temp_name,i+1);

            continue;
        }

        // GET_JSON_VALUE_STRING(dev_list_item, "tag", ptr->read_tags[ptr->read_tags_size]->read_reg.tag);
        // proto_syslog(LOG_DEBUG, "tag:%s",ptr->read_tags[ptr->read_tags_size]->read_reg.tag);
        if (ptr->read_tags[ptr->read_tags_size] == NULL)
            ptr->read_tags[ptr->read_tags_size] = calloc(sizeof(read_reg_info_t), 1);

        get_tag_general_info(i, dev_list_item, &ptr->read_tags[ptr->read_tags_size]->read_reg);
        if (prot_info_list != NULL)
        {
        
        int prot_info_cnt = cJSON_GetArraySize(prot_info_list);
        // proto_syslog(LOG_DEBUG, "prot_info_cnt:%d",prot_info_cnt);
        switch (prot_type)
        {
        case PROTOCOL_TYPE_NONE:
            break;

        case PROTOCOL_TYPE_SCRIPT:
            break;

        case PROTOCOL_TYPE_MODBUS_RTU:
        case PROTOCOL_TYPE_MODBUS_TCP:
        case PROTOCOL_TYPE_MODBUS_RTU_RCRC:
        {
            modbus_read_reg_info_t *reg_info = NULL;
            reg_info = calloc(sizeof(modbus_read_reg_info_t), 1);
            reg_info->head = 1;
            cJSON *p0 = cJSON_GetArrayItem(prot_info_list, 0);
            cJSON *p1 = cJSON_GetArrayItem(prot_info_list, 1);
            cJSON *p2 = cJSON_GetArrayItem(prot_info_list, 2);
            cJSON *p3 = cJSON_GetArrayItem(prot_info_list, 3);
            cJSON *p4 = cJSON_GetArrayItem(prot_info_list, 4);
            cJSON *p5 = cJSON_GetArrayItem(prot_info_list, 5);
            cJSON *p6 = cJSON_GetArrayItem(prot_info_list, 6);
            if (!p0 || !p1 || !p2 || !p3 || !p4 || !p5 || !p6)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                proto_syslog(LOG_WARNING, "invalid read reg :%d", i);
                continue;
            }
            reg_info->func_code = p0->valueint;
            // proto_syslog(LOG_DEBUG, "func_code:%d",reg_info->func_code);
            int addr_tmp = analysis_addr(p1->valuestring) + dev->base_addr;
            reg_info->reg_addr = (addr_tmp > MODBUS_MAX_POINT_ADDR)?MODBUS_MAX_POINT_ADDR:addr_tmp;
            // proto_syslog(LOG_DEBUG, "reg_addr_s:%s",p1->valuestring);
            // proto_syslog(LOG_DEBUG, "reg_addr:0x%04x",reg_info->reg_addr);
            reg_info->data_type = get_type_by_string(p2->valuestring);
            // proto_syslog(LOG_DEBUG, "data_type:%d",reg_info->data_type);
            reg_info->data_order = get_order_by_string(p3->valuestring);
            // proto_syslog(LOG_DEBUG, "data_order:%d",reg_info->data_order);
            reg_info->bit_st = p4->valueint;
            // proto_syslog(LOG_DEBUG, "bit_st:%d",reg_info->bit_st);
            reg_info->bit_len = p5->valueint;
            // proto_syslog(LOG_DEBUG, "bit_len:%d",reg_info->bit_len);
            reg_info->reversal = p6->valueint;
            // proto_syslog(LOG_DEBUG, "reversal:%d",reg_info->reversal);

            ptr->read_tags[ptr->read_tags_size]->read_reg.proto_ptr = reg_info;
            proto_syslog(LOG_DEBUG, "******func_code:%d, reg_addr:0x%04x data_type:%d data_order:%d bit_st:%d bit_len:%d reversal:%d",
                      reg_info->func_code, reg_info->reg_addr, reg_info->data_type,
                      reg_info->data_order, reg_info->bit_st, reg_info->bit_len, reg_info->reversal);
        };
        break;

        case PROTOCOL_TYPE_DLT645_2007:
        case PROTOCOL_TYPE_DLT645_1997:
        {
            if (prot_info_cnt != 2)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                proto_syslog(LOG_ERR, "dlt 645 read info cfg error");
                if (ptr->read_tags[ptr->read_tags_size] != NULL)
                {
                    free(ptr->read_tags[ptr->read_tags_size]);
                    ptr->read_tags[ptr->read_tags_size] = NULL;
                    continue;
                }
            }
            else
            {
                dlt645_rw_info_t *dlt645_info = calloc(sizeof(dlt645_rw_info_t), 1);
                if (dlt645_info == NULL)
                {
                    proto_syslog(LOG_ERR, "calloc err for dlt645_info");
                }
                else
                {
                    cJSON *data_id = cJSON_GetArrayItem(prot_info_list, 0);
                    cJSON *index = cJSON_GetArrayItem(prot_info_list, 1);
                    dlt645_info->data_id = analysis_addr(data_id->valuestring);

                    dlt645_info->index = index->valueint;
                    ptr->read_tags[ptr->read_tags_size]->read_reg.proto_ptr = dlt645_info;
                    proto_syslog(LOG_DEBUG, "dlt645_info[%d]:data_id=0x%x, index=%d", i, dlt645_info->data_id, dlt645_info->index);
                }
            }
        }
        break;
        case PROTOCOL_TYPE_UPS:
        {
            if (prot_info_cnt != 5)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                proto_syslog(LOG_ERR, "ups read info cfg error");
                if (ptr->read_tags[ptr->read_tags_size] != NULL)
                {
                    free(ptr->read_tags[ptr->read_tags_size]);
                    ptr->read_tags[ptr->read_tags_size] = NULL;
                    continue;
                }
            }
            else
            {
                ups_read_info_t *ups_info = calloc(sizeof(ups_read_info_t), 1);
                if (ups_info == NULL)
                {
                    proto_syslog(LOG_ERR, "calloc err for ups_info");
                }
                else
                {
                    cJSON *cmd = cJSON_GetArrayItem(prot_info_list, 0);
                    cJSON *type = cJSON_GetArrayItem(prot_info_list, 1);
                    cJSON *pos = cJSON_GetArrayItem(prot_info_list, 2);
                    cJSON *bit_pos = cJSON_GetArrayItem(prot_info_list, 3);
                    //
                    ups_info->cmd = strdup(cmd->valuestring);
                    ups_info->type = analysis_ups_type(type->valuestring);
                    ups_info->pos = pos->valueint;
                    ups_info->bit_pos = bit_pos->valueint;
                    //
                    ptr->read_tags[ptr->read_tags_size]->read_reg.proto_ptr = ups_info;
                    proto_syslog(LOG_DEBUG, "ups_info[%d]:cmd=%s, type=%d, pos=%d, bit_pos=%d", \
                    i, ups_info->cmd, ups_info->type, ups_info->pos, ups_info->bit_pos);
                }
            }
        }break;

        case PROTOCOL_TYPE_BMSER_BIN:
        {
            if (prot_info_cnt != 6)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                proto_syslog(LOG_ERR, "ups read info cfg error");
                if (ptr->read_tags[ptr->read_tags_size] != NULL)
                {
                    free(ptr->read_tags[ptr->read_tags_size]);
                    ptr->read_tags[ptr->read_tags_size] = NULL;
                    continue;
                }
            }
            else
            {
                bmser_parser_info_t *bmser_info = calloc(sizeof(bmser_parser_info_t), 1);
                if (bmser_info == NULL)
                {
                    proto_syslog(LOG_ERR, "calloc err for bmser_info");
                }
                else
                {
                    cJSON *addr = cJSON_GetArrayItem(prot_info_list, 0);
                    cJSON *sub_id = cJSON_GetArrayItem(prot_info_list, 1);
                    cJSON *type = cJSON_GetArrayItem(prot_info_list, 2);
                    cJSON *bit_pos = cJSON_GetArrayItem(prot_info_list, 3);
                    cJSON *bit_len = cJSON_GetArrayItem(prot_info_list, 4);
                    cJSON *reverse = cJSON_GetArrayItem(prot_info_list, 5);
                    //
                    bmser_info->block_id = analysis_addr(addr->valuestring);
                    bmser_info->sub_id = analysis_addr(sub_id->valuestring);
                    bmser_info->type = bmser_protocol_to_type(type->valuestring);
                    bmser_info->bit_pos = bit_pos->valueint;
                    bmser_info->bit_len = bit_len->valueint;
                    bmser_info->reverse = reverse->valueint;
                    bmser_info->byte_index = -1;
                    //
                    ptr->read_tags[ptr->read_tags_size]->read_reg.proto_ptr = bmser_info;
                    //
                    proto_syslog(LOG_DEBUG, "bmser_info[%d]:block_id=0x%08x, sub_id=%d, type=%d, bit_pos=%d, bit_len=%d, reverse=%d", \
                    i, bmser_info->block_id, bmser_info->sub_id, bmser_info->type, bmser_info->bit_pos, bmser_info->bit_len, bmser_info->reverse);
                }
            }

        }break;

        case PROTOCOL_TYPE_CAN:
        {
            if (prot_info_cnt < 7)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                proto_syslog(LOG_ERR, "can read info cfg error");
                if (ptr->read_tags[ptr->read_tags_size] != NULL)
                {
                    free(ptr->read_tags[ptr->read_tags_size]);
                    ptr->read_tags[ptr->read_tags_size] = NULL;
                    continue;
                }
            }
            else
            {
                can_parser_info_t *can_info = creat_can_parser_info(prot_info_list);
                if (can_info == NULL)
                {
                    proto_syslog(LOG_ERR, "creat can_parser_info err");
                }
                else
                {
                    ptr->read_tags[ptr->read_tags_size]->read_reg.proto_ptr = can_info;
                }
            }
        }
        break;

        case PROTOCOL_TYPE_IEC104_CLIENT:
        {
            int addr, type;
            cJSON * jaddr, * jtype;
            iec104_reg_info_t * info104;

            addr = type = -1;
            info104 = (iec104_reg_info_t *) calloc(0x1, sizeof(*info104));
            jaddr = cJSON_GetArrayItem(prot_info_list, 0);
            jtype = cJSON_GetArrayItem(prot_info_list, 1);
            if (jaddr && cJSON_IsString(jaddr) && jaddr->valuestring)
                addr = (int) strtol(jaddr->valuestring, NULL, 0);
            if (jtype && cJSON_IsString(jtype) && jtype->valuestring) {
                if (strcmp(jtype->valuestring, "bool") == 0)
                    type = C_SC_NA_1;
                else if (strcmp(jtype->valuestring, "int16") == 0)
                    type = C_SE_NB_1;
                else if (strcmp(jtype->valuestring, "float") == 0)
                    type = C_SE_NC_1;
                else if (strcmp(jtype->valuestring, "floatn") == 0)
                    type = C_SE_NA_1;
                else {
                    proto_syslog(LOG_ERR, "invalid IEC104 type: %s", jtype->valuestring);
                }
            }

            if (info104 != NULL) {
                info104->ioa_addr = addr;
                info104->ioa_type = type;
                ptr->read_tags[ptr->read_tags_size]->read_reg.proto_ptr = info104;
            }

            break;
        }
		
        case PROTOCOL_TYPE_CUSTOM:
        {
            if (prot_info_cnt != 7)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                proto_syslog(LOG_ERR, "custom read info cfg error");
                if (ptr->read_tags[ptr->read_tags_size] != NULL)
                {
                    free(ptr->read_tags[ptr->read_tags_size]);
                    ptr->read_tags[ptr->read_tags_size] = NULL;
                    continue;
                }
            }
            else
            {
                custom_parser_info_t *custom_info = creat_custom_parser_info(prot_info_list);
                if (custom_info == NULL)
                {
                    proto_syslog(LOG_ERR, "creat custom_parser_info err");
                }
                else
                {
                    ptr->read_tags[ptr->read_tags_size]->read_reg.proto_ptr = custom_info;
                }
            }
        }
        break;

        case PROTOCOL_TYPE_YDT1363_2014:
        {
            if (prot_info_cnt < 7)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                proto_syslog(LOG_ERR, "ydt1363 read reg info cfg error");
                if (ptr->read_tags[ptr->read_tags_size] != NULL)
                {
                    free(ptr->read_tags[ptr->read_tags_size]);
                    ptr->read_tags[ptr->read_tags_size] = NULL;
                    continue;
                }
            }
            else
            {
                ydt1363_parser_info_t *ydt1363_info = creat_ydt1363_parser_info(prot_info_list);
                if (ydt1363_info == NULL)
                {
                    proto_syslog(LOG_ERR, "creat ydt1363_info err");
                }
                else
                {
                    ptr->read_tags[ptr->read_tags_size]->read_reg.proto_ptr = ydt1363_info;
                }
            }
        }
        break;
        case PROTOCOL_TYPE_EMS:
        case PROTOCOL_TYPE_LC:
        case PROTOCOL_TYPE_OPCUA:
            break;
        default:
            continue;
            break;
        }
        }
        ptr->read_tags_size++;
    }
out:
    return ptr;
}

static template_t *get_write_reg(cJSON *root,device_t *dev, int prot_type, template_t *ptr,char *temp_name)
{
    cJSON *write_reg = cJSON_GetObjectItemCaseSensitive(root, "write_reg");
    if (!write_reg)
    {
        goto out;
    }
    proto_syslog(LOG_DEBUG, "WRITE_TAGS:");
    int write_reg_cnt = cJSON_GetArraySize(write_reg);
    for (int i = 0; i < write_reg_cnt; i++)
    {
        if (ptr->write_tags_size >= WR_TAGS_MAX)
        {
            BUSINESS_LOG(BLOG_NOTICE, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 缺少 read_reg",temp_name);
            proto_syslog(LOG_ERR, "write_reg_cnt overflow");
            goto out;
        }
        cJSON *dev_list_item = cJSON_GetArrayItem(write_reg, i);

        // GET_JSON_VALUE_STRING(dev_list_item, "tag", ptr->write_tags[ptr->write_tags_size]->write_reg.tag);
        // proto_syslog(LOG_DEBUG, "tag:%s",ptr->write_tags[ptr->write_tags_size]->write_reg.tag);
        cJSON *prot_info_list = cJSON_GetObjectItemCaseSensitive(dev_list_item, "prot");
        if (prot_info_list == NULL)
        {
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 写点位第[%d]条缺少prot",temp_name,i+1);

            proto_syslog(LOG_ERR, "get_write_reg err[%d]", i);
            continue;
        }
        if (ptr->write_tags[ptr->write_tags_size] == NULL)
            ptr->write_tags[ptr->write_tags_size] = calloc(sizeof(write_reg_info_t), 1);

        get_tag_general_info(i, dev_list_item, &ptr->write_tags[ptr->write_tags_size]->write_reg);
        GET_JSON_VALUE_INT(dev_list_item, "no_fetch", ptr->write_tags[ptr->write_tags_size]->no_fetch);
        // proto_syslog(LOG_DEBUG, "offset:%d",ptr->write_tags[ptr->write_tags_size]->no_fetch);
        proto_syslog(LOG_DEBUG, "no_fetch:%d", ptr->write_tags[ptr->write_tags_size]->no_fetch);

        if (prot_info_list != NULL)
        {

            int prot_info_cnt = cJSON_GetArraySize(prot_info_list);
            switch (prot_type)
            {
            case PROTOCOL_TYPE_NONE:
                break;

            case PROTOCOL_TYPE_SCRIPT:
                break;

            case PROTOCOL_TYPE_MODBUS_RTU:
            case PROTOCOL_TYPE_MODBUS_TCP:
            case PROTOCOL_TYPE_MODBUS_RTU_RCRC:
            {
                modbus_write_reg_info_t *reg_info = NULL;
                reg_info = calloc(sizeof(modbus_write_reg_info_t), 1);
                reg_info->head = 0;
                cJSON *p0 = cJSON_GetArrayItem(prot_info_list, 0);
                cJSON *p1 = cJSON_GetArrayItem(prot_info_list, 1);
                cJSON *p2 = cJSON_GetArrayItem(prot_info_list, 2);
                cJSON *p3 = cJSON_GetArrayItem(prot_info_list, 3);
                if (!p0 || !p1 || !p2 || !p3)
                {
                    BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                    proto_syslog(LOG_DEBUG, "invalid write reg :%d", i);
                    continue;
                }
                reg_info->func_code = p0->valueint;
                int addr_tmp = analysis_addr(p1->valuestring) + dev->base_addr;
                reg_info->reg_addr = (addr_tmp > MODBUS_MAX_POINT_ADDR)?MODBUS_MAX_POINT_ADDR:addr_tmp;
                reg_info->data_type = get_type_by_string(p2->valuestring);
                // proto_syslog(LOG_DEBUG, "data_type:%d",reg_info->data_type);
                reg_info->data_order = get_order_by_string(p3->valuestring);
                ptr->write_tags[ptr->write_tags_size]->write_reg.proto_ptr = reg_info;
                proto_syslog(LOG_DEBUG, "******func_code:%d, reg_addr:0x%04x data_type:%d data_order:%d",
                        reg_info->func_code, reg_info->reg_addr, reg_info->data_type,
                        reg_info->data_order);
            };
            break;

            case PROTOCOL_TYPE_DLT645_2007:
            case PROTOCOL_TYPE_DLT645_1997:
            {
                if (prot_info_cnt != 2)
                {
                    BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                    proto_syslog(LOG_ERR, "dlt 645 write info cfg error");
                    if (ptr->write_tags[ptr->write_tags_size] != NULL)
                    {
                        free(ptr->write_tags[ptr->write_tags_size]);
                        ptr->write_tags[ptr->write_tags_size] = NULL;
                        continue;
                    }
                }
                else
                {
                    dlt645_rw_info_t *dlt645_info = calloc(sizeof(dlt645_rw_info_t), 1);
                    if (dlt645_info == NULL)
                    {
                        proto_syslog(LOG_ERR, "calloc err for dlt645_info");
                    }
                    else
                    {
                        cJSON *data_id = cJSON_GetArrayItem(prot_info_list, 0);
                        cJSON *index = cJSON_GetArrayItem(prot_info_list, 1);
                        dlt645_info->data_id = analysis_addr(data_id->valuestring);

                        dlt645_info->index = index->valueint;
                        ptr->write_tags[ptr->write_tags_size]->write_reg.proto_ptr = dlt645_info;
                        proto_syslog(LOG_DEBUG, "dlt645_info[%d]:data_id=0x%x, index=%d", i, dlt645_info->data_id, dlt645_info->index);
                    }
                }
            }break;

            case PROTOCOL_TYPE_UPS:
            {
                if (prot_info_cnt != 2)
                {
                    BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                    proto_syslog(LOG_ERR, "ups write info cfg error");
                    if (ptr->write_tags[ptr->write_tags_size] != NULL)
                    {
                        free(ptr->write_tags[ptr->write_tags_size]);
                        ptr->write_tags[ptr->write_tags_size] = NULL;
                        continue;
                    }
                }
                else
                {
                    ups_write_info_t *ups_info = calloc(sizeof(ups_write_info_t), 1);
                    if (ups_info == NULL)
                    {
                        proto_syslog(LOG_ERR, "calloc err for ups_info");
                    }
                    else
                    {
                        cJSON *cmd = cJSON_GetArrayItem(prot_info_list, 0);
                        cJSON *need_par = cJSON_GetArrayItem(prot_info_list, 1);
                        //
                        ups_info->cmd = strdup(cmd->valuestring);
                        ups_info->need_par = need_par->valueint;
                        //
                        ptr->write_tags[ptr->write_tags_size]->write_reg.proto_ptr = ups_info;
                        proto_syslog(LOG_DEBUG, "ups write info[%d]:cmd=%s, need_par=%d",
                                i, ups_info->cmd, ups_info->need_par);
                    }
                }
            }
            break;

            case PROTOCOL_TYPE_BMSER_BIN:
            {
                if (prot_info_cnt != 6)
                {
                    BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                    proto_syslog(LOG_ERR, "ups read info cfg error");
                    if (ptr->write_tags[ptr->read_tags_size] != NULL)
                    {
                        free(ptr->write_tags[ptr->read_tags_size]);
                        ptr->write_tags[ptr->read_tags_size] = NULL;
                        continue;
                    }
                }
                else
                {
                    bmser_parser_info_t *bmser_info = calloc(sizeof(bmser_parser_info_t), 1);
                    if (bmser_info == NULL)
                    {
                        proto_syslog(LOG_ERR, "calloc err for bmser_info");
                    }
                    else
                    {
                        cJSON *addr = cJSON_GetArrayItem(prot_info_list, 0);
                        cJSON *sub_id = cJSON_GetArrayItem(prot_info_list, 1);
                        cJSON *type = cJSON_GetArrayItem(prot_info_list, 2);
                        cJSON *bit_pos = cJSON_GetArrayItem(prot_info_list, 3);
                        cJSON *bit_len = cJSON_GetArrayItem(prot_info_list, 4);
                        cJSON *reverse = cJSON_GetArrayItem(prot_info_list, 5);
                        //
                        bmser_info->block_id = analysis_addr(addr->valuestring);
                        bmser_info->sub_id = analysis_addr(sub_id->valuestring);
                        bmser_info->type = bmser_protocol_to_type(type->valuestring);
                        bmser_info->bit_pos = bit_pos->valueint;
                        bmser_info->bit_len = bit_len->valueint;
                        bmser_info->reverse = reverse->valueint;
                        bmser_info->byte_index = -1;
                        //
                        ptr->write_tags[ptr->write_tags_size]->write_reg.proto_ptr = bmser_info;
                        //
                        proto_syslog(LOG_DEBUG, "bmser_info[%d]:block_id=0x%08x, sub_id=%d, type=%d, bit_pos=%d, bit_len=%d, reverse=%d",
                                i, bmser_info->block_id, bmser_info->sub_id, bmser_info->type, bmser_info->bit_pos, bmser_info->bit_len, bmser_info->reverse);
                    }
                }
            }
            break;

            case PROTOCOL_TYPE_CAN:
            {
                if (prot_info_cnt < 7)
                {
                    BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

                    proto_syslog(LOG_ERR, "can read info cfg error");
                    if (ptr->write_tags[ptr->write_tags_size] != NULL)
                    {
                        free(ptr->write_tags[ptr->write_tags_size]);
                        ptr->write_tags[ptr->write_tags_size] = NULL;
                        continue;
                    }
                }
                else
                {
                    can_parser_info_t *can_info = creat_can_parser_info(prot_info_list);
                    if (can_info == NULL)
                    {
                        proto_syslog(LOG_ERR, "creat can_parser_info err");
                    }
                    else
                    {
                        ptr->write_tags[ptr->write_tags_size]->write_reg.proto_ptr = can_info;
                    }
                }
            }
            break;

            case PROTOCOL_TYPE_IEC104_CLIENT:
            {
                int addr, type;
                cJSON * jaddr, * jtype;
                iec104_reg_info_t * info104;

                addr = type = -1;
                info104 = (iec104_reg_info_t *) calloc(0x1, sizeof(*info104));
                jaddr = cJSON_GetArrayItem(prot_info_list, 0);
                jtype = cJSON_GetArrayItem(prot_info_list, 1);
                if (jaddr && cJSON_IsString(jaddr) && jaddr->valuestring)
                    addr = (int) strtol(jaddr->valuestring, NULL, 0);
                if (jtype && cJSON_IsString(jtype) && jtype->valuestring) {
                    if (strcmp(jtype->valuestring, "bool") == 0)
                        type = C_SC_NA_1;
                    else if (strcmp(jtype->valuestring, "int16") == 0)
                        type = C_SE_NB_1;
                    else if (strcmp(jtype->valuestring, "float") == 0)
                        type = C_SE_NC_1;
                    else if (strcmp(jtype->valuestring, "floatn") == 0)
                        type = C_SE_NA_1;
                    else {
                        proto_syslog(LOG_ERR, "invalid IEC104 type: %s", jtype->valuestring);
                    }
                }

                if (info104 != NULL) {
                    info104->ioa_addr = addr;
                    info104->ioa_type = type;
                    ptr->write_tags[ptr->write_tags_size]->write_reg.proto_ptr = info104;
                }
                break;
            }
			
			case PROTOCOL_TYPE_CUSTOM:
			{
				if (prot_info_cnt != 7)
				{
                    BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

					proto_syslog(LOG_ERR, "custom read info cfg error");
					if (ptr->write_tags[ptr->write_tags_size] != NULL)
					{
						free(ptr->write_tags[ptr->write_tags_size]);
						ptr->write_tags[ptr->write_tags_size] = NULL;
						continue;
					}
				}
				else
				{
					custom_parser_info_t *custom_info = creat_custom_parser_info(prot_info_list);
					if (custom_info == NULL)
					{
						proto_syslog(LOG_ERR, "creat custom_parser_info err");
					}
					else
					{
						ptr->write_tags[ptr->write_tags_size]->write_reg.proto_ptr = custom_info;
					}
				}
			}
			break;

			case PROTOCOL_TYPE_YDT1363_2014:
			{
				if (prot_info_cnt < 7)
				{
                    BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 读点位第[%d]条prot不正确",temp_name,i+1);

					proto_syslog(LOG_ERR, "ydt1363 write reg info cfg error");
					if (ptr->write_tags[ptr->write_tags_size] != NULL)
					{
						free(ptr->write_tags[ptr->write_tags_size]);
						ptr->write_tags[ptr->write_tags_size] = NULL;
						continue;
					}
				}
				else
				{
					ydt1363_parser_info_t *ydt1363_info = creat_ydt1363_parser_info(prot_info_list);
					if (ydt1363_info == NULL)
					{
						proto_syslog(LOG_ERR, "creat ydt1363_info err");
					}
					else
					{
						ptr->write_tags[ptr->write_tags_size]->write_reg.proto_ptr = ydt1363_info;
					}
				}
			}
			break;
            case PROTOCOL_TYPE_EMS:
            case PROTOCOL_TYPE_LC:
            case PROTOCOL_TYPE_OPCUA:
                break;

            default:
                continue;
                break;
            }
        }
        ptr->write_tags_size++;
    }
out:
    return ptr;
}

static template_t *get_info_user_def(cJSON **temp_info, device_t *dev, int prot_type, template_t *ptr)
{
    cJSON *user_def = cJSON_GetObjectItemCaseSensitive(*temp_info, "user_def");
    if (!user_def)
    {
        proto_syslog(LOG_ERR, "get user_def error");
        return ptr;
    }

    switch (prot_type)
    {
    case PROTOCOL_TYPE_MODBUS_RTU:
    case PROTOCOL_TYPE_MODBUS_TCP:
    case PROTOCOL_TYPE_MODBUS_RTU_RCRC:
    break;

    case PROTOCOL_TYPE_DLT645_2007:
    case PROTOCOL_TYPE_DLT645_1997:
    break;

    case PROTOCOL_TYPE_UPS:
    break;

    case PROTOCOL_TYPE_BMSER_BIN:
    break;

    case PROTOCOL_TYPE_CAN:
    break;

    case PROTOCOL_TYPE_IEC104_CLIENT:
    break;

    case PROTOCOL_TYPE_CUSTOM:
    break;

    case PROTOCOL_TYPE_YDT1363_2014:
    {
        ydt1363_user_def_t *ydt1363_user_def = NULL;
        ydt1363_user_def = calloc(1, sizeof(ydt1363_user_def_t));

        cJSON *ver = cJSON_GetObjectItemCaseSensitive(user_def, "ver");
        if (!ver){
            ydt1363_user_def->ver = strdup("2.2"); // default: ydt1363 protocol version
            proto_syslog(LOG_WARNING, "get ydt1363 protocol ver fail, use default ver:%s", "2.2");
        }
        else{
            ydt1363_user_def->ver = strdup(ver->valuestring);
        }

        cJSON *cid1 = cJSON_GetObjectItemCaseSensitive(user_def, "cid1");
        if (!cid1){
            ydt1363_user_def->cid1 = 0x46; // default：磷酸铁锂电池(设备类型)
            proto_syslog(LOG_WARNING, "get cid1 fail, use default cid1:0x%02x", 0x46);
        }
        else{
            ydt1363_user_def->cid1 = analysis_addr(cid1->valuestring);
        }

        ydt1363_user_def->custom = NULL;
        cJSON *custom = cJSON_GetObjectItemCaseSensitive(user_def, "custom");
        if (!custom){
            //ydt1363_user_def->custom = strdup("custom");
            proto_syslog(LOG_WARNING, "get ydt1363 protocol custom fail");
        }
        else{
            ydt1363_user_def->custom = strdup(custom->valuestring);
        }

        ptr->proto_ptr = ydt1363_user_def;
    }
    break;

    default:
        proto_syslog(LOG_WARNING, "unspported type of protocol");
        break;
    }

    return ptr;
}

static template_t *get_poll_list(cJSON *root,device_t *dev, int prot_type, template_t *ptr,char *temp_name)
{

    cJSON *poll_list = cJSON_GetObjectItemCaseSensitive(root, "poll_list");
    if (!poll_list)
    {
        proto_syslog(LOG_ERR, "get_poll_list error");
        BUSINESS_LOG(BLOG_WARNING, DC_START_ID_006, dev->no, "[数采]模板文件[%s] poll_list为空 注意核查 当前协议是否需要",temp_name);
        ptr->poll_list_size = 0;
        return ptr;
    }
    proto_syslog(LOG_NOTICE, "POLL LIST:");
    int poll_list_cnt = cJSON_GetArraySize(poll_list);
    for (int i = 0; i < poll_list_cnt; i++)
    {
        if(i>=NUM_POLL_CHUNkS_MAX)
        {
            proto_syslog(LOG_WARNING, "poll list size is too large!");
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] poll_list数量超过上限[%d]",temp_name,NUM_POLL_CHUNkS_MAX);
            break;
        }
        
        cJSON *dev_list_item = cJSON_GetArrayItem(poll_list, i);

        // GET_JSON_VALUE_STRING(dev_list_item, "type", type);
        // if (strcmp(type, "PCS") != 0) {
        //     continue;
        // }
        cJSON *scan_rate = cJSON_GetObjectItemCaseSensitive(dev_list_item, "scan_rate");
        if (!scan_rate)
        {
            proto_syslog(LOG_ERR, "invalid scan_rate");
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 第[%d]条 scan_rate无效",temp_name,i+1);
            continue;
        }

        ptr->poll_list[ptr->poll_list_size].scan_rate = scan_rate->valueint;
        // proto_syslog(LOG_DEBUG, "scan rate:%ld",ptr->poll_list[ptr->poll_list_size].scan_rate);
        proto_syslog(LOG_DEBUG, "list %d 's scan rate:%d", i, (int)ptr->poll_list[ptr->poll_list_size].scan_rate);
        cJSON *prot_info_list = cJSON_GetObjectItemCaseSensitive(dev_list_item, "prot");
        if (prot_info_list == NULL)
        {
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 第[%d]条 prot无效",temp_name,i+1);
            continue;
        }
        int prot_info_cnt = cJSON_GetArraySize(prot_info_list);
        modbus_poll_info_t *poll_info = NULL;
        switch (prot_type)
        {
        case PROTOCOL_TYPE_MODBUS_RTU:
        case PROTOCOL_TYPE_MODBUS_TCP:
        case PROTOCOL_TYPE_MODBUS_RTU_RCRC:
            poll_info = calloc(sizeof(modbus_poll_info_t), 1);
            if (prot_info_cnt < 3)
            {
                proto_syslog(LOG_DEBUG, "invalid number of poll params at ");
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 第[%d]条 prot内容错误请核对",temp_name,i+1);
                continue;
            }
            cJSON *p0 = cJSON_GetArrayItem(prot_info_list, 0);
            cJSON *p1 = cJSON_GetArrayItem(prot_info_list, 1);
            cJSON *p2 = cJSON_GetArrayItem(prot_info_list, 2);
            if (!p0 || !p1 || !p2)
            {
                proto_syslog(LOG_DEBUG, "invalid poll list:%d", i);
                continue;
            }
            poll_info->func_code = p0->valueint;
            poll_info->size = p2->valueint;
            poll_info->reg_addr = analysis_addr(p1->valuestring) + dev->base_addr;
            int addr_tmp = analysis_addr(p1->valuestring) + dev->base_addr;
            poll_info->reg_addr = (addr_tmp > MODBUS_MAX_POINT_ADDR)?MODBUS_MAX_POINT_ADDR:addr_tmp;
            ptr->poll_list[ptr->poll_list_size].proto_ptr = poll_info;
            // proto_syslog(LOG_DEBUG, "modbus poll list:%s func_code:%d, addr:%x size:%d",poll_info->func_code,poll_info->reg_addr,poll_info->size);
            break;
        case PROTOCOL_TYPE_DLT645_2007:
        case PROTOCOL_TYPE_DLT645_1997:
        {
            dlt645_poll_info_t *dlt645_poll = NULL;
            if (prot_info_cnt != 4)
            {
                proto_syslog(LOG_ERR, "dlt645_poll prot cfg error!!! prot len = %d", prot_info_cnt);
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 第[%d]条 prot内容错误请核对",temp_name,i+1);
  
            }
            else
            {
                cJSON *data_id = cJSON_GetArrayItem(prot_info_list, 0);
                cJSON *data_type = cJSON_GetArrayItem(prot_info_list, 1);
                cJSON *pos = cJSON_GetArrayItem(prot_info_list, 2);
                cJSON *data_len = cJSON_GetArrayItem(prot_info_list, 3);
                dlt645_poll = calloc(sizeof(dlt645_poll_info_t), 1);
                if (dlt645_poll == NULL)
                {
                    proto_syslog(LOG_ERR, "dlt645_poll calloc err!!!");
                }
                else
                {
                    dlt645_poll->data_id = analysis_addr(data_id->valuestring);
                    dlt645_poll->type = dlt645_get_ext_type_by_string(data_type->valuestring);
                    dlt645_poll->pos = pos->valueint;
                    dlt645_poll->data_len = data_len->valueint;
                    ptr->poll_list[ptr->poll_list_size].proto_ptr = dlt645_poll;
                }
                proto_syslog(LOG_DEBUG, "dlt645 poll list[%d], data_id:0x%x, pos:%d, data_len=%d, type = %d",
                          i, dlt645_poll->data_id, dlt645_poll->pos, dlt645_poll->data_len, dlt645_poll->type);
            }
        }
        break;
        case PROTOCOL_TYPE_UPS:
        {
            ups_poll_info_t *ups_poll = NULL;
            if (prot_info_cnt != 1)
            {
                proto_syslog(LOG_ERR, "ups_poll prot cfg error!!! prot len = %d", prot_info_cnt);
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 第[%d]条 prot内容错误请核对",temp_name,i+1);
  
            }
            else
            {
                cJSON *cmd = cJSON_GetArrayItem(prot_info_list, 0);
                ups_poll = calloc(sizeof(ups_poll_info_t), 1);
                if (ups_poll == NULL)
                {
                    proto_syslog(LOG_ERR, "ups_poll calloc err!!!");
                }
                else
                {
                    char ups_buff[12] = {0};
                    ups_poll->cmd_len = snprintf(ups_buff, sizeof(ups_poll), "%s", cmd->valuestring);
                    if (ups_poll->cmd_len > 0)
                    {
                        ups_poll->cmd = strdup(ups_buff);
                    }
                    ptr->poll_list[ptr->poll_list_size].proto_ptr = ups_poll;
                }
                proto_syslog(LOG_DEBUG, "ups poll list[%d], cmd:%s, cmd_len:%d",
                          i, ups_poll->cmd, ups_poll->cmd_len);
            }

        }break;

        case PROTOCOL_TYPE_BMSER_BIN:
        {
            bmser_poll_info_t *bmser_poll = NULL;
            if (prot_info_cnt != 3)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 第[%d]条 prot内容错误请核对",temp_name,i+1);
                proto_syslog(LOG_ERR, "bmser_poll prot cfg error!!! prot len = %d", prot_info_cnt);
            }
            else
            {
                cJSON *block_id = cJSON_GetArrayItem(prot_info_list, 0);
                cJSON *sub_id = cJSON_GetArrayItem(prot_info_list, 1);
                cJSON *sub_num = cJSON_GetArrayItem(prot_info_list, 2);
                bmser_poll = calloc(1, sizeof(bmser_poll_info_t));
                if (bmser_poll == NULL)
                {
                    proto_syslog(LOG_ERR, "bmser_poll calloc err!!!");
                }
                else
                {
                    bmser_poll->block_id = analysis_addr(block_id->valuestring);
                    bmser_poll->sub_id = analysis_addr(sub_id->valuestring);
                    bmser_poll->sub_num = sub_num->valueint;
                    //
                    ptr->poll_list[ptr->poll_list_size].proto_ptr = bmser_poll;
                }
                proto_syslog(LOG_DEBUG, "bmser poll list[%d], block_id:0x%08x, sub_id:0x%04x, sub_num=%d",
                          i, bmser_poll->block_id, bmser_poll->sub_id, bmser_poll->sub_num);
            }

        }break;

        case PROTOCOL_TYPE_CAN:
        {
            proto_can_poll *cam_poll = NULL;
            if (prot_info_cnt < 1)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 第[%d]条 prot内容错误请核对",temp_name,i+1);
                proto_syslog(LOG_ERR, "cam_poll prot cfg error!!! prot len = %d", prot_info_cnt);
            }
            else
            {
                cJSON *id = cJSON_GetArrayItem(prot_info_list, 0);
                cam_poll = calloc(1, sizeof(proto_can_poll));
                if (cam_poll == NULL)
                {
                    proto_syslog(LOG_ERR, "cam_poll calloc err!!!");
                }
                else
                {
                    cam_poll->id = analysis_addr(id->valuestring);
                    if (strlen(id->valuestring) > 5)
                    {
                        cam_poll->id |= 0x80000000;
                    }
                    //
                    ptr->poll_list[ptr->poll_list_size].proto_ptr = cam_poll;

                    if (prot_info_cnt > 1)
                    {
                        cJSON *id = cJSON_GetArrayItem(prot_info_list, 1);
                        char *p_data = strdup(id->valuestring);
                        int init_data_len = strlen(p_data);
                        if (init_data_len != 16)
                        {
                            proto_syslog(LOG_WARNING, "init data len:%d", init_data_len);
                            if (init_data_len > 16)
                            {
                                init_data_len = 16;
                            }
                        }
                        for (int i = 0; i < init_data_len; i++) // 检查数据准确,并转为大写
                        {
                            if ((p_data[i] >= '0' && p_data[i] <= '9') || (p_data[i] >= 'A' && p_data[i] <= 'F'))
                            {
                            }
                            else if (p_data[i] >= 'a' && p_data[i] <= 'f')
                            {
                                p_data[i] -= ('a' - 'A');
                            }
                            else
                            {
                                proto_syslog(LOG_WARNING, "init data format err:%s", id->valuestring);
                                init_data_len = 0; // 为了不写入初始化数据
                            }
                        }
                        init_data_len = init_data_len / 2;
                        for (int i = 0; i < init_data_len; i++)
                        {
                            cam_poll->buff.buff[i] = (p_data[2 * i] >= '0' && p_data[2 * i] <= '9') ? (p_data[2 * i] - '0') : (p_data[2 * i] - 'A' + 10);
                            cam_poll->buff.buff[i] = cam_poll->buff.buff[i] << 4;
                            cam_poll->buff.buff[i] |= (p_data[2 * i + 1] >= '0' && p_data[2 * i + 1] <= '9') ? (p_data[2 * i + 1] - '0') : (p_data[2 * i + 1] - 'A' + 10);
                        }
                        cam_poll->dlc = init_data_len;
                        free(p_data);
                    }
                    else
                    {
                        cam_poll->dlc = 8; // 没有配置默认8字节
                    }

                    if (prot_info_cnt > 2)
                    {
                        cJSON *loop_tx = cJSON_GetArrayItem(prot_info_list, 2);
                        if (loop_tx->valueint != 0)
                        {
                            cam_poll->loop_tx = 1;
                        }
                    }

                    if (prot_info_cnt > 3)
                    {
                        cJSON *remote_len = cJSON_GetArrayItem(prot_info_list, 3);
                        cam_poll->remote_len = remote_len->valueint;
                    }
                    else
                    {
                        cam_poll->remote_len = -1;
                    }
                }
                proto_syslog_hex(LOG_INFO, cam_poll->buff.buff, sizeof(cam_poll->buff), "can poll list[%d], id:0x%08x,dlc:%d,remote_len:%d,init data:", i, cam_poll->id, cam_poll->dlc, cam_poll->remote_len);
            }
        }
        break;

        case PROTOCOL_TYPE_IEC104_CLIENT:
        {
            proto_iec104_poll * iec104p;

            cJSON *jid = cJSON_GetArrayItem(prot_info_list, 0);
            iec104p = (proto_iec104_poll *) calloc(1, sizeof(*iec104p));
            if (iec104p != NULL) {
                if (cJSON_IsNumber(jid))
                    iec104p->iec104_id = jid->valueint;
                ptr->poll_list[ptr->poll_list_size].proto_ptr = iec104p;
            }
            break;
        }

        case PROTOCOL_TYPE_CUSTOM:
        {
            proto_custom_poll *custom_poll = NULL;
            if (prot_info_cnt < 1)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 第[%d]条 prot内容错误请核对",temp_name,i+1);
                proto_syslog(LOG_ERR, "custom_poll prot cfg error!!! prot len = %d", prot_info_cnt);
            }
            else
            {
                cJSON *id = cJSON_GetArrayItem(prot_info_list, 0);
                custom_poll = calloc(1, sizeof(proto_custom_poll));
                if (custom_poll == NULL)
                {
                    proto_syslog(LOG_ERR, "custom_poll calloc err!!!");
                }
                else
                {
                    custom_poll->id = analysis_addr(id->valuestring);
                    ptr->poll_list[ptr->poll_list_size].proto_ptr = custom_poll;

                    if (prot_info_cnt > 1)
                    {
                        cJSON *id = cJSON_GetArrayItem(prot_info_list, 1);
                        char *p_data = strdup(id->valuestring);
                        int init_data_len = strlen(p_data);
                        if (init_data_len >= 1024)
                        {
                            proto_syslog(LOG_WARNING, "init data len:%d", init_data_len);
                            init_data_len = 1024;
                        }
                        for (int i = 0; i < init_data_len; i++) // 检查数据准确,并转为小写
                        {
                            if ((p_data[i] >= '0' && p_data[i] <= '9') || (p_data[i] >= 'a' && p_data[i] <= 'f'))
                            {
                            }
                            else if (p_data[i] >= 'A' && p_data[i] <= 'F')
                            {
                                p_data[i] += 'a' - 'A';
                            }
                            else
                            {
                                proto_syslog(LOG_WARNING, "init data format err:%s", id->valuestring);
                                init_data_len = 0; // 为了不写入初始化数据
                            }
                        }
                        custom_poll->len = init_data_len / 2;
                        for (int i = 0; i < custom_poll->len; i++)
                        {
                            custom_poll->buff[i] = (p_data[2 * i] >= '0' && p_data[2 * i] <= '9') ? (p_data[2 * i] - '0') : (p_data[2 * i] - 'a' + 10);
                            custom_poll->buff[i] = custom_poll->buff[i] << 4;
                            custom_poll->buff[i] |= (p_data[2 * i + 1] >= '0' && p_data[2 * i + 1] <= '9') ? (p_data[2 * i + 1] - '0') : (p_data[2 * i + 1] - 'a' + 10);
                        }
                        free(p_data);
                    }
					
                    if (prot_info_cnt > 2)
                    {
                        cJSON *crc16 = cJSON_GetArrayItem(prot_info_list, 2);
						
                        custom_poll->crc16_des = strdup(crc16->valuestring);
						if (!custom_poll->crc16_des || strlen(custom_poll->crc16_des) <= 0){
                            
                            custom_poll->crc16 = CRC16_N;
						}
						else
							custom_poll->crc16 = get_crc16_by_string(custom_poll->crc16_des);
                    }

                    if (prot_info_cnt > 3)
                    {
                        cJSON *loop_tx = cJSON_GetArrayItem(prot_info_list, 3);
                        if (loop_tx->valueint != 0)
                        {
                            custom_poll->loop_tx = 1;
                        }
                    }
                }
                
                proto_syslog_hex(LOG_INFO, custom_poll->buff, custom_poll->len, "custom poll list[%d], id:0x%04x,init data:", i, custom_poll->id);
            }
        }
        break;

        case PROTOCOL_TYPE_YDT1363_2014:
        {
            proto_ydt1363_poll *ydt1363_poll = NULL;
            if (prot_info_cnt < 3)
            {
                BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 第[%d]条 prot内容错误请核对",temp_name,i+1);
                proto_syslog(LOG_ERR, "ydt1363_poll prot cfg error!!! prot len = %d", prot_info_cnt);
            }
            else
            {
                cJSON *cid2 = cJSON_GetArrayItem(prot_info_list, 0);
                ydt1363_poll = calloc(1, sizeof(proto_ydt1363_poll));
                if (ydt1363_poll == NULL)
                {
                    proto_syslog(LOG_ERR, "ydt1363_poll calloc err!!!");
                }
                else
                {
                    ydt1363_poll->cid2 = analysis_addr(cid2->valuestring);
                    ptr->poll_list[ptr->poll_list_size].proto_ptr = ydt1363_poll;

                    cJSON *command_info = cJSON_GetArrayItem(prot_info_list, 1);
                    ydt1363_poll->command_info = strdup(command_info->valuestring);
                    analysis_command_info(ydt1363_poll);
					
                    cJSON *val = cJSON_GetArrayItem(prot_info_list, 2);
                    if (!val || strlen(val->valuestring) == 0)
                    {
                        proto_syslog(LOG_WARNING, "get val fail");
                        ydt1363_poll->val = 0x00;// 无效
                    }
                    else{
                        if (strcmp(val->valuestring, "$ADR") == 0){ // 取设备地址填充，沛城BMS适配
                            ydt1363_poll->val = *(uint32_t *)dev->proto_ptr;
                        }
                        else{
                            ydt1363_poll->val = analysis_addr(val->valuestring);
                        }
                    }

                    if (prot_info_cnt > 3)
                    {
                        cJSON *id = cJSON_GetArrayItem(prot_info_list, 3);
                        ydt1363_poll->id = analysis_addr(id->valuestring);
                    }else
                        ydt1363_poll->id = 0x00; // id = 0，真正的周期性查询指令~~

                    ydt1363_user_def_t *ydt1363_user_def = (ydt1363_user_def_t *)ptr->proto_ptr;
                    if (ydt1363_user_def->custom != NULL){
                        ydt1363_poll->item[CUSTOM].exist = 1;
                        ydt1363_poll->item[CUSTOM].des = strdup("custom");
                        ydt1363_poll->item[CUSTOM].len = strlen(ydt1363_user_def->custom);
                        strncpy((char*)ydt1363_poll->item[CUSTOM].buff, ydt1363_user_def->custom, sizeof(ydt1363_poll->item[CUSTOM].buff) - 1);
                    }
                    else{
                        proto_syslog(LOG_WARNING, "no custom");
                    }
#if 1
                    for (int i = 0; i < ydt1363_poll->command_info_list_num; i++)
                    {
                        for (int j = 0; j < g_ydt1363_code_count; j++)
                        {
                            ydt1363_code_des_t *ydt1363_code_des = &(ydt1363_code_tab[j]);
                            if (strcmp(ydt1363_poll->command_info_list[i], ydt1363_code_des->des) == 0){
                                ydt1363_poll->item[ydt1363_code_des->code].exist = 1;
                                ydt1363_poll->item[ydt1363_code_des->code].des = strdup(ydt1363_code_des->des);
                                proto_syslog(LOG_DEBUG, "ydt1363 command info exist %s index=[%d]", ydt1363_code_des->des, ydt1363_code_des->code);
                                break;
                            }
                        }
                    }  
#endif               
                }
            }
        }
        break;

        default:
            proto_syslog(LOG_WARNING, "unspported type of protocol");
            break;
        }
        ptr->poll_list_size++;
    }

    return ptr;
}

/****************************************************************************
 适配器加载由两种方式，一种是在模板文件中定义，另一种是在代码中定义
代码中定义: 复制一份 common_adapter.c 代码，自定义执行函数更换函数指针，模板中 info:adapter 字段 需与 代码中的 compatible 字段一致
模板中定义: 定义 adapter 列表, 使用common_adapter.c 中的函数, function 名可自定义
 ****************************************************************************/
static template_t* get_adapter(cJSON* root, device_t* dev, int prot_type, template_t* ptr)
{
    cJSON* adapter = cJSON_GetObjectItem(root, "adapter"); // "adapter":[]
    if (adapter == NULL) return ptr;

    adapter_function_t* f = NULL;
    char dev_func_name[ADAPTER_FUNCTION_NAME_LEN] = {0};

    int func_num = cJSON_GetArraySize(adapter); 

    if (func_num > 0)
    {
        ptr->adapter_functions = (adapter_function_t**)calloc(sizeof(void*), func_num);
    }
    ptr->adapter_functions_size = 0;
    for (int i = 0; i < func_num; i++)
    {
        cJSON* func = cJSON_GetArrayItem(adapter, i);
        if (!func) continue;

        cJSON* function = cJSON_GetObjectItemCaseSensitive(func, "function");
        if (function == NULL || strlen(function->valuestring) <= 0) continue;

        snprintf(dev_func_name, ADAPTER_FUNCTION_NAME_LEN, "%s.%s", dev->no, function->valuestring);

        cJSON* tagname = cJSON_GetObjectItemCaseSensitive(func, "tagname");
        if (tagname != NULL) // tagname 存在，则直接写值
        {
            reg_info_t* reg = NULL; // 检查点位是否存在于 read_reg 或 wirte_reg
            for (int j = 0; j < ptr->write_tags_size; j++)
            {
                if (strcmp(tagname->valuestring, ptr->write_tags[j]->write_reg.name) == 0)
                {
                    reg = &ptr->write_tags[j]->write_reg;
                    break;
                }
            }
            if (reg == NULL)
            {
                for (int j = 0; j < ptr->read_tags_size; j++)
                {
                    if (strcmp(tagname->valuestring, ptr->read_tags[j]->read_reg.name) == 0)
                    {
                        reg = &ptr->read_tags[j]->read_reg;
                        break;
                    }
                }
            }
            if (reg == NULL) continue;

            f = (adapter_function_t*)malloc(sizeof(adapter_function_t) + sizeof(adapter_var_map_t));
            f->function = strdup(function->valuestring);
            f->maps_num  = 1;

            // 直接写值
            f->maps[0].tagname = strdup(tagname->valuestring);
            f->maps[0].map_in  = -1;
            f->maps[0].map_out = -1;

            f->maps[0].data_type = reg->data_type;

            ptr->adapter_functions[ptr->adapter_functions_size++] = f;
        }
        else // tagname 不存在，则查找 value_maps
        {
            cJSON* maps = cJSON_GetObjectItemCaseSensitive(func, "value_maps");
            if (maps == NULL) continue;

            int map_num  = cJSON_GetArraySize(maps);
            f = (adapter_function_t*)malloc(sizeof(adapter_function_t) + sizeof(adapter_var_map_t) * map_num);
            f->function = strdup(function->valuestring);
            f->maps_num = 0;

            for (int j = 0; j < map_num; j++)
            {
                cJSON* map     = cJSON_GetArrayItem(maps, j);
                cJSON* tagname = cJSON_GetObjectItemCaseSensitive(map, "tagname");
                cJSON* map_in  = cJSON_GetObjectItemCaseSensitive(map, "map_in");
                cJSON* map_out = cJSON_GetObjectItemCaseSensitive(map, "map_out");
                if (tagname == NULL || map_in == NULL || map_out == NULL)
                {
                    continue;
                }

                reg_info_t* reg = NULL; // 检查点位是否存在
                for (int i = 0; i < ptr->write_tags_size; i++)
                {
                    if (strcmp(tagname->valuestring, ptr->write_tags[i]->write_reg.name) == 0)
                    {
                        reg = &ptr->write_tags[i]->write_reg;
                        break;
                    }
                }
                if (reg == NULL)
                {
                    for (int i = 0; i < ptr->read_tags_size; i++)
                    {
                        if (strcmp(tagname->valuestring, ptr->read_tags[i]->read_reg.name) == 0)
                        {
                            reg = &ptr->read_tags[i]->read_reg;
                            break;
                        }
                    }
                }
                if (reg == NULL) continue;
                // 点位映射
                f->maps[f->maps_num].tagname   = strdup(tagname->valuestring);
                f->maps[f->maps_num].data_type = reg->data_type;
                f->maps[f->maps_num].map_in    = map_in->valueint;
                f->maps[f->maps_num].map_out   = map_out->valueint;
                f->maps_num++;
            }
            ptr->adapter_functions[ptr->adapter_functions_size++] = f;
        }
        if (f != NULL)
        {
           hash_intptr_addptr(&this_p->func_map_hash, dev_func_name, strlen(dev_func_name), f);
        }
        f = NULL;
    }
    return ptr;
}

int compare_version(const char* v1, const char* v2)
{
    // 格式必须为:xxx.xxx.xxx
    const char *vv[2] = {v1, v2};
    int point_sum = 0;
    const char *p = NULL;
    //检查格式
    for (int i = 0; i < sizeof(vv) / sizeof(vv[0]); i++)
    {
        point_sum = 0;
        p = vv[i];
        while (*p != '\0')
        {
            if (*p < '0' || *p > '9')
            {
                if (*p == '.')
                {
                    point_sum++;
                }
                else
                {
                    return -2;
                }
            }
            p++;
        }

        if (point_sum != 2)
        {
            return -3;
        }
    }

    int cmp[3][2] = {0};
    sscanf(v1, "%d.%d.%d", &cmp[0][0], &cmp[1][0], &cmp[2][0]);
    sscanf(v2, "%d.%d.%d", &cmp[0][1], &cmp[1][1], &cmp[2][1]);

    for (int i = 0; i < sizeof(cmp) / sizeof(cmp[0]); i++)
    {
        if (cmp[i][0] != cmp[i][1])
        {
            return (cmp[i][0] < cmp[i][1]) ? -1 : 1;
        }
    }

    return 0;
}


int bmser_tag_cmp(const void *cmp1, const void *cmp2)
{
    const tag_t **p1 = (const tag_t **)cmp1;
    const tag_t **p2 = (const tag_t **)cmp2;

    bmser_parser_info_t *parser_info1 = (bmser_parser_info_t *)(p1[0]->proto_ptr);
    bmser_parser_info_t *parser_info2 = (bmser_parser_info_t *)(p2[0]->proto_ptr);

    if (parser_info1->block_id == parser_info2->block_id)
    {
        return (parser_info1->sub_id - parser_info2->sub_id);
    }
    else
    {
        return (parser_info1->block_id - parser_info2->block_id);
    }
}

int bmser_channel_late_handle(channel_t *pc)
{
    tag_t **tag_arr = (tag_t **)calloc(RO_TAGS_MAX, sizeof(tag_t *));
    int tag_arr_size = 0;
    if (tag_arr == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error");
        return -1;
    }

    for (int d = 0; d < pc->devs_size; d++)
    {
        device_t *pd = pc->devs[d];
        proto_syslog(LOG_NOTICE, "->dev:%s", pd->no);
        for (int i = 0; i < pc->chunks_interv_size; i++)
        {
            if (pd == pc->chunks_interv[i]->dev_ptr)
            {
                bmser_poll_info_t *poll_info = (bmser_poll_info_t *)pc->chunks_interv[i]->chunk_proto_ptr;
                bmser_parser_info_t *parser_info = NULL;
                tag_arr_size = 0;
                proto_syslog(LOG_NOTICE, "-->poll_info:block_is=0x%08x, sub_id=0x%04x, sub_sum=%d", poll_info->block_id, poll_info->sub_id, poll_info->sub_num);
                for (int k = 0; k < pd->tags.ro_tags_size; k++)
                {
                    parser_info = (bmser_parser_info_t *)pd->tags.ro_tags[k]->proto_ptr;
                    if ((parser_info->block_id == poll_info->block_id) && (parser_info->sub_id >= poll_info->sub_id) && (parser_info->sub_id < (poll_info->sub_id + poll_info->sub_num)))
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.ro_tags[k];
                    }
                }

                for (int k = 0; k < pd->tags.rw_tags_size; k++)
                {
                    parser_info = (bmser_parser_info_t *)pd->tags.rw_tags[k]->proto_ptr;
                    if ((parser_info->block_id == poll_info->block_id) && (parser_info->sub_id >= poll_info->sub_id) && (parser_info->sub_id < (poll_info->sub_id + poll_info->sub_num)))
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.rw_tags[k];
                    }
                }
                
                poll_info->tag_sum = tag_arr_size;
                if (tag_arr_size > 0)
                {
                    poll_info->p_tag = (tag_t **)calloc(tag_arr_size, sizeof(tag_t *));
                    memcpy(poll_info->p_tag, tag_arr, tag_arr_size * sizeof(tag_t *));
#if 0 //需要tag地址连续
                    qsort(poll_info->p_tag, poll_info->tag_sum, sizeof(tag_t *), bmser_tag_cmp);
                    parser_info = (bmser_parser_info_t *)poll_info->p_tag[0]->proto_ptr;
                    parser_info->byte_index = 0;
                    bmser_parser_info_t *pre_parser_info = parser_info;
                    for (int i = 0; i < tag_arr_size; i++)
                    {
                        parser_info = (bmser_parser_info_t *)poll_info->p_tag[i]->proto_ptr;
                        if (parser_info->sub_id == pre_parser_info->sub_id)
                        {
                            parser_info->byte_index = pre_parser_info->byte_index;
                        }
                        else
                        {
                            if (parser_info->sub_id != (pre_parser_info->sub_id + 1))
                            {
                                proto_syslog(LOG_ERR, "tag[%s] and tag[%s] sub_id is discontinuous", poll_info->p_tag[i - 1]->name, poll_info->p_tag[i]->name);
                                exit(-1);
                            }
                            switch (pre_parser_info->type)
                            {
                            case BMSER_DATA_U16:
                            case BMSER_DATA_S16:
                            case BMSER_DATA_BIT16:
                                parser_info->byte_index = pre_parser_info->byte_index + 2;
                                break;

                            case BMSER_DATA_U32:
                            case BMSER_DATA_S32:
                            case BMSER_DATA_FLOAT:
                                parser_info->byte_index = pre_parser_info->byte_index + 4;
                                break;

                            default:
                                proto_syslog(LOG_ERR, "parser info type error");
                                break;
                            }
                        }
                        pre_parser_info = parser_info;
                        //
                        proto_syslog(LOG_NOTICE, "--->tag:%s, block_id=0x%08x, sub_id=0x%04x, byte_index = %d",
                                  poll_info->p_tag[i]->name, parser_info->block_id, parser_info->sub_id, parser_info->byte_index);
                    }
#else
                    for (int i = 0; i < tag_arr_size; i++)
                    {
                        parser_info = (bmser_parser_info_t *)poll_info->p_tag[i]->proto_ptr;
                        parser_info->byte_index = (parser_info->sub_id - poll_info->sub_id) * 2;
                    }
#endif
                    for (int i = 0; i < tag_arr_size; i++)
                    {
                        parser_info = (bmser_parser_info_t *)poll_info->p_tag[i]->proto_ptr;
                        proto_syslog(LOG_NOTICE, "--->tag:%s, block_id=0x%08x, sub_id=0x%04x, byte_index = %d",
                                  poll_info->p_tag[i]->name, parser_info->block_id, parser_info->sub_id, parser_info->byte_index);
                    }

                }
            }
        }
    }
    free(tag_arr);
    tag_arr = NULL;
    return 0;
}

int modbus_channel_late_handle(channel_t *pc)
{
    tag_t **tag_arr = (tag_t **)calloc(RO_TAGS_MAX, sizeof(tag_t *));
    int tag_arr_size = 0;
    if (tag_arr == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error");
        return -1;
    }

    for (int d = 0; d < pc->devs_size; d++)
    {
        device_t *pd = pc->devs[d];
        proto_syslog(LOG_NOTICE, "->dev:%s", pd->no);
        for (int i = 0; i < pc->chunks_interv_size; i++)
        {
            if (pd == pc->chunks_interv[i]->dev_ptr)
            {
                modbus_poll_info_t *poll_info = (modbus_poll_info_t *)pc->chunks_interv[i]->chunk_proto_ptr;
                poll_info->index = i;
                modbus_read_reg_info_t *parser_info = NULL;
                tag_arr_size = 0;
                proto_syslog(LOG_NOTICE, "-->poll_info:func_code=0x%08x, reg_addr=0x%04x, size=%d", poll_info->func_code, poll_info->reg_addr, poll_info->size);
                for (int k = 0; k < pd->tags.ro_tags_size; k++)
                {
                    parser_info = (modbus_read_reg_info_t *)pd->tags.ro_tags[k]->proto_ptr;
                    if ((parser_info->func_code == poll_info->func_code) && (parser_info->reg_addr >= poll_info->reg_addr) && (parser_info->reg_addr < (poll_info->reg_addr + poll_info->size)))
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.ro_tags[k];
                    }
                }

                for (int k = 0; k < pd->tags.rw_tags_size; k++)
                {
                    parser_info = (modbus_read_reg_info_t *)pd->tags.rw_tags[k]->proto_ptr;
                    if ((parser_info->func_code == poll_info->func_code) && (parser_info->reg_addr >= poll_info->reg_addr) && (parser_info->reg_addr < (poll_info->reg_addr + poll_info->size)))
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.rw_tags[k];
                    }
                }

                poll_info->tag_sum = tag_arr_size;
                if (tag_arr_size > 0)
                {
                    poll_info->p_tag = (tag_t **)calloc(tag_arr_size, sizeof(tag_t *));
                    memcpy(poll_info->p_tag, tag_arr, tag_arr_size * sizeof(tag_t *));
                    for (int i = 0; i < tag_arr_size; i++)
                    {
                        parser_info = (modbus_read_reg_info_t *)poll_info->p_tag[i]->proto_ptr;
                    }
                    for (int i = 0; i < tag_arr_size; i++)
                    {
                        parser_info = (modbus_read_reg_info_t *)poll_info->p_tag[i]->proto_ptr;
                        proto_syslog(LOG_INFO, "--->tag:%s, func_code=0x%08x, reg_addr=0x%04x",
                                     poll_info->p_tag[i]->name, parser_info->func_code, parser_info->reg_addr);
                    }
                }
            }
        }
    }
    free(tag_arr);
    tag_arr = NULL;
    return 0;
}

int can_channel_late_handle(channel_t *pc)
{
    tag_t **tag_arr = (tag_t **)calloc(RO_TAGS_MAX, sizeof(tag_t *));
    int tag_arr_size = 0;
    if (tag_arr == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error");
        return -1;
    }

    for (int d = 0; d < pc->devs_size; d++)
    {
        device_t *pd = pc->devs[d];
        const proto_can_adp *adp = find_proto_can_adp(pd->use_template->adapter);
        if (adp != NULL)
        {
            proto_syslog(LOG_NOTICE, "find proto can adp ok, adp:%s", pd->use_template->adapter);
        }
        proto_syslog(LOG_NOTICE, "->dev:%s", pd->no);
        for (int i = 0; i < pc->chunks_interv_size; i++)
        {
            if (pd == pc->chunks_interv[i]->dev_ptr)
            {
                proto_can_poll *poll_info = (proto_can_poll *)pc->chunks_interv[i]->chunk_proto_ptr;
                can_parser_info_t *parser_info = NULL;
                tag_arr_size = 0;
                proto_syslog(LOG_NOTICE, "-->poll_info:id=0x%08x", poll_info->id);
                for (int k = 0; k < pd->tags.ro_tags_size; k++)
                {
                    parser_info = (can_parser_info_t *)pd->tags.ro_tags[k]->proto_ptr;
                    if (parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.ro_tags[k];
                    }
                }

                for (int k = 0; k < pd->tags.rw_tags_size; k++)
                {
                    parser_info = (can_parser_info_t *)pd->tags.rw_tags[k]->proto_ptr;
                    if (parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.rw_tags[k];
                    }
                }
                if (adp != NULL)
                {
                    poll_info->fun = find_proto_can_adp_handle(adp, poll_info->id);
                    if (poll_info->fun != NULL)
                    {
                        proto_syslog(LOG_NOTICE, "find adp handle:0x%08x", poll_info->id);
                    }
                    else
                    {
                        proto_syslog(LOG_WARNING, "not find adp handle:0x%08x", poll_info->id);
                    }
                }
                poll_info->tag_sum = tag_arr_size;
                if (tag_arr_size > 0)
                {
                    poll_info->p_tag = (tag_t **)calloc(tag_arr_size, sizeof(tag_t *));
                    memcpy(poll_info->p_tag, tag_arr, tag_arr_size * sizeof(tag_t *));

                    for (int i = 0; i < tag_arr_size; i++)
                    {
                        parser_info = (can_parser_info_t *)poll_info->p_tag[i]->proto_ptr;
                        proto_syslog(LOG_NOTICE, "--->tag:%s, id=0x%08x, byte_index = %d",
                                     poll_info->p_tag[i]->name, parser_info->id, parser_info->byte_index);
                    }
                }
            }
        }
        //
        for (int i = 0; i < pc->chunks_nonstop_size; i++)
        {
            if (pd == pc->chunks_nonstop[i]->dev_ptr)
            {
                proto_can_poll *poll_info = (proto_can_poll *)pc->chunks_nonstop[i]->chunk_proto_ptr;
                can_parser_info_t *parser_info = NULL;
                tag_arr_size = 0;
                proto_syslog(LOG_NOTICE, "-->poll_info:id=0x%08x", poll_info->id);
                for (int k = 0; k < pd->tags.ro_tags_size; k++)
                {
                    parser_info = (can_parser_info_t *)pd->tags.ro_tags[k]->proto_ptr;
                    if (parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.ro_tags[k];
                    }
                }

                for (int k = 0; k < pd->tags.rw_tags_size; k++)
                {
                    parser_info = (can_parser_info_t *)pd->tags.rw_tags[k]->proto_ptr;
                    if (parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.rw_tags[k];
                    }
                }
                if (adp != NULL)
                {
                    poll_info->fun = find_proto_can_adp_handle(adp, poll_info->id);
                    if (poll_info->fun != NULL)
                    {
                        proto_syslog(LOG_NOTICE, "find adp handle:0x%08x", poll_info->id);
                    }
                    else
                    {
                        proto_syslog(LOG_WARNING, "not find adp handle:0x%08x", poll_info->id);
                    }
                }
                poll_info->tag_sum = tag_arr_size;
                if (tag_arr_size > 0)
                {
                    poll_info->p_tag = (tag_t **)calloc(tag_arr_size, sizeof(tag_t *));
                    memcpy(poll_info->p_tag, tag_arr, tag_arr_size * sizeof(tag_t *));

                    for (int i = 0; i < tag_arr_size; i++)
                    {
                        parser_info = (can_parser_info_t *)poll_info->p_tag[i]->proto_ptr;
                        proto_syslog(LOG_NOTICE, "--->tag:%s, id=0x%08x, byte_index = %d",
                                     poll_info->p_tag[i]->name, parser_info->id, parser_info->byte_index);
                    }
                }
            }
        }
    }

    free(tag_arr);
    tag_arr = NULL;
    return 0;
}

int custom_channel_late_handle(channel_t *pc)
{
    tag_t **tag_arr = (tag_t **)calloc(RO_TAGS_MAX, sizeof(tag_t *));
    int tag_arr_size = 0;
    if (tag_arr == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error");
        return -1;
    }

    for (int d = 0; d < pc->devs_size; d++)
    {
        device_t *pd = pc->devs[d];
        /*const proto_can_adp *adp = find_proto_can_adp(pd->use_template->adapter);
        if (adp != NULL)
        {
            proto_syslog(LOG_NOTICE, "find proto can adp ok, adp:%s", pd->use_template->adapter);
        }*/
        proto_syslog(LOG_NOTICE, "->dev:%s", pd->no);
        for (int i = 0; i < pc->chunks_interv_size; i++)
        {
            if (pd == pc->chunks_interv[i]->dev_ptr)
            {
                proto_custom_poll *poll_info = (proto_custom_poll *)pc->chunks_interv[i]->chunk_proto_ptr;
                custom_parser_info_t *parser_info = NULL;
                tag_arr_size = 0;
                proto_syslog(LOG_NOTICE, "-->poll_info:id=0x%08x", poll_info->id);
                for (int k = 0; k < pd->tags.ro_tags_size; k++)
                {
                    parser_info = (custom_parser_info_t *)pd->tags.ro_tags[k]->proto_ptr;
                    if (parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.ro_tags[k];
                    }
                }

                for (int k = 0; k < pd->tags.rw_tags_size; k++)
                {
                    parser_info = (custom_parser_info_t *)pd->tags.rw_tags[k]->proto_ptr;
                    if (parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.rw_tags[k];
                    }
                }
				/*
                if (adp != NULL)
                {
                    poll_info->fun = find_proto_can_adp_handle(adp, poll_info->id);
                    if (poll_info->fun != NULL)
                    {
                        proto_syslog(LOG_NOTICE, "find adp handle:0x%08x", poll_info->id);
                    }
                    else
                    {
                        proto_syslog(LOG_WARNING, "not find adp handle:0x%08x", poll_info->id);
                    }
                }*/
                poll_info->tag_sum = tag_arr_size;
                if (tag_arr_size > 0)
                {
                    poll_info->p_tag = (tag_t **)calloc(tag_arr_size, sizeof(tag_t *));
                    memcpy(poll_info->p_tag, tag_arr, tag_arr_size * sizeof(tag_t *));

                    for (int i = 0; i < tag_arr_size; i++)
                    {
                        parser_info = (custom_parser_info_t *)poll_info->p_tag[i]->proto_ptr;
                        proto_syslog(LOG_NOTICE, "--->tag:%s, id=0x%08x, byte_index = %d",
                                     poll_info->p_tag[i]->name, parser_info->id, parser_info->byte_index);
                    }
                }
            }
        }
        //
        for (int i = 0; i < pc->chunks_nonstop_size; i++)
        {
            if (pd == pc->chunks_nonstop[i]->dev_ptr)
            {
                proto_custom_poll *poll_info = (proto_custom_poll *)pc->chunks_nonstop[i]->chunk_proto_ptr;
                custom_parser_info_t *parser_info = NULL;
                tag_arr_size = 0;
                proto_syslog(LOG_NOTICE, "-->poll_info:id=0x%08x", poll_info->id);
                for (int k = 0; k < pd->tags.ro_tags_size; k++)
                {
                    parser_info = (custom_parser_info_t *)pd->tags.ro_tags[k]->proto_ptr;
                    if (parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.ro_tags[k];
                    }
                }

                for (int k = 0; k < pd->tags.rw_tags_size; k++)
                {
                    parser_info = (custom_parser_info_t *)pd->tags.rw_tags[k]->proto_ptr;
                    if (parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.rw_tags[k];
                    }
                }
				/*
                if (adp != NULL)
                {
                    poll_info->fun = find_proto_can_adp_handle(adp, poll_info->id);
                    if (poll_info->fun != NULL)
                    {
                        proto_syslog(LOG_NOTICE, "find adp handle:0x%08x", poll_info->id);
                    }
                    else
                    {
                        proto_syslog(LOG_WARNING, "not find adp handle:0x%08x", poll_info->id);
                    }
                }
                */
                poll_info->tag_sum = tag_arr_size;
                if (tag_arr_size > 0)
                {
                    poll_info->p_tag = (tag_t **)calloc(tag_arr_size, sizeof(tag_t *));
                    memcpy(poll_info->p_tag, tag_arr, tag_arr_size * sizeof(tag_t *));

                    for (int i = 0; i < tag_arr_size; i++)
                    {
                        parser_info = (custom_parser_info_t *)poll_info->p_tag[i]->proto_ptr;
                        proto_syslog(LOG_NOTICE, "--->tag:%s, id=0x%08x, byte_index = %d",
                                     poll_info->p_tag[i]->name, parser_info->id, parser_info->byte_index);
                    }
                }
            }
        }
    }

    free(tag_arr);
    tag_arr = NULL;
    return 0;
}

int ydt1363_channel_late_handle(channel_t *pc)
{
    tag_t **tag_arr = (tag_t **)calloc(RO_TAGS_MAX, sizeof(tag_t *));
    int tag_arr_size = 0;
    if (tag_arr == NULL)
    {
        proto_syslog(LOG_ERR, "calloc error");
        return -1;
    }

    for (int d = 0; d < pc->devs_size; d++)
    {
        device_t *pd = pc->devs[d];
        /*const proto_can_adp *adp = find_proto_can_adp(pd->use_template->adapter);
        if (adp != NULL)
        {
            proto_syslog(LOG_NOTICE, "find proto can adp ok, adp:%s", pd->use_template->adapter);
        }*/
        proto_syslog(LOG_NOTICE, "->dev:%s", pd->no);
        for (int i = 0; i < pc->chunks_interv_size; i++)
        {
            if (pd == pc->chunks_interv[i]->dev_ptr)
            {
                proto_ydt1363_poll *poll_info = (proto_ydt1363_poll *)pc->chunks_interv[i]->chunk_proto_ptr;
                ydt1363_parser_info_t *parser_info = NULL;
                tag_arr_size = 0;
                proto_syslog(LOG_NOTICE, "-->poll_info: id=0x%02x", poll_info->id);
                for (int k = 0; k < pd->tags.ro_tags_size; k++)
                {
                    parser_info = (ydt1363_parser_info_t *)pd->tags.ro_tags[k]->proto_ptr;
                    // 1.周期性查询命令匹配cid2，2.遥控/遥调命令匹配id
                    if (poll_info->id == 0 && parser_info->cid2 == poll_info->cid2)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.ro_tags[k];
                    }
                }

                for (int k = 0; k < pd->tags.rw_tags_size; k++)
                {
                    parser_info = (ydt1363_parser_info_t *)pd->tags.rw_tags[k]->proto_ptr;
                    if (poll_info->id != 0 && parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.rw_tags[k];
                    }
                }
				/*
                if (adp != NULL)
                {
                    poll_info->fun = find_proto_can_adp_handle(adp, poll_info->id);
                    if (poll_info->fun != NULL)
                    {
                        proto_syslog(LOG_NOTICE, "find adp handle:0x%08x", poll_info->id);
                    }
                    else
                    {
                        proto_syslog(LOG_WARNING, "not find adp handle:0x%08x", poll_info->id);
                    }
                }*/
                poll_info->tag_sum = tag_arr_size;
                if (tag_arr_size > 0)
                {
                    poll_info->p_tag = (tag_t **)calloc(tag_arr_size, sizeof(tag_t *));
                    memcpy(poll_info->p_tag, tag_arr, tag_arr_size * sizeof(tag_t *));
                }
            }
        }
        //
        for (int i = 0; i < pc->chunks_nonstop_size; i++)
        {
            if (pd == pc->chunks_nonstop[i]->dev_ptr)
            {
                proto_ydt1363_poll *poll_info = (proto_ydt1363_poll *)pc->chunks_nonstop[i]->chunk_proto_ptr;
                ydt1363_parser_info_t *parser_info = NULL;
                tag_arr_size = 0;
                proto_syslog(LOG_NOTICE, "-->poll_info: id=0x%02x", poll_info->id);
                for (int k = 0; k < pd->tags.ro_tags_size; k++)
                {
                    parser_info = (ydt1363_parser_info_t *)pd->tags.ro_tags[k]->proto_ptr;
                    if (poll_info->id == 0 && parser_info->cid2 == poll_info->cid2)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.ro_tags[k];
                    }
                }

                for (int k = 0; k < pd->tags.rw_tags_size; k++)
                {
                    parser_info = (ydt1363_parser_info_t *)pd->tags.rw_tags[k]->proto_ptr;
                    if (poll_info->id != 0 && parser_info->id == poll_info->id)
                    {
                        if (tag_arr_size >= RO_TAGS_MAX)
                        {
                            proto_syslog(LOG_ERR, "tag arr size error");
                            continue;
                        }

                        tag_arr[tag_arr_size++] = pd->tags.rw_tags[k];
                    }
                }
				/*
                if (adp != NULL)
                {
                    poll_info->fun = find_proto_can_adp_handle(adp, poll_info->id);
                    if (poll_info->fun != NULL)
                    {
                        proto_syslog(LOG_NOTICE, "find adp handle:0x%08x", poll_info->id);
                    }
                    else
                    {
                        proto_syslog(LOG_WARNING, "not find adp handle:0x%08x", poll_info->id);
                    }
                }
                */
                poll_info->tag_sum = tag_arr_size;
                if (tag_arr_size > 0)
                {
                    poll_info->p_tag = (tag_t **)calloc(tag_arr_size, sizeof(tag_t *));
                    memcpy(poll_info->p_tag, tag_arr, tag_arr_size * sizeof(tag_t *));
                }
            }
        }
    }

    free(tag_arr);
    tag_arr = NULL;
    return 0;
}

int proto_forward_later_handle(void)
{
    int err = 0;
    for (int c = 0; c < this_p->channels_size; c++)
    {
        channel_t *pc = this_p->channels[c];
        proto_syslog(LOG_NOTICE, "proto_forward later handle [%d], %s, %s", c, pc->channel, pc->protocol);
        switch (pc->proto_type)
        {
        case PROTOCOL_TYPE_BMSER_BIN:
            err += bmser_channel_late_handle(pc);
            break;

        case PROTOCOL_TYPE_MODBUS_RTU:
        case PROTOCOL_TYPE_MODBUS_TCP:
        case PROTOCOL_TYPE_MODBUS_RTU_RCRC:
            err += modbus_channel_late_handle(pc);
            break;

        case PROTOCOL_TYPE_CAN:
            err += can_channel_late_handle(pc);
            break;

        case PROTOCOL_TYPE_IEC104_CLIENT:
            // TODO
            break;
			
        case PROTOCOL_TYPE_CUSTOM:
            err += custom_channel_late_handle(pc);
            break;

        case PROTOCOL_TYPE_YDT1363_2014:
            err += ydt1363_channel_late_handle(pc);
            break;

        default:

            break;
        }
    }
    return err;
}

static int parse_template_cfg(proto_forward_t *var,device_t *dev, int prot_type, const char *cfg_file, template_t **temp_ptr)
{
    int re = -1;
    char *pdata = NULL;
    cJSON *root = NULL;
    cJSON *temp_info = NULL;
    template_t *ptr = calloc(sizeof(template_t), 1);

    *temp_ptr = ptr;

    if (*temp_ptr == NULL)
        goto out;

    int find_protocol = 0;
    proto_syslog(LOG_NOTICE, "template_file: %s", cfg_file);

    pdata = read_file_data(cfg_file);
    if (pdata == NULL)
    {
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s]不存在或内容不存在",cfg_file);
        proto_syslog(LOG_ERR, "read cfg file %s error", cfg_file);
        goto out;
    }

    root = json_parse_string_with_comments(pdata);
    if (!root)
    {
        proto_syslog(LOG_ERR, "parse pdata %s error", pdata);
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s]内容非JSON",cfg_file);
        goto out;
    }

    temp_info = cJSON_GetObjectItemCaseSensitive(root, "info");
    if (temp_info == NULL)
    {
        proto_syslog(LOG_ERR, "get temp info fail:%s", cfg_file);
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s]缺少info字段",cfg_file);
        goto out;
    }

    cJSON *j_tmp = cJSON_GetObjectItemCaseSensitive(temp_info, "adapter");
    if (!j_tmp || strlen(j_tmp->valuestring) == 0)
    {
        proto_syslog(LOG_WARNING, "get adapter fail:%s", cfg_file);
        snprintf(ptr->adapter, sizeof(ptr->adapter), "%s", COMMON_ADAPTER);
        BUSINESS_LOG(BLOG_WARNING, DC_START_ID_006, dev->no, "[数采]模板文件[%s]未获取到适配器使用默认适配器",cfg_file);
    }
    else
    {
        snprintf(ptr->adapter, sizeof(ptr->adapter), "%s", j_tmp->valuestring);
    }

    j_tmp = cJSON_GetObjectItemCaseSensitive(temp_info, "model");
    if (!j_tmp)
    {
        snprintf(ptr->model, sizeof(ptr->model), "*");
        BUSINESS_LOG(BLOG_WARNING, DC_START_ID_006, dev->no, "[数采]模板文件[%s]未获取到型号信息，告警会无法使用",cfg_file);
        proto_syslog(LOG_WARNING, "get model fail:%s", cfg_file);
    }
    else
    {
        snprintf(ptr->model, sizeof(ptr->model), "%s", j_tmp->valuestring);
    }

    j_tmp = cJSON_GetObjectItemCaseSensitive(temp_info, "compatible");
    if (!j_tmp)
    {
        proto_syslog(LOG_WARNING, "warning, no compatible fields:%s", cfg_file);
    }
    else
    {
#if 0 /* After some discussion, please refer to: https://www.tapd.cn/32297576/prong/stories/view/1132297576001006027 */
        int cmp_re = compare_version(SW_VERSION, j_tmp->valuestring);
        if (cmp_re == -1) // 固件版本比模板的兼容版本低
        {
            proto_syslog(LOG_ERR, "error: the template compatible too low, software version:%s, compatible:%s, cfg_file:%s", \
            SW_VERSION, j_tmp->valuestring, cfg_file);
            goto out;
        }
        else if (cmp_re < -1)
        {
            proto_syslog(LOG_WARNING, "warning: the template compatible can't compare, software version:%s, compatible:%s, cfg_file=%s", \
            SW_VERSION, j_tmp->valuestring, cfg_file);
        }
#endif
    }

    j_tmp = cJSON_GetObjectItemCaseSensitive(temp_info, "vendor");
    if (!j_tmp)
    {
        snprintf(ptr->vendor, sizeof(ptr->vendor), "*");
        BUSINESS_LOG(BLOG_WARNING, DC_START_ID_006, dev->no, "[数采]模板文件[%s]未获取到厂家信息，告警会无法使用",cfg_file);
        proto_syslog(LOG_WARNING, "warning, no vendor fields:%s", cfg_file);
    }
    else
    {
        snprintf(ptr->vendor, sizeof(ptr->vendor), "%s", j_tmp->valuestring);
    }

    cJSON *read_reg = cJSON_GetObjectItemCaseSensitive(root, "read_reg");
    if (!read_reg)
    {
        proto_syslog(LOG_ERR, "get object dev_class error");
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s]缺少 读点位内容[read_reg],此项必填",cfg_file);
        goto out;
    }

    cJSON *protocol = cJSON_GetObjectItemCaseSensitive(temp_info, "protocol");
    if (!protocol)
    {
        proto_syslog(LOG_ERR, "get temp protocol type error");
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s]缺少 协议[protocol],此项必填",cfg_file);
        goto out;
    }
    int protocol_cnt = cJSON_GetArraySize(protocol);
    if (protocol_cnt <= 0)
    {
        proto_syslog(LOG_ERR, "get temp protocol count error :%d", protocol_cnt);
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s]缺少 协议[protocol],此项必填",cfg_file);
        goto out;
    }
    proto_syslog(LOG_NOTICE, "temp protocol type:");
    for (int i = 0; i < MIN(protocol_cnt, PROTOCOL_TYPE_MAX); i++)
    {
        cJSON *p0 = cJSON_GetArrayItem(protocol, i);
        proto_syslog(LOG_NOTICE, "protocol:%d %s", i, p0->valuestring);
        if (prot_type == (ptr->proto_type[i] = get_protocal_type(p0->valuestring)))
        {
            find_protocol = 1;
            proto_syslog(LOG_NOTICE, "dev use temp's protocol:%s proto_type:%d", p0->valuestring, ptr->proto_type[i]);
            break;
        }
    }
    if (find_protocol != 1)
    {
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]模板文件[%s] 协议 当前程序无法识别 请确认是否支持或内容是否正确",cfg_file);
        proto_syslog(LOG_ERR, "dev can not find protocol in temp:%s", cfg_file);
        goto out;
    }
    get_info_user_def(&temp_info, dev, prot_type, ptr);

    get_poll_list(root, dev, prot_type, ptr, (char *)cfg_file);
    get_read_reg(root, dev, prot_type, ptr, (char *)cfg_file);
    get_write_reg(root, dev, prot_type, ptr, (char *)cfg_file);
    get_adapter(root, dev, prot_type, ptr); // 加载适配器层

    re = 0;

out:
    if ((re != 0) && ptr)
    {
        free(ptr);
        ptr = NULL;
        *temp_ptr = ptr;
    }
    if (root != NULL)
    {
        cJSON_Delete(root);
        root = NULL;
    }
    if (pdata != NULL)
    {
        free(pdata);
        pdata = NULL;
    }
    return re;
}

#if 0
static int delete_template_cfg(proto_forward_t *var)
{
    return 0;
}
#endif

static tag_t *sys_online_tags_create(channel_t *chan_ptr, proto_forward_t *var, device_t *dev,sqlite3 *db)
{
    tag_t *tag = calloc(sizeof(tag_t), 1);

    strcpy(tag->name, "Online");
    strcpy(tag->disp_name, _("Online"));
    tag->chan_ptr = chan_ptr;
    tag->dev_ptr = dev;
    tag->property = PROPERTY_REALDATA;
    tag->data_type = TYPE_TAG_INT;
    tag->history_type = HISTORY_TYPE_BOTH;
    snprintf(tag->dev_tag_name, sizeof(tag->dev_tag_name), "%s.%s", dev->no, tag->name);
    tag->scale = 1;
	tag->desc = strdup(_("0:Offline;1:Online;"));
#ifdef EN_OPCUA    
    {
        UA_SERVER_PARAM *ua_ser_para = var->ua_server_param;
        UA_Server *server = ua_ser_para->ua_server;
        char enum_name[DEV_NO_LEN + TAG_NAME_LEN + 10] = {0};
        snprintf(enum_name, sizeof(enum_name), "%s.%s_enum", dev->no, tag->name);
        tag->ua_enum_type = ua_creat_custom_enum(server, enum_name, _("0:Offline;1:Online;"));
        if (tag->ua_enum_type == NULL)
        {
            proto_syslog(LOG_ERR, "dev:%s tag:%s creat ua custom enum err!!!!!!!", dev->no, tag->name);
        }
        else
        {
            proto_syslog(LOG_NOTICE, "dev:%s tag:%s creat ua custom enum success", dev->no, tag->name);
        }
    }
#endif
    int ret = hash_intptr_addptr(&var->tag_map_hash, tag->dev_tag_name, strlen(tag->dev_tag_name), tag);
    if (ret < 0)
    {
        proto_syslog(LOG_ERR, "hash_intptr_addptr failed, key: %s", tag->dev_tag_name);
        goto ERR;
    }
#ifdef EN_OPCUA
    ua_server_add_data_item(var, dev, tag, PROPERTY_SYSTEM,READ_ONLY,db);
#endif
    proto_syslog(LOG_INFO, "Add success: %s", tag->dev_tag_name);
    return tag;

ERR:
    if (tag)
    {
        free(tag);
        tag = NULL;
    }
    return NULL;
}

static tag_t *sys_locked_tags_create(channel_t *chan_ptr, proto_forward_t *var, device_t *dev,sqlite3 *db)
{
    tag_t *tag = calloc(sizeof(tag_t), 1);

    strcpy(tag->name, STATE_CHANNELLOCK);
    strcpy(tag->disp_name, _(STATE_CHANNELLOCK));
    tag->chan_ptr = chan_ptr;
    tag->dev_ptr = dev;
    tag->property = PROPERTY_ALARM;
    tag->point_type = PROPERTY_ALARM;
    tag->data_type = TYPE_TAG_INT;
    tag->history_type = HISTORY_TYPE_OFF;
    snprintf(tag->dev_tag_name, sizeof(tag->dev_tag_name), "%s.%s", dev->no, tag->name);
	tag->desc = strdup("0:未锁定;1:锁定;");
#ifdef EN_OPCUA    
    {
        UA_SERVER_PARAM *ua_ser_para = var->ua_server_param;
        UA_Server *server = ua_ser_para->ua_server;
        char enum_name[DEV_NO_LEN + TAG_NAME_LEN + 10] = {0};
        snprintf(enum_name, sizeof(enum_name), "%s.%s_enum", dev->no, tag->name);
        tag->ua_enum_type = ua_creat_custom_enum(server, enum_name, _("0:Unlocked;1:Locked;"));
        if (tag->ua_enum_type == NULL)
        {
            proto_syslog(LOG_ERR, "dev:%s tag:%s creat ua custom enum err!!!!!!!", dev->no, tag->name);
        }
        else
        {
            proto_syslog(LOG_NOTICE, "dev:%s tag:%s creat ua custom enum success", dev->no, tag->name);
        }
    }
#endif
    int ret = hash_intptr_addptr(&var->tag_map_hash, tag->dev_tag_name, strlen(tag->dev_tag_name), tag);
    if (ret < 0)
    {
        proto_syslog(LOG_ERR, "hash_intptr_addptr failed, key: %s", tag->dev_tag_name);
        goto ERR;
    }
#ifdef EN_OPCUA
    ua_server_add_data_item(var, dev, tag, PROPERTY_SYSTEM,READ_ONLY,db);
#endif
    proto_syslog(LOG_INFO, "Add success: %s", tag->dev_tag_name);
    return tag;

ERR:
    if (tag)
    {
        free(tag);
        tag = NULL;
    }
    return NULL;
}


#ifdef EN_EMS
static int get_dev_alarm_cnt(char *name)
{
    proto_forward_t *var_proto = get_proto_forward_var();
    int alarm_cnt = 0;
    for (size_t i = 0; i < var_proto->channels_size; i++)
    {
        for (size_t j = 0; j < var_proto->channels[i]->devs_size; j++)
        {
            if (strcmp(name,var_proto->channels[i]->devs[j]->no) == 0)
            {
                for (size_t k = 0; k < var_proto->channels[i]->devs[j]->tags.ro_tags_size; k++)
                {
                    if ((var_proto->channels[i]->devs[j]->tags.ro_tags[k]->point_type == PROPERTY_ALARM))
                    {
                        if (var_proto->channels[i]->devs[j]->tags.ro_tags[k]->data_type == TYPE_TAG_INT)
                        {
                            if(var_proto->channels[i]->devs[j]->tags.ro_tags[k]->read_cache.to_int==1)
                            {
                                alarm_cnt++;
                            }
                                
                        }         
                        else      
                        {
                            if(check_float_is_1(var_proto->channels[i]->devs[j]->tags.ro_tags[k]->read_cache.to_float))
                            {
                                alarm_cnt++;
                            }
                        }  
                    }
                }

            } 
        }
    }
    return alarm_cnt;
}
#endif

static int cur_group_not_exist_pcs(cJSON *cfg, char *g)
{
    cJSON *dev_tree = cJSON_GetObjectItemCaseSensitive(cfg, "dev_tree");
    int array_size = cJSON_GetArraySize(dev_tree);
    cJSON *type = NULL;
    cJSON *it = NULL;
    //
    for (int i = 0; i < array_size; i++)
    {
        it = cJSON_GetArrayItem(dev_tree, i);
        type = cJSON_GetObjectItemCaseSensitive(it, "type");
        if ((type != NULL) && cJSON_IsString(type) && (strcmp(type->valuestring, "LC") == 0))
        {
            //
            cJSON *sub_dev_tree = cJSON_GetObjectItemCaseSensitive(it, "dev_tree");
            if (dev_tree != NULL)
            {
                int len = cJSON_GetArraySize(sub_dev_tree);
                for (int j = 0; j < len; j++)
                {
                    it = cJSON_GetArrayItem(sub_dev_tree, j);
                    type = cJSON_GetObjectItemCaseSensitive(it, "type");
                    if ((type != NULL) && cJSON_IsString(type) && (strcmp(type->valuestring, "PCS") == 0))
                    {
                        cJSON *group = cJSON_GetObjectItemCaseSensitive(it, "group");
                        if ((group != NULL) && cJSON_IsString(group) && (strcmp(group->valuestring, g) == 0))
                        {
                            return 1;
                        }
                    }
                }
            }
        }
    }
    ems_syslog(LOG_CRIT, "当前组[%s]没有PCS", g);
    return 0;
}
int add_ext_tag(channel_t *chan_ptr, device_t *dev)
{
    char file_path[256] = {0};
    snprintf(file_path, sizeof(file_path), LC_DEVS_DIR"/%s/ext_tag.json", dev->no);
    char *json_str = read_file_data(file_path);
    if (!json_str)
    {
        return -1;
    }
    cJSON *root = cJSON_Parse(json_str);
    free(json_str);
    if (!root)
    {
        proto_syslog(LOG_CRIT, "配置文件解析失败: %s", file_path);
        return -2;
    }
    int array_size = cJSON_GetArraySize(root);
    cJSON *it = NULL;
    for (int i = 0; i < array_size; i++)
    {
        it = cJSON_GetArrayItem(root, i);
        register_ext_tag(chan_ptr, dev, it);
    }
    return 0;
}

int add_bms_map_node(channel_t *chan_ptr, device_t *dev)
{
    const char *file_path = DEV_GROUP_INFO_PATH;
    char *json_str = read_file_data(file_path);
    if (!json_str)
    {
        return -1;
    }
    cJSON *root = cJSON_Parse(json_str);
    free(json_str);
    if (!root)
    {
        return -2;
    }
    cJSON *dev_tree = cJSON_GetObjectItemCaseSensitive(root, "dev_tree");
    int array_size = cJSON_GetArraySize(dev_tree);
    cJSON *target = NULL;
    cJSON *no = NULL;
    cJSON *it = NULL;
    //
    for (int i = 0; i < array_size; i++)
    {
        it = cJSON_GetArrayItem(dev_tree, i);
        no = cJSON_GetObjectItemCaseSensitive(it, "no");
        if ((no != NULL) && cJSON_IsString(no) && (strcmp(no->valuestring, dev->no) == 0))
        {
            target = it;
            dev_tree = cJSON_GetObjectItemCaseSensitive(target, "dev_tree");
            target = NULL;
            if (dev_tree != NULL)
            {
                int len = cJSON_GetArraySize(dev_tree);
                char *groups[32];
                int has_bau[32] = {0};
                int group_num = 0;

                struct {
                    char *group;
                    cJSON *bau_item;
                } bau_items[32];
                int bau_count = 0;
                struct {
                    char *group;
                    cJSON *bcu_item;
                } bcu_items[32];
                int bcu_count = 0;

                for (int j = 0; j < len && group_num < 32; j++)
                {
                    cJSON *subitem = cJSON_GetArrayItem(dev_tree, j);
                    cJSON *type = cJSON_GetObjectItemCaseSensitive(subitem, "type");
                    cJSON *subtype = cJSON_GetObjectItemCaseSensitive(subitem, "subtype");
                    cJSON *group = cJSON_GetObjectItemCaseSensitive(subitem, "group");
                    if ((type != NULL) && cJSON_IsString(type) && (strcmp(type->valuestring, "BMS") == 0) && 
                        (subtype != NULL) && cJSON_IsString(subtype) && (group != NULL) && cJSON_IsString(group))
                    {
                        int idx = -1;
                        for (int g = 0; g < group_num; g++)
                        {
                            if (strcmp(groups[g], group->valuestring) == 0)
                            {
                                idx = g;
                                break;
                            }
                        }
                        if (idx < 0 && group_num < 32)
                        {
                            groups[group_num] = group->valuestring;
                            idx = group_num++;
                        }
                        if (strcmp(subtype->valuestring, "BAU") == 0)
                        {
                            has_bau[idx] = 1;
                            if (cur_group_not_exist_pcs(root, group->valuestring) != 0)
                            {
                                bau_items[bau_count].group = group->valuestring;
                                bau_items[bau_count].bau_item = subitem;
                                bau_count++;
                            }
                        }
                        else if (strcmp(subtype->valuestring, "BCU") == 0)
                        {
                            bcu_items[bcu_count].group = group->valuestring;
                            bcu_items[bcu_count].bcu_item = subitem;
                            bcu_count++;
                        }
                    }
                }

                for (int i = 0; i < bau_count; i++)
                {
                    cJSON *no = cJSON_GetObjectItemCaseSensitive(bau_items[i].bau_item, "no");
                    cJSON *name = cJSON_GetObjectItemCaseSensitive(bau_items[i].bau_item, "name");
                    if (no && name)
                        register_ext_info(chan_ptr, dev, no->valuestring, no->valuestring, name->valuestring);
                }

                for (int g = 0; g < group_num; g++)
                {
                    if (has_bau[g])
                        continue;
                    int found_bcu = 0;
                    for (int k = 0; k < bcu_count; k++)
                    {
                        if (strcmp(bcu_items[k].group, groups[g]) == 0)
                        {
                            found_bcu = 1;
                            break;
                        }
                    }
                    if (found_bcu && cur_group_not_exist_pcs(root, groups[g]) != 0)
                    {
                        char bau_no[128];
                        snprintf(bau_no, sizeof(bau_no), "%s_BAU", groups[g]);
                        register_ext_info(chan_ptr, dev, bau_no, bau_no, bau_no);
                    }
                }

            }
            else
            {
                ems_syslog(LOG_CRIT, "设备[%s]无子设备树(dev_tree)", no->valuestring);
            }
            break;
        }
    }
    //
    cJSON_Delete(root);
    return 0;
}


int add_vir_bau_node(void *chan_ptr, device_t *dev) {
    if (strcmp(dev->dev_type, "EMS") != 0) {
        return -1;
    }
    const char *file_path = DEV_GROUP_INFO_PATH;
    char *json_str = read_file_data(file_path);
    if (!json_str) {
        return -1;
    }

    cJSON *root = cJSON_Parse(json_str);
    free(json_str);
    if (!root) {
        return -2;
    }

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

    typedef struct {
        char group[128];
        int bcu_count;
    } group_stat_t;

    group_stat_t group_stats[32] = {0};
    int group_stat_num = 0;
    int dev_tree_size = cJSON_GetArraySize(dev_tree);

    for (int i = 0; i < dev_tree_size; ++i) {
        cJSON *item = cJSON_GetArrayItem(dev_tree, i);
        cJSON *type = cJSON_GetObjectItem(item, "type");
        cJSON *subtype = cJSON_GetObjectItem(item, "subtype");
        cJSON *group = cJSON_GetObjectItem(item, "group");
        cJSON *parent = cJSON_GetObjectItem(item, "parent");
        if (!type || !cJSON_IsString(type) ||
            strcmp(type->valuestring, "BMS") != 0 ||
            !subtype || !cJSON_IsString(subtype) ||
            strcmp(subtype->valuestring, "BCU") != 0 ||
            !group || !cJSON_IsString(group)) {
            continue;
        }
        if (parent && cJSON_IsString(parent)) {
            continue;
        }
        int found = 0;
        for (int j = 0; j < group_stat_num; ++j) {
            if (strcmp(group_stats[j].group, group->valuestring) == 0) {
                group_stats[j].bcu_count++;
                found = 1;
                break;
            }
        }
        if (!found && group_stat_num < 32) {
            strncpy(group_stats[group_stat_num].group, group->valuestring, sizeof(group_stats[group_stat_num].group) - 1);
            group_stats[group_stat_num].bcu_count = 1;
            group_stat_num++;
        }
    }

    for (int g = 0; g < group_stat_num; ++g) {
        if (group_stats[g].bcu_count < 2)
            continue;

        char group_mp[160] = {0};
        snprintf(group_mp, sizeof(group_mp), "%s_BAU", group_stats[g].group);

        int bcu_global_index = 0;
        for (int i = 0; i < dev_tree_size; ++i) {
            cJSON *item = cJSON_GetArrayItem(dev_tree, i);
            cJSON *type = cJSON_GetObjectItem(item, "type");
            cJSON *subtype = cJSON_GetObjectItem(item, "subtype");
            cJSON *group = cJSON_GetObjectItem(item, "group");
            cJSON *no = cJSON_GetObjectItem(item, "no");
            cJSON *parent = cJSON_GetObjectItem(item, "parent");
            if (!type || !cJSON_IsString(type) ||
                strcmp(type->valuestring, "BMS") != 0 ||
                !subtype || !cJSON_IsString(subtype) ||
                strcmp(subtype->valuestring, "BCU") != 0 ||
                !group || !cJSON_IsString(group) ||
                strcmp(group->valuestring, group_stats[g].group) != 0 ||
                !no || !cJSON_IsString(no)) {
                continue;
            }
            if (parent && cJSON_IsString(parent)) {
                continue;
            }

            get_bcu_dev_info(no->valuestring, group_mp);
            bcu_global_index++;
        }
        // 注册虚拟BAU节点
        register_vir_bau_info(chan_ptr, dev, dev->no, group_mp, group_mp);
    }

    cJSON_Delete(root);
    return 0;
}

int read_alarm_cb(char *name, void *value_v)
{
    int *value = value_v;
    char dev_name[DEV_NAME_LEN] = {0};
    char *slip_p = NULL;
    char *end_p = strchr(name, '.');
    if (end_p == NULL)
    {
        return -1;
    }
    //
    do
    {
        slip_p = end_p;
        end_p = strchr(end_p + 1, '.');
    } while (end_p != NULL);
    memcpy(dev_name, name, slip_p - name);
#ifdef EN_EMS
    *value = get_dev_alarm_cnt(dev_name);
#else
    *value = 0;
#endif
    return 0;
}

static int dev_add_other_info(channel_t *chan_ptr, proto_forward_t *var, device_t *dev)
{
#ifdef EN_OPCUA
    char *p = dev->use_template->vendor;
    if (0 != ua_server_add_string_node(var, dev, "vendor", _("Vendor"), p))
    {
        proto_syslog(LOG_WARNING, "dev[%s] add vendor info error", dev->no);
    }

    p = dev->use_template->model;
    if (0 != ua_server_add_string_node(var, dev, "model", _("Model"), p))
    {
        proto_syslog(LOG_WARNING, "dev[%s] add model info error", dev->no);
    }
    
    total_cb_t *total_cb_i=calloc(sizeof(total_cb_t),1);  
    total_cb_i->onRead = read_alarm_cb;
    total_cb_i->onWrite = NULL;
    if (0 != add_alarm_num_to_node(var, dev, FAULT_WORDS, _("Number of fault words"), total_cb_i))
    {
        free(total_cb_i);
    }
#endif
    if ((get_cabinet_info()->ms_mode == MS_MODE_DC) && (dev->user_def != NULL) && (strstr(dev->user_def, "is_ems") != NULL))
    {
        add_bms_map_node(chan_ptr, dev);
    }
    else if(get_cabinet_info()->ms_mode == MS_MODE_DC)
    {
        add_vir_bau_node(chan_ptr, dev);
    }
    add_ext_tag(chan_ptr, dev);
    return 0;
}

static void extra_detail(tag_t *tag, device_t *dev, reg_info_t tag_info)
{
    for (size_t i = 0; i < tag_info.extra_num; i++)
    {
        proto_syslog(LOG_INFO, "tag_info.extra[i]: %s", tag_info.extra[i]);
        if (strcmp(tag_info.extra[i], "ct") == 0) // 电流变比
        {
            float ct = find_float_in_user_def(dev, "ct", 1);
            if(!check_float_is_1(ct))
            {
                tag->scale = tag->scale * ct;
            }
            proto_syslog(LOG_NOTICE, "%s.%s from extra get new ct:%f, now scale:%f", dev->no, tag->name, ct, tag->scale);
        }
        else if (strcmp(tag_info.extra[i], "pt") == 0) // 电压变比
        {
            float pt = find_float_in_user_def(dev, "pt", 1);
            if(!check_float_is_1(pt))
            {
                tag->scale = pt * tag->scale;
            }
            proto_syslog(LOG_NOTICE, "%s.%s from extra get new pt:%f, now scale:%f", dev->no, tag->name, pt, tag->scale);
        }
    }
}

static int get_poll_list_num(channel_t *chan_ptr,protocol_type_t proto_type,device_t *dev,  tag_t *tag ,void *proto_ptr, int readonly)
{
    switch (proto_type)
    {
    case PROTOCOL_TYPE_MODBUS_TCP:
    case PROTOCOL_TYPE_MODBUS_RTU:
    case PROTOCOL_TYPE_MODBUS_RTU_RCRC:
    {
        modbus_read_reg_info_t *reg_info = proto_ptr;
        for (size_t i = 0; i < chan_ptr->chunks_interv_size; i++)
        {
            if(chan_ptr->chunks_interv[i]->dev_ptr != dev) continue;

            modbus_poll_info_t *poll_info = chan_ptr->chunks_interv[i]->chunk_proto_ptr;
            if ((reg_info->func_code == poll_info->func_code) && (reg_info->reg_addr >= poll_info->reg_addr) && (reg_info->reg_addr < (poll_info->reg_addr + poll_info->size)))
            {
                tag->poll_list_type = 0;
                tag->poll_list_num = i;
                return i;
            }
        }
        for (size_t i = 0; i < chan_ptr->chunks_nonstop_size; i++)
        {
            if(chan_ptr->chunks_nonstop[i]->dev_ptr != dev) continue;

            modbus_poll_info_t *poll_info = chan_ptr->chunks_nonstop[i]->chunk_proto_ptr;
            if ((reg_info->func_code == poll_info->func_code) && (reg_info->reg_addr >= poll_info->reg_addr) && (reg_info->reg_addr < (poll_info->reg_addr + poll_info->size)))
            {
                tag->poll_list_type = 1;
                tag->poll_list_num = i;
                return i;
            }
        }
        return -1;
    }
    break;
    case PROTOCOL_TYPE_DLT645_2007:
    case PROTOCOL_TYPE_DLT645_1997:
    {
        dlt645_rw_info_t *dlt645_info = proto_ptr;
        for (size_t i = 0; i < chan_ptr->chunks_interv_size; i++)
        {
            if(chan_ptr->chunks_interv[i]->dev_ptr != dev) continue;
          
            dlt645_poll_info_t *dlt645_poll_info = chan_ptr->chunks_interv[i]->chunk_proto_ptr;
            if (dlt645_info->data_id == dlt645_poll_info->data_id)
            {
                tag->poll_list_type = 0;
                tag->poll_list_num = i;
                return i;
            }
        }
        for (size_t i = 0; i < chan_ptr->chunks_nonstop_size; i++)
        {
            if(chan_ptr->chunks_nonstop[i]->dev_ptr != dev) continue;

            dlt645_poll_info_t *dlt645_poll_info = chan_ptr->chunks_nonstop[i]->chunk_proto_ptr;
            if (dlt645_info->data_id == dlt645_poll_info->data_id)
            {
                tag->poll_list_type = 1;
                tag->poll_list_num = i;
                return i;
            }
        }
    }
    break;

    case PROTOCOL_TYPE_UPS:
    {
        ups_read_info_t *read_info = (ups_read_info_t*)proto_ptr;
        for (size_t i = 0; i < chan_ptr->chunks_interv_size; i++)
        {
            if(chan_ptr->chunks_interv[i]->dev_ptr != dev) continue;

            ups_poll_info_t *poll_info = chan_ptr->chunks_interv[i]->chunk_proto_ptr;
            if (memcmp(read_info->cmd, poll_info->cmd, poll_info->cmd_len) == 0)
            {
                tag->poll_list_type = 0;
                tag->poll_list_num = i;
                return i;
            }
        }
        for (size_t i = 0; i < chan_ptr->chunks_nonstop_size; i++)
        {
            if(chan_ptr->chunks_nonstop[i]->dev_ptr != dev) continue;

            ups_poll_info_t *poll_info = chan_ptr->chunks_nonstop[i]->chunk_proto_ptr;
            if (memcmp(read_info->cmd, poll_info->cmd, poll_info->cmd_len) == 0)
            {
                tag->poll_list_type = 1;
                tag->poll_list_num = i;
                return i;
            }
        }
    }break;

    case PROTOCOL_TYPE_EMS:
    case PROTOCOL_TYPE_OPCUA:
    case PROTOCOL_TYPE_LC:
        return 0;

    case PROTOCOL_TYPE_BMSER_BIN:
    {
        bmser_parser_info_t *read_info = (bmser_parser_info_t *)proto_ptr;
        for (size_t i = 0; i < chan_ptr->chunks_interv_size; i++)
        {
            if (chan_ptr->chunks_interv[i]->dev_ptr != dev)
            {
                continue;
            }
            bmser_poll_info_t *poll_info = (bmser_poll_info_t *)chan_ptr->chunks_interv[i]->chunk_proto_ptr;
            if (poll_info->block_id == read_info->block_id)
            {
                tag->poll_list_type = 0;
                tag->poll_list_num = i;
                return i;
            }
        }
        for (size_t i = 0; i < chan_ptr->chunks_nonstop_size; i++)
        {
            if (chan_ptr->chunks_nonstop[i]->dev_ptr != dev)
            {
                continue;
            }
            bmser_poll_info_t *poll_info = (bmser_poll_info_t *)chan_ptr->chunks_nonstop[i]->chunk_proto_ptr;
            if (poll_info->block_id == read_info->block_id)
            {
                tag->poll_list_type = 1;
                tag->poll_list_num = i;
                return i;
            }
        }
    }
    break;

    case PROTOCOL_TYPE_CAN:
    {
            can_parser_info_t *read_info = (can_parser_info_t *)proto_ptr;
            for (size_t i = 0; i < chan_ptr->chunks_interv_size; i++)
            {
                if (chan_ptr->chunks_interv[i]->dev_ptr != dev)
                {
                    continue;
                }
                proto_can_poll *poll_info = (proto_can_poll *)chan_ptr->chunks_interv[i]->chunk_proto_ptr;
                if (poll_info->id == read_info->id)
                {
                    tag->poll_list_type = 0;
                    tag->poll_list_num = i;
                    return i;
                }
            }
            for (size_t i = 0; i < chan_ptr->chunks_nonstop_size; i++)
            {
                if (chan_ptr->chunks_nonstop[i]->dev_ptr != dev)
                {
                    continue;
                }
                proto_can_poll *poll_info = (proto_can_poll *)chan_ptr->chunks_nonstop[i]->chunk_proto_ptr;
                if (poll_info->id == read_info->id)
                {
                    tag->poll_list_type = 1;
                    tag->poll_list_num = i;
                    return i;
                }
            }
    }
    break;

    case PROTOCOL_TYPE_IEC104_CLIENT:
        return 0;

    case PROTOCOL_TYPE_CUSTOM:
    {
            custom_parser_info_t *parser_info = (custom_parser_info_t *)proto_ptr;
            for (size_t i = 0; i < chan_ptr->chunks_interv_size; i++)
            {
                if (chan_ptr->chunks_interv[i]->dev_ptr != dev)
                {
                    continue;
                }
                proto_custom_poll *poll_info = (proto_custom_poll *)chan_ptr->chunks_interv[i]->chunk_proto_ptr;
                if (poll_info->id == parser_info->id)
                {
                    tag->poll_list_type = 0;
                    tag->poll_list_num = i;
                    return i;
                }
            }
            for (size_t i = 0; i < chan_ptr->chunks_nonstop_size; i++)
            {
                if (chan_ptr->chunks_nonstop[i]->dev_ptr != dev)
                {
                    continue;
                }
                proto_custom_poll *poll_info = (proto_custom_poll *)chan_ptr->chunks_nonstop[i]->chunk_proto_ptr;
                if (poll_info->id == parser_info->id)
                {
                    tag->poll_list_type = 1;
                    tag->poll_list_num = i;
                    return i;
                }
            }
    }
    break;

    case PROTOCOL_TYPE_YDT1363_2014:
    {
            ydt1363_parser_info_t *parser_info = (ydt1363_parser_info_t *)proto_ptr;
            for (size_t i = 0; i < chan_ptr->chunks_interv_size; i++)
            {
                if (chan_ptr->chunks_interv[i]->dev_ptr != dev)
                {
                    continue;
                }
                proto_ydt1363_poll *poll_info = (proto_ydt1363_poll *)chan_ptr->chunks_interv[i]->chunk_proto_ptr;
                // 引入tag readonly属性，防止匹配错poll
                if ((readonly == READ_ONLY && poll_info->id == 0 && poll_info->cid2 == parser_info->cid2) || (readonly == !READ_ONLY && poll_info->id != 0 && poll_info->id == parser_info->id))
                {
                    tag->poll_list_type = 0;
                    tag->poll_list_num = i;
                    return i;
                }
            }
            for (size_t i = 0; i < chan_ptr->chunks_nonstop_size; i++)
            {
                if (chan_ptr->chunks_nonstop[i]->dev_ptr != dev)
                {
                    continue;
                }
                proto_ydt1363_poll *poll_info = (proto_ydt1363_poll *)chan_ptr->chunks_nonstop[i]->chunk_proto_ptr;
                if ((readonly == READ_ONLY && poll_info->id == 0 && poll_info->cid2 == parser_info->cid2) || (readonly == !READ_ONLY && poll_info->id != 0 && poll_info->id == parser_info->id))
                {
                    tag->poll_list_type = 1;
                    tag->poll_list_num = i;
                    return i;
                }
            }
    }
    break;

    default:
        break;
    }

    return -1;
}



static tag_t *tags_create(channel_t *chan_ptr, protocol_type_t protocol_type,  proto_forward_t *var, device_t *dev, reg_info_t tag_info,int readonly,sqlite3 *db)
{
    int num = -1;
    tag_t *tag = calloc(sizeof(tag_t), 1);
    tag->primary = tag_info.primary;
    tag->offset = tag_info.offset;
    tag->scale = tag_info.scale;
    tag->proto_ptr = tag_info.proto_ptr;
    tag->funptr = tag_info.funptr;
    strncpy(tag->name, tag_info.name,TAG_NAME_LEN);
    strncpy(tag->disp_name, tag_info.disp_name,TAG_NAME_LEN);
    tag->chan_ptr = chan_ptr;
    tag->property = tag_info.property;
    tag->no_snapshoot = tag_info.no_snapshoot;
    tag->dev_ptr = dev;
    tag->data_type = tag_info.data_type;//tag_info.data_type;
    tag->history_type = tag_info.history_type;
    tag->point_type  = tag_info.property;
    tag->read_cache_time = clock_get_ms();
    if (tag_info.desc != NULL)
    {
#if EN_OPCUA
        tag->desc = strdup(tag_info.desc);
        if (var->ua_server_param == NULL)
        {

        }
        else
        {
            UA_SERVER_PARAM *ua_ser_para = var->ua_server_param;
            UA_Server *server = ua_ser_para->ua_server;
            char enum_name[TAG_NAME_LEN + DEV_NO_LEN + 10] = {0};
            snprintf(enum_name, sizeof(enum_name), "%s.%s_enum", dev->no, tag->name);
            tag->ua_enum_type = ua_creat_custom_enum(server, enum_name, tag_info.desc);
            if (tag->ua_enum_type == NULL)
            {
                proto_syslog(LOG_ERR, "dev:%s tag:%s creat ua custom enum err!!!!!!!", dev->no, tag->name);
            }
            else
            {
                proto_syslog(LOG_NOTICE, "dev:%s tag:%s creat ua custom enum success", dev->no, tag->name);
            }
        }
#endif
    }
    num = get_poll_list_num(chan_ptr,protocol_type, dev,tag, tag_info.proto_ptr, readonly);
    extra_detail(tag, dev, tag_info);
    if (num < 0)
    {
        tag->poll_list_num = -1;
        tag->poll_list_type = -1;
        proto_syslog(LOG_WARNING, "dev:%s, %s's poll_list no find", dev->no, tag_info.name);
    }
    else
    {
        proto_syslog(LOG_DEBUG, "%s get poll_list %d", tag_info.name, tag->poll_list_num);
    }
    snprintf(tag->dev_tag_name, sizeof(tag->dev_tag_name), "%s.%s", dev->no, tag_info.name);
    int ret = hash_intptr_addptr(&var->tag_map_hash, tag->dev_tag_name, strlen(tag->dev_tag_name), tag);
    if (ret < 0)
    {
        proto_syslog(LOG_ERR, "hash_intptr_addptr failed, key: %s", tag->dev_tag_name);
        goto ERR;
    }
#if EN_OPCUA
    ua_server_add_data_item(var, dev, tag, tag_info.property ,readonly, db);
#endif
    proto_syslog(LOG_INFO, "Add success: %s", tag->dev_tag_name);
    return tag;

ERR:
    if (tag)
    {
        free(tag);
        tag = NULL;
    }
    return NULL;
}

static dlt645_dev_attr *creat_dlt645_dev_attr(char *addr_str)
{
    dlt645_dev_attr *arr = NULL;
    int len = strlen(addr_str);
    if (len == 12)
    {
        arr = calloc(sizeof(dlt645_dev_attr), 1);
        if (arr != NULL)
        {
            len = (len / 2) - 1;
            for (int i = 0; i <= len; i++) // 倒序
            {
                arr->bcd_addr[len - i] = (addr_str[i * 2] - '0') << 4 | (addr_str[(i * 2) + 1] - '0');
            }
        }
    }
    return arr;
}

#if 1 // 电表时间同步适配
static int merter_sync_time_tags_create(channel_t *chan_ptr, proto_forward_t *var, device_t *dev, void *db)
{
    tag_t *tag = calloc(sizeof(tag_t), 1);
    dev->adp->tag = tag;

    strcpy(tag->name, "SyncTime");
    strcpy(tag->disp_name, _("Synchronize time"));
    tag->chan_ptr = chan_ptr;
    tag->dev_ptr = dev;
    tag->data_type = TYPE_TAG_INT;
    tag->history_type = HISTORY_TYPE_CHANGE;
    tag->no_fetch = 1;
    tag->proto_ptr = NULL;
    tag->funptr = NULL;
    snprintf(tag->dev_tag_name, sizeof(tag->dev_tag_name), "%s.%s", dev->no, tag->name);

    //char enum_name[TAG_NAME_LEN + DEV_NO_LEN + 10] = {0};
    //snprintf(enum_name, sizeof(enum_name), "%s.%s_enum", dev->no, tag->name);
    tag->ua_enum_type = NULL;
    int ret = hash_intptr_addptr(&var->tag_map_hash, tag->dev_tag_name, strlen(tag->dev_tag_name), tag);
    if (ret < 0)
    {
        proto_syslog(LOG_ERR, "hash_intptr_addptr failed, key: %s", tag->dev_tag_name);
        dev->adp->tag = NULL;
        free(tag);
        return -1;
    }
#ifdef EN_OPCUA
    ua_server_add_data_item(var, dev, tag, PROPERTY_REALDATA, 0, (sqlite3*)db);
#endif
    proto_syslog(LOG_INFO, "Add success: %s", tag->dev_tag_name);
    return 0;
}

static bmser_dev_attr *creat_bmser_dev_attr(char *addr_str)
{
    bmser_dev_attr *arr = calloc(1, sizeof(bmser_dev_attr));
    snprintf(arr->addr.str, sizeof(arr->addr.str), "%s", addr_str);
    arr->addr.no = analysis_addr(arr->addr.str);
    arr->online = -1;
    return arr;
}

static char *at_at182g_meter_compatible[] = {"at_at182g", NULL};
static device_adapter at_at182g_meter_adp = {
    .compatible = at_at182g_meter_compatible,
    .tags_create = merter_sync_time_tags_create,
    .handle = meter_at_at182g_adp_handle,
};

static char *acrel_dtsd1352_meter_compatible[] = {"acrel_dtsd1352", NULL};
static device_adapter acrel_dtsd1352_meter_adp = {
    .compatible = acrel_dtsd1352_meter_compatible,
    .tags_create = merter_sync_time_tags_create,
    .handle = meter_acrel_dtsd1352_adp_handle,
};

static char *dlt645_meter_compatible[] = {"dlt645", NULL};
static device_adapter dlt645_meter_adp = {
    .compatible = dlt645_meter_compatible,
    .tags_create = merter_sync_time_tags_create,
    .handle = meter_dlt645_common_adp_handle,
};
static device_adapter *dev_adp[] = {
    &at_at182g_meter_adp,
    &acrel_dtsd1352_meter_adp,
    &dlt645_meter_adp,
};

device_adapter *device_adapter_probe(const char *adp_name)
{
    proto_syslog(LOG_NOTICE, "adp_name = %s", adp_name);
    for (int i = 0; i < sizeof(dev_adp) / sizeof(dev_adp[0]); i++)
    {
        char **pp = dev_adp[i]->compatible;
        while ((pp != NULL) && (*pp != NULL))
        {
            proto_syslog(LOG_NOTICE, "*pp = %s", *pp);
            if (strcmp(*pp, adp_name) == 0)
            {
                return dev_adp[i];
            }
            pp++;
        }
    }

    return NULL;
}
#endif

static void free_dev(device_t *dev)
{
	if (dev->dev_type)
		free(dev->dev_type);
	if (dev->dev_child_type)
		free(dev->dev_child_type);
	if (dev->user_def)
		free(dev->user_def);
	if (dev->use_template)
		free(dev->use_template);
	if (dev->proto_ptr)
		free(dev->proto_ptr);
	if (dev->adp)
		free(dev->adp);
	if (dev->ext_par)
		free(dev->ext_par);
	free(dev);
}

static device_t *device_create(cJSON *dev_info)
{
    char template[64] = {0};
    device_t *dev = calloc(sizeof(device_t), 1);
    
    char protocol[PROTOCOL_NAME_LEN] = {};
    GET_JSON_VALUE_STRING(dev_info, "protocol", protocol);
    int proto_type = get_protocal_type(protocol);

    GET_JSON_VALUE_DY_STRING(dev_info, "user_def", dev->user_def);
    if(dev->user_def!=NULL)
    analysis_user_def(dev);

    dev->timeout = find_int_in_user_def(dev,"frame_timeout",5000);
    dev->interval =  find_int_in_user_def(dev,"frame_interval",20);
    dev->write_gap = find_int_in_user_def(dev,"frame_wgap", -1);
    dev->base_addr = find_int_in_user_def(dev,"base_addr", 0);
    
    GET_JSON_VALUE_STRING(dev_info, "addr", dev->addr);
    GET_JSON_VALUE_DY_STRING(dev_info, "type", dev->dev_child_type);
    GET_JSON_VALUE_STRING(dev_info, "no", dev->no);
    GET_JSON_VALUE_STRING(dev_info, "name", dev->disp_name);
    GET_JSON_VALUE_STRING(dev_info, "template", template);

    cJSON *it_json = cJSON_GetObjectItemCaseSensitive(dev_info, "period");
    if ((it_json != NULL) && cJSON_IsNumber(it_json))
    {
        dev->sample_period = it_json->valueint;
        proto_syslog(LOG_INFO, "%s set value %d as sample period", dev->no, dev->sample_period);
    }
    else
    {
        dev->sample_period = default_sample_rate;
        proto_syslog(LOG_NOTICE, "%s set default value %d as sample period", dev->no, dev->sample_period);
    }

    dev->proto_type = proto_type;
    switch (dev->proto_type)
    {
    case PROTOCOL_TYPE_MODBUS_RTU:
    case PROTOCOL_TYPE_MODBUS_TCP:
    case PROTOCOL_TYPE_MODBUS_RTU_RCRC:
    {
        // 没有使能默认值
        if (!dev->addr || strlen(dev->addr) <= 0)
        {
            dev->proto_ptr = analysis_addr_with_p("1");
            BUSINESS_LOG(BLOG_WARNING, DC_START_ID_005, NULL, "[数采]设备[%s]MODBUS地址异常使用默认值1",dev->no);
        }
        else
        {
            uint32_t* addr_tmp = calloc(sizeof(uint32_t),1);
            *addr_tmp = analysis_addr(dev->addr);
            if(*addr_tmp > MODBUS_MAX_DEV_ADDR)
            {
                *addr_tmp = MODBUS_MAX_DEV_ADDR;
            }
            dev->proto_ptr = addr_tmp;
        }
        proto_syslog(LOG_INFO, "dev->proto_ptr:dev_addr=%u", *(uint32_t*)dev->proto_ptr);
    }
    break;

    case PROTOCOL_TYPE_DLT645_2007:
    case PROTOCOL_TYPE_DLT645_1997:
    {
        dlt645_dev_attr *attr = creat_dlt645_dev_attr(dev->addr);
        if (attr == NULL)
        {
            proto_syslog(LOG_ERR, "dlt645 device:%s, creat_dlt645_dev_attr error!!!", dev->no);
            BUSINESS_LOG(LOG_ERR, DC_START_ID_005, NULL, "[数采]设备[%s]DLT645地址异常,将不会创建此设备",dev->no);
            goto ERR;
        }
        dev->proto_ptr = attr;
        proto_syslog(LOG_INFO, "dev->proto_ptr:dev_addr=%s", dev->addr);
    }
    break;

    case PROTOCOL_TYPE_EMS:
    case PROTOCOL_TYPE_OPCUA:
    case PROTOCOL_TYPE_LC:
    break;

    case PROTOCOL_TYPE_UPS:
    //没用设备信息
    dev->proto_ptr = NULL;
    break;

    case PROTOCOL_TYPE_BMSER_BIN:
    {
        bmser_dev_attr *attr = creat_bmser_dev_attr(dev->addr);
        if (attr == NULL)
        {
            BUSINESS_LOG(LOG_ERR, DC_START_ID_005, NULL, "[数采]设备[%s]BMSER_BIN地址异常,将不会创建此设备",dev->no);
            proto_syslog(LOG_ERR, "dlt645 device:%s, creat_bmser_dev_attr error!!!", dev->no);
            goto ERR;
        }
        dev->proto_ptr = attr;
        proto_syslog(LOG_INFO, "dev->proto_ptr:dev_addr=%s", attr->addr.str);
    }
    break;

    case PROTOCOL_TYPE_CAN:
    {
        // 没有使能默认值
        if (!dev->addr || strlen(dev->addr) <= 0)//can无地址
            dev->proto_ptr = analysis_addr_with_p("1");
        else
            dev->proto_ptr = analysis_addr_with_p(dev->addr);
        proto_syslog(LOG_INFO, "dev->proto_ptr:dev_addr=%u", *(uint32_t *)dev->proto_ptr);
    }
    break;

    case PROTOCOL_TYPE_IEC104_CLIENT:
        // parse common address for IEC104:
        if (!dev->addr || strlen(dev->addr) <= 0)
        {
            dev->proto_ptr = analysis_addr_with_p("1");
            BUSINESS_LOG(BLOG_WARNING, DC_START_ID_005, NULL, "[数采]设备[%s]IEC104地址异常,使用默认地址1",dev->no);
        }
        else
            dev->proto_ptr = analysis_addr_with_p(dev->addr);
        proto_syslog(LOG_ERR, "IEC104 client, dev->proto_ptr: dev_addr=%u", *(uint32_t*)dev->proto_ptr);
        break;

    case PROTOCOL_TYPE_CUSTOM:
    {
        // 没有使能默认值
        if (!dev->addr || strlen(dev->addr) <= 0)
        {
            dev->proto_ptr = analysis_addr_with_p("1");dev->proto_ptr = analysis_addr_with_p("1");
            BUSINESS_LOG(BLOG_WARNING, DC_START_ID_005, NULL, "[数采]设备[%s]自定义参数异常,使用默认地址1",dev->no);
        }
        else
            dev->proto_ptr = analysis_addr_with_p(dev->addr);
        proto_syslog(LOG_INFO, "dev->proto_ptr:dev_addr=%u", *(uint32_t *)dev->proto_ptr);
    }
    break;

    case PROTOCOL_TYPE_YDT1363_2014:
    {
        // 没有使能默认值
        if (!dev->addr || strlen(dev->addr) <= 0)
        {
            dev->proto_ptr = analysis_addr_with_p("1");
            BUSINESS_LOG(BLOG_WARNING, DC_START_ID_005, NULL, "[数采]设备[%s]YDT1363地址异常,使用默认地址1",dev->no);
        }
        else{
            uint32_t* addr_tmp = calloc(sizeof(uint32_t), 1);
            *addr_tmp = analysis_addr(dev->addr);
            *addr_tmp = (*addr_tmp >= YDT1363_MAX_DEV_ADDR) ? YDT1363_MAX_DEV_ADDR - 1 : *addr_tmp;
            *addr_tmp = (*addr_tmp <= YDT1363_MIN_DEV_ADDR) ? YDT1363_MIN_DEV_ADDR + 1 : *addr_tmp;
            dev->proto_ptr = addr_tmp;
        }
        proto_syslog(LOG_INFO, "dev->proto_ptr:dev_addr=%u", *(uint32_t *)dev->proto_ptr);
    }
    break;

    default:
        proto_syslog(LOG_ERR, "unsupported this protocol:%s", protocol);
        BUSINESS_LOG(LOG_ERR, DC_START_ID_005, NULL, "[数采]设备[%s]协议不支持",dev->no);
        goto ERR;
        break;
    }
    return dev;
ERR:
    if (dev && dev->no)
    {
        BUSINESS_LOG(LOG_ERR, DC_START_ID_005, NULL, "[数采]设备[%s]创建失败",dev->no);
    }
    if (dev != NULL)
    {
        free_dev(dev);
        dev = NULL;
    }
    
    return NULL;
}

static void *creat_channle_attr(channel_t *chan_ptr)
{
    void *re = NULL;
    switch (chan_ptr->proto_type)
    {
    case PROTOCOL_TYPE_MODBUS_RTU:
    {
        modbus_proto *mt = calloc(sizeof(modbus_proto), 1);
         if (mt == NULL)
        {
            proto_syslog(LOG_ERR, "modbus_proto calloc error!!!");
        }
        else
        {
            if (strstr(chan_ptr->channel, ":") == NULL) // 不是ip地址格式
            {
                mt->type = MDBUS_RTU;
                mt->dev_or_ip = chan_ptr->channel;
            }
            else
            {
                parse_host_port(chan_ptr->channel, &mt->dev_or_ip, &mt->port);
                mt->type = MDBUS_RTU_OVER_TCP;
            }
            re = mt;
        }
    }
    break;

    case PROTOCOL_TYPE_MODBUS_TCP:
    {
        modbus_proto *mt = calloc(sizeof(modbus_proto), 1);
        if (mt == NULL)
        {
            proto_syslog(LOG_ERR, "modbus_proto calloc error!!!");
        }
        else
        {
            parse_host_port(chan_ptr->channel, &mt->dev_or_ip, &mt->port);
            mt->type = MDBUS_TCP;
            re = mt;
        }
    }
    break;

    case PROTOCOL_TYPE_MODBUS_RTU_RCRC:
    {
        modbus_proto *mt = calloc(sizeof(modbus_proto), 1);
         if (mt == NULL)
        {
            proto_syslog(LOG_ERR, "modbus_proto calloc error!!!");
        }
        else
        {
            mt->type = MDBUS_RTU_RCRC;
            mt->dev_or_ip = chan_ptr->channel;
            re = mt;
        }
    }
    break;

    case PROTOCOL_TYPE_DLT645_2007:
    case PROTOCOL_TYPE_DLT645_1997:
    {
        dlt645_proto_t *tmp = calloc(sizeof(dlt645_proto_t), 1);
        if (tmp == NULL)
        {
            proto_syslog(LOG_ERR, "dlt645_proto_t calloc error!!!");
        }
        else
        {
            if (strstr(chan_ptr->channel, ":") == NULL) // 不是ip地址格式
            {
                tmp->type = DLT645_UART;
                tmp->dev_or_ip = chan_ptr->channel;
            }
            else
            {
                parse_host_port(chan_ptr->channel, &tmp->dev_or_ip, &tmp->port);
                tmp->type = DLT645_TCP;
            }

            proto_syslog(LOG_DEBUG, "channel info:proto=dlt645,type=%d, ip_or_dev=%s, port=%d",
                    tmp->type, tmp->dev_or_ip, tmp->port);
            re = tmp;
        }
    }
    break;
    case PROTOCOL_TYPE_EMS:
        re = calloc(sizeof(int),1);
        break;
    case PROTOCOL_TYPE_OPCUA:
        re = calloc(1, sizeof(opcua_write_list));
        break;
    case PROTOCOL_TYPE_LC:
        {
            int *tmp = calloc(sizeof(int),1);
            re = tmp;
        }
        break;
    case PROTOCOL_TYPE_UPS:
    {
        ups_proto_t *tmp = calloc(sizeof(ups_proto_t), 1);
        if (tmp == NULL)
        {
            proto_syslog(LOG_ERR, "ups_proto_t calloc error!!!");
        }
        else
        {
            if (strstr(chan_ptr->channel, ":") == NULL) // 不是ip地址格式
            {
                tmp->type = UPS_UART;
                tmp->dev_or_ip = chan_ptr->channel;
            }
            else
            {
                parse_host_port(chan_ptr->channel, &tmp->dev_or_ip, &tmp->port);
                tmp->type = UPS_TCP;
            }

            proto_syslog(LOG_DEBUG, "channel info:proto=ups,type=%d, ip_or_dev=%s, port=%d",
                    tmp->type, tmp->dev_or_ip, tmp->port);
            re = tmp;
        }
    }break;

    case PROTOCOL_TYPE_BMSER_BIN:
    {
        bmser_proto_s *tmp = calloc(sizeof(bmser_proto_s), 1);
        if (tmp == NULL)
        {
            proto_syslog(LOG_ERR, "bmser_proto_s calloc error!!!");
        }
        else
        {
            parse_host_port(chan_ptr->channel, &tmp->ip, &tmp->port);
            proto_syslog(LOG_DEBUG, "channel info:proto=%s, ip_or_dev=%s, port=%d",
                    get_protocal_string(PROTOCOL_TYPE_BMSER_BIN), tmp->ip, tmp->port);
            re = tmp;
        }
    }break;

    case PROTOCOL_TYPE_CAN:
    {
        can_proto_s *tmp = calloc(sizeof(can_proto_s), 1);
        if (tmp == NULL)
        {
            proto_syslog(LOG_ERR, "can_proto_s calloc error!!!");
        }
        else
        {
            snprintf(tmp->dev, sizeof(tmp->dev), "%s", chan_ptr->channel);
            proto_syslog(LOG_DEBUG, "channel info:proto=%s, dev=%s",
                         get_protocal_string(PROTOCOL_TYPE_CAN), tmp->dev);
            re = tmp;
        }
    }
    break;

    case PROTOCOL_TYPE_IEC104_CLIENT:
    {
        iec104_proto * tmp = (iec104_proto *) calloc(0x1, sizeof(iec104_proto));
        if (tmp == NULL) {
            proto_syslog(LOG_ERR, "failed to allocate memory!!!");
        } else {
            parse_host_port(chan_ptr->channel, &tmp->iec104_ip, &tmp->iec104_port);
            re = tmp;
        }
        break;
    }

    case PROTOCOL_TYPE_CUSTOM:
    {
        custom_proto_t *tmp = calloc(sizeof(custom_proto_t), 1);
        if (tmp == NULL)
        {
            proto_syslog(LOG_ERR, "custom_proto_t calloc error!!!");
        }
        else
        {
            if (strstr(chan_ptr->channel, ":") == NULL) // 不是ip地址格式
            {
                tmp->type = CUS_UART;
                tmp->dev_or_ip = chan_ptr->channel;
            }
            else
            {
                parse_host_port(chan_ptr->channel, &tmp->dev_or_ip, &tmp->port);
                tmp->type = CUS_TCP;
            }

            proto_syslog(LOG_DEBUG, "channel info:proto=custom, type=%d, ip_or_dev=%s, port=%d",
                    tmp->type, tmp->dev_or_ip, tmp->port);
            re = tmp;
        }
		break;
    }

    case PROTOCOL_TYPE_YDT1363_2014:
    {
        ydt1363_proto_t *tmp = calloc(sizeof(ydt1363_proto_t), 1);
        if (tmp == NULL)
        {
            proto_syslog(LOG_ERR, "ydt1363_proto_t calloc error!!!");
        }
        else
        {
            if (strstr(chan_ptr->channel, ":") == NULL) // 不是ip地址格式
            {
                tmp->type = YDT1363_UART;
                tmp->dev_or_ip = chan_ptr->channel;
            }
            else
            {
                parse_host_port(chan_ptr->channel, &tmp->dev_or_ip, &tmp->port);
                tmp->type = YDT1363_TCP;
            }

            proto_syslog(LOG_DEBUG, "channel info:proto=ydt1363, type=%d, ip_or_dev=%s, port=%d",
                    tmp->type, tmp->dev_or_ip, tmp->port);
            re = tmp;
        }
		break;
    }

    default:
        proto_syslog(LOG_ERR, "unsupported this proto_type:%d", chan_ptr->proto_type);
        break;
    }
    
    return re;
}

int find_dev_channel_pr_by_dev_no(proto_forward_t *var,char*name,channel_t **chan_pr,device_t **dev_pr)
{
    for(int i = 0 ; i<var->channels_size ;i++)
    {
        for(int j = 0 ; j<var->channels[i]->devs_size ;j++)
        {
            if(strcmp(var->channels[i]->devs[j]->no,name)==0)
            {
                proto_syslog(LOG_NOTICE,"find dev :%s 's channel pr\n",name);
                *chan_pr = var->channels[i];
                *dev_pr = var->channels[i]->devs[j];
                return 0;
            }
        }
    }
    return -1;
}


int set_sample_period_by_dev_no(proto_forward_t *var,char*name,int sample_period)
{
    channel_t *chan_pr;
    device_t *dev_pr;
    if(find_dev_channel_pr_by_dev_no(var,name,&chan_pr,&dev_pr)<0)
    {
        proto_syslog(LOG_INFO,"no find :%s\n",name);
        return -1;
    }
    dev_pr->sample_period = sample_period;

    return 0;
}


int dev_tags_creat_and_add_to_channle(device_t *dev, template_t *temp_ptr, channel_t *chan_ptr)
{
    if ((chan_ptr == NULL)||(dev == NULL)||(temp_ptr == NULL))
    {
        return -1;
    }
    
    proto_forward_t *var = (proto_forward_t*)chan_ptr->var;
    int i = 0;

    dev->chunks_interv = &chan_ptr->chunks_interv[chan_ptr->chunks_interv_size];
    dev->chunks_nonstop = &chan_ptr->chunks_nonstop[chan_ptr->chunks_nonstop_size];
    // 解析轮询点位
    dev->min_scan_rate = 999999999;
    for (i = 0; i < temp_ptr->poll_list_size; i++)
    {
        if (!temp_ptr->poll_list[i].proto_ptr)
        {
            proto_syslog(LOG_ERR, "invalid poll config at template!");
            continue;
        }
        if (temp_ptr->poll_list[i].scan_rate < MINSCANTIME)
        {
            if (chan_ptr->chunks_nonstop_size < NUM_POLL_CHUNkS_MAX)
            {
                chan_ptr->chunks_nonstop[chan_ptr->chunks_nonstop_size] = calloc(sizeof(chunk_nonstop_t), 1);
                chan_ptr->chunks_nonstop[chan_ptr->chunks_nonstop_size]->dev_ptr = dev;
                chan_ptr->chunks_nonstop[chan_ptr->chunks_nonstop_size]->chunk_proto_ptr = temp_ptr->poll_list[i].proto_ptr;
                chan_ptr->chunks_nonstop_size++;
                chan_ptr->chunks_nonstop_index = 0;
            }
            else
            {
                proto_syslog(LOG_WARNING, "chunks_nonstop_size is too large!");
            }
        }
        else
        {
            if (chan_ptr->chunks_interv_size < NUM_POLL_CHUNkS_MAX)
            {
                chan_ptr->chunks_interv[chan_ptr->chunks_interv_size] = calloc(sizeof(chunk_interv_t), 1);
                chan_ptr->chunks_interv[chan_ptr->chunks_interv_size]->last_poll = 0; //为了连接建立后第一个轮询更快,
                chan_ptr->chunks_interv[chan_ptr->chunks_interv_size]->poll_interv = temp_ptr->poll_list[i].scan_rate;
                chan_ptr->chunks_interv[chan_ptr->chunks_interv_size]->dev_ptr = dev;
                chan_ptr->chunks_interv[chan_ptr->chunks_interv_size]->chunk_proto_ptr = temp_ptr->poll_list[i].proto_ptr;
                chan_ptr->chunks_interv_size++;
            }
            else
            {
                proto_syslog(LOG_WARNING, "chunks_interv_size is too large!");
            }
        }
        // 取设备的采集最小间隔
        dev->min_scan_rate = MIN(dev->min_scan_rate, temp_ptr->poll_list[i].scan_rate);
    }
    //
    chunk_interv_t **chunks_interv_end = &chan_ptr->chunks_interv[chan_ptr->chunks_interv_size];
    chunk_nonstop_t **chunks_nonstop_end = &chan_ptr->chunks_nonstop[chan_ptr->chunks_nonstop_size];
    dev->chunks_interv_size = chunks_interv_end - dev->chunks_interv;
    dev->chunks_nonstop_size = chunks_nonstop_end - dev->chunks_nonstop;
    proto_syslog(LOG_WARNING, "dev:%s,chunks_interv_size:%d,chunks_nonstop_size:%d", dev->no, dev->chunks_interv_size, dev->chunks_nonstop_size);
    //
    dev->min_scan_rate = dev->min_scan_rate < MINSCANTIME ? MINSCANTIME : dev->min_scan_rate;
    sqlite3 *db = NULL;
#ifdef EN_SQLITE
#ifdef DBFILE
    int ret = sqlite3_open(DBFILE, &db);
    if (ret < 0)
        proto_syslog(LOG_ERR, "OPEN " DBFILE " ERROR!");
#endif
#endif
    proto_syslog(LOG_INFO, "ADD SYSTEM INFO TAG:");
    {
        tag_t *tag = sys_online_tags_create(chan_ptr, var, dev, db);
        dev->tags.sys_info_tags[dev->tags.sys_info_tags_size] = tag;
        dev->tags.sys_info_tags_size++;
    }
    {
        tag_t *tag = sys_locked_tags_create(chan_ptr, var, dev, db);
        dev->tags.sys_info_tags[dev->tags.sys_info_tags_size] = tag;
        dev->tags.sys_info_tags_size++;
    }
    proto_syslog(LOG_INFO, "ADD READ TAG:");
    for (i = 0; i < temp_ptr->read_tags_size; i++)
    {
        tag_t *tag = tags_create(chan_ptr, chan_ptr->proto_type, var, dev, temp_ptr->read_tags[i]->read_reg,READ_ONLY,db);
        // 更新指针
        dev->tags.ro_tags[dev->tags.ro_tags_size] = tag;
        dev->tags.ro_tags_size++;
    }
    proto_syslog(LOG_INFO, "ADD WRITE TAG:");
    for (i = 0; i < temp_ptr->write_tags_size; i++)
    {
        tag_t *tag = tags_create(chan_ptr, chan_ptr->proto_type, var, dev, temp_ptr->write_tags[i]->write_reg,!READ_ONLY,db);
        tag->no_fetch = temp_ptr->write_tags[i]->no_fetch;
        // 更新指针
        dev->tags.rw_tags[dev->tags.rw_tags_size] = tag;
        dev->tags.rw_tags_size++;
    }

    if (dev->adp)
    {
        if(0 == dev->adp->tags_create(chan_ptr, var, dev, db))
        {
            proto_syslog(LOG_INFO, "%s adapter tags create success", dev->no);
        }
        else
        {
            proto_syslog(LOG_INFO, "%s adapter tags create fail", dev->no);
        }
    }
#ifdef EN_SQLITE
    sqlite3_close(db);
#endif
    if (chan_ptr->devs_size < CHANNEL_DEVS_MAX)
    {
        chan_ptr->devs[chan_ptr->devs_size] = dev;
        proto_syslog(LOG_NOTICE, "channel[%d] created device %s", chan_ptr->devs_size, dev->no);
        chan_ptr->devs_size++;
    }
    else
    {
        proto_syslog(LOG_ERR, "channel[%s] created device[%s] error, devs_size=%d", chan_ptr->channel, dev->no, chan_ptr->devs_size);
    }

    dev_add_other_info(chan_ptr, var, dev);
    return 0;
}

static channel_t *channel_create(proto_forward_t *var, cJSON *dev_info,char *dev_type)
{
    char channel[CHANNEL_NAME_SIZE];
    char * userdef_back = NULL;
    device_t *dev = NULL;
    int idx = 0;
    template_t *temp_ptr = NULL;
    if (var->channels_size >= CHANNEL_MAX)
    {
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_004, NULL, "[数采]创建设备失败，通道数量超过最大限制");
        proto_syslog(LOG_ERR, "created channel[%d-%s] error", idx, channel);
        goto ERR;
    }
    GET_JSON_VALUE_STRING(dev_info, "channel", channel);
    if (strlen(channel) <= 0)
    {
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_004, NULL, "[数采]创建设备失败，通道名称为空");
        proto_syslog(LOG_ERR, "invalid template!");
        goto ERR;
    }

    GET_JSON_VALUE_DY_STRING(dev_info, "user_def", userdef_back);
    dev = device_create(dev_info);
    if (!dev)
    {
        proto_syslog(LOG_WARNING, "failed to create dev");
        goto ERR;
    }
    dev->dev_type = strdup(dev_type);
    if (strcmp(dev->dev_type, "METER") == 0)
    {
        dev->is_meter = 1;
    }

    char temp_file[192] = {0};
    char file_name[128] = {0};
    GET_JSON_VALUE_STRING(dev_info, "template", file_name);
    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);
    }

    check_load_template_config(temp_file, sizeof(temp_file), userdef_back);
    int ret = parse_template_cfg(var, dev,dev->proto_type, temp_file, &temp_ptr); // 找出模板,检查模板
    if ((ret != 0) || (temp_ptr == NULL))
    {
        proto_syslog(LOG_ERR, "dev[%s] parse template error", dev->no);
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_006, dev->no, "[数采]设备[%s]模板获取失败",dev->no);
        goto ERR;
    }
    else
    {
        cJSON_AddItemToObject(dev_info,"vendor",cJSON_CreateString(temp_ptr->vendor));
        cJSON_AddItemToObject(dev_info,"model",cJSON_CreateString(temp_ptr->model));
        proto_syslog(LOG_NOTICE, "dev[%s] parse template success", dev->no);
        dev->use_template = temp_ptr;
        dev->adp = device_adapter_probe(temp_ptr->adapter);
        if (dev->adp != NULL)
        {
            proto_syslog(LOG_NOTICE, "%s probe adpter success", dev->no);
        }
        else
        {
            proto_syslog(LOG_NOTICE, "%s probe adpter fail", dev->no);
        }
    }

    channel_t *chan_ptr = NULL;
    proto_syslog(LOG_NOTICE, " var->channels_size:%d!", var->channels_size);
    for (int i = 0; i < var->channels_size; i++)
    {
        if (strcmp(channel, var->channels[i]->channel) == 0)
        {
            chan_ptr = var->channels[i];
            proto_syslog(LOG_INFO, "find name:%s as same as others", channel);
        }
    }
    idx = var->channels_size;
    if (!chan_ptr)
    {
        if (idx >= CHANNEL_MAX)
        {
            proto_syslog(LOG_ERR, "created channel[%d-%s] error", idx, channel);
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_007,  dev->no, "[数采]通道创建失败 超过最大数量[%d]",CHANNEL_MAX);

            goto ERR;
        }
        // 创建新的结构体
        chan_ptr = calloc(sizeof(channel_t), 1);
        memset(chan_ptr, 0, sizeof(channel_t));
        var->channels[idx] = chan_ptr;
        strcpy(var->channels[idx]->channel, channel);
        strcpy(var->channels[idx]->protocol, get_protocal_string(dev->proto_type));
        var->channels[idx]->proto_type = dev->proto_type;
        var->channels[idx]->var = var; // 传递全局结构
        INIT_LIST_HEAD(&var->channels[idx]->wirte_list);
        // 按照协议类型处理初始化结构体
        pthread_rwlock_init(&var->channels[idx]->list_rwlock, NULL);
        pthread_mutex_init(&var->channels[idx]->mutex, NULL);
        pthread_cond_init(&var->channels[idx]->cond, NULL);
        sem_init(&var->channels[idx]->write_sem,0,0);
        chan_ptr->proto_ptr = creat_channle_attr(chan_ptr);
        if (chan_ptr->proto_ptr == NULL)
        {
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_007,  dev->no, "[数采]通道[%s]创建失败",channel);
            proto_syslog(LOG_ERR, "created channel[%d-%s] error", idx, channel);
            free(chan_ptr);
            chan_ptr = NULL;
            goto ERR;
        }
        else
        {
            proto_syslog(LOG_NOTICE, "created channel[%d-%s] success", idx, channel);
            // 存储通道信息
            var->channels_size++;
        }
    }
    else
    {
        if (chan_ptr->proto_type != dev->proto_type)
        {
            chan_ptr = NULL;
        }
    }
#ifdef EN_OPCUA
    ua_server_add_dev_node((UA_SERVER_PARAM *)var->ua_server_param,chan_ptr, dev);
#endif

    dev_tags_creat_and_add_to_channle(dev, temp_ptr, chan_ptr);
    if (userdef_back != NULL)
        free(userdef_back);
    return chan_ptr;

ERR:
    if (userdef_back != NULL) {
        free(userdef_back);
        userdef_back = NULL;
    }
    if (temp_ptr)
    {
        free(temp_ptr);
        temp_ptr = NULL;
    }
    if (dev)
    {
        free_dev(dev);
        dev= NULL;
    }
    return NULL;
}

nodetrans* parse_nodetrans(cJSON *json_array, int *count) {
    *count = cJSON_GetArraySize(json_array);
    nodetrans *trans = (nodetrans *)malloc(*count * sizeof(nodetrans));

    for (int i = 0; i < *count; i++) {
        cJSON *item = cJSON_GetArrayItem(json_array, i);
        cJSON *src = cJSON_GetObjectItemCaseSensitive(item, "src");
        cJSON *dest = cJSON_GetObjectItemCaseSensitive(item, "dest");

        if (cJSON_IsNumber(src) && cJSON_IsNumber(dest)) {
            trans[i].source_value = src->valueint;
            trans[i].expect_value = dest->valueint;
        } else {
            ems_syslog(LOG_ERR, "Invalid nodetrans format");
            free(trans);
            return NULL;
        }
    }

    return trans;
}

// 辅助函数：解析 inside_node 数组
inside_node* parse_inside_node(cJSON *json_array, int *count) {
    *count = cJSON_GetArraySize(json_array);
    inside_node *nodes = (inside_node *)malloc(*count * sizeof(inside_node));

    for (int i = 0; i < *count; i++) {
        cJSON *item = cJSON_GetArrayItem(json_array, i);
        cJSON *devname = cJSON_GetObjectItemCaseSensitive(item, "devname");
        cJSON *tag = cJSON_GetObjectItemCaseSensitive(item, "tag");
        cJSON *valuelist = cJSON_GetObjectItemCaseSensitive(item, "valuelist");

        if (cJSON_IsString(devname) && cJSON_IsString(tag) && cJSON_IsArray(valuelist)) {
            strncpy(nodes[i].devname, devname->valuestring, DEV_NAME_LENGTH - 1);
            nodes[i].devname[DEV_NAME_LENGTH - 1] = '\0';
            strncpy(nodes[i].inside_tag, tag->valuestring, TAG_MAX_LEN - 1);
            nodes[i].inside_tag[TAG_MAX_LEN - 1] = '\0';

            nodes[i].trans = parse_nodetrans(valuelist, &nodes[i].trans_count);
        } else {
            ems_syslog(LOG_ERR, "Invalid inside_node format\n");
            free(nodes);
            return NULL;
        }
    }

    return nodes;
}
#ifdef EN_EMS
static tag_t *sys_special_tags_create(channel_t *chan_ptr, proto_forward_t *var, device_t *dev,special_point *special,cJSON *newtag,cJSON *name,cJSON *desc)
{
    sqlite3 *db = NULL;
    tag_t *tag = calloc(sizeof(tag_t), 1);
    if (tag == NULL) {
        proto_syslog(LOG_ERR, "calloc failed for tag");
        return NULL;
    }
    strcpy(tag->name, newtag->valuestring);
    strcpy(tag->disp_name, name->valuestring);
    tag->desc = strdup(desc->valuestring);
    tag->chan_ptr = chan_ptr;
    tag->dev_ptr = dev;
    tag->point_type = PROPERTY_ALARM;
    tag->property = PROPERTY_REALDATA;
    tag->data_type = TYPE_TAG_INT;
    tag->history_type = HISTORY_TYPE_BOTH;
    snprintf(tag->dev_tag_name, sizeof(tag->dev_tag_name), "%s.%s", dev->no, tag->name);
#ifdef EN_OPCUA    
    {
        UA_SERVER_PARAM *ua_ser_para = var->ua_server_param;
        UA_Server *server = ua_ser_para->ua_server;
        char enum_name[DEV_NO_LEN + TAG_NAME_LEN + 10] = {0};
        snprintf(enum_name, sizeof(enum_name), "%s.%s_enum", dev->no, tag->name);
        tag->ua_enum_type = ua_creat_custom_enum(server, enum_name,tag->desc);
        if (tag->ua_enum_type == NULL)
        {
            proto_syslog(LOG_ERR, "dev:%s tag:%s creat ua custom enum err!!!!!!!", dev->no, tag->name);
        }
        else
        {
            proto_syslog(LOG_NOTICE, "dev:%s tag:%s creat ua custom enum success", dev->no, tag->name);
        }
    }
#endif


    int ret = hash_intptr_addptr(&var->tag_map_hash, tag->dev_tag_name, strlen(tag->dev_tag_name), tag);
    if (ret < 0)
    {
        proto_syslog(LOG_ERR, "hash_intptr_addptr failed, key: %s", tag->dev_tag_name);
        goto ERR;
    }
#ifdef EN_OPCUA
    ua_server_add_data_item(var, dev, tag, PROPERTY_SYSTEM,READ_ONLY,db);
#endif

    proto_syslog(LOG_INFO, "Add success: %s", tag->dev_tag_name);
    return tag;

ERR:
    if (tag)
    {
        free(tag);
        tag = NULL;
    }
    return NULL;
}


int north_cloud_load_nodemap(char *cfg_file)
{
    proto_forward_t *var = get_proto_forward_var();
    ems_base_variant_t north_var = {.dev = NULL, .flag = O_RDONLY, .data_type = TYPE_TAG_INT, .point_type = PROPERTY_REALDATA, .history_type = "both", .fun_code = 0x04, .addr = 0x70,  .type = TYPE_INT, .order = ORDER_AB, .coefficient = 1};
    char *data = NULL;
    int ret = 0;
    if (var == NULL || cfg_file == NULL || var->northdevs == NULL)
    {
        ems_syslog(LOG_ERR, "p_nodes_cfg:%p cfg_file:%p", var, cfg_file);
        return -1;
    }
    var->northdevs->devnum = 0;

    data = read_file_data(cfg_file);
    if (data == NULL)
    {
        ems_syslog(LOG_ERR, "read cfg file %s error", cfg_file);
        ret = -1;
        goto out;
    }

    cJSON *root = cJSON_Parse(data);
    if (!root)
    {
        ems_syslog(LOG_ERR, "parse cfg file error");
        ret = -1;
        goto out;
    }

    cJSON *nodemap = cJSON_GetObjectItem(root, "nodemap");
    int node_cnt = cJSON_GetArraySize(nodemap);
    for (int i = 0; i < node_cnt; i++)
    {
        device_t *dev = NULL;
        channel_t *chan_ptr = NULL;
        var->northdevs->devnum++;
        cJSON *devnodemap = cJSON_GetArrayItem(nodemap, i);
        GET_JSON_VALUE_STRING(devnodemap, "dev", var->northdevs->devs[i].devname);
        GET_JSON_VALUE_STRING(devnodemap, "devtype", var->northdevs->devs[i].devtype);
        GET_JSON_VALUE_INT(devnodemap, "lcnum", var->northdevs->devs[i].lcnum);
        cJSON *alarmparam = cJSON_GetObjectItem(devnodemap, "alarmparam");
        if (alarmparam != NULL && cJSON_IsObject(alarmparam))
        {
            cJSON *node = NULL;
            cJSON_ArrayForEach(node, alarmparam)
            {
                nodemap_entry *nentry = NULL;
                char uatag[TAG_MAX_LEN] = {0};
                snprintf(uatag, TAG_MAX_LEN, "%s", node->string);
                cJSON *value_item = cJSON_GetObjectItem(alarmparam, uatag);
                if (value_item != NULL && cJSON_IsArray(value_item) && cJSON_GetArraySize(value_item) >= 2)
                {
                    cJSON *cloudtag_item = cJSON_GetArrayItem(value_item, 0);
                    cJSON *value = cJSON_GetArrayItem(value_item, 1);
                    if (cloudtag_item != NULL && cJSON_IsString(cloudtag_item))
                    {
                        HASH_FIND_STR(var->northdevs->devs[i].alarmentry, uatag, nentry);
                        if (NULL == nentry)
                        {
                            nentry = malloc(sizeof(nodemap_entry));
                            nentry->customtag = malloc(sizeof(north_customtag));
                            snprintf(nentry->uatag, TAG_MAX_LEN, "%s", uatag);
                            snprintf(nentry->customtag->cloudtag, TAG_MAX_LEN, "%s", cloudtag_item->valuestring);
                            nentry->customtag->scale = value->valueint;
                            HASH_ADD_STR(var->northdevs->devs[i].alarmentry, uatag, nentry);
                        }
                    }
                }
            }
        }
        cJSON *dataparam = cJSON_GetObjectItem(devnodemap, "dataparam");
        if (dataparam != NULL && cJSON_IsObject(dataparam))
        {
            cJSON *node = NULL;
            cJSON_ArrayForEach(node, dataparam)
            {
                nodemap_entry *nentry = NULL;
                char uatag[TAG_MAX_LEN] = {0};
                snprintf(uatag, TAG_MAX_LEN, "%s", node->string);
                cJSON *value_item = cJSON_GetObjectItem(dataparam, uatag);
                if (value_item != NULL && cJSON_IsArray(value_item) && cJSON_GetArraySize(value_item) >= 2)
                {
                    cJSON *cloudtag_item = cJSON_GetArrayItem(value_item, 0);
                    cJSON *value = cJSON_GetArrayItem(value_item, 1);
                    if (cloudtag_item != NULL && cJSON_IsString(cloudtag_item))
                    {
                        HASH_FIND_STR(var->northdevs->devs[i].dataentry, uatag, nentry);
                        if (NULL == nentry)
                        {
                            nentry = malloc(sizeof(nodemap_entry));
                            nentry->customtag = malloc(sizeof(north_customtag));
                            snprintf(nentry->uatag, TAG_MAX_LEN, "%s", uatag);
                            snprintf(nentry->customtag->cloudtag, TAG_MAX_LEN, "%s", cloudtag_item->valuestring);
                            nentry->customtag->scale = value->valueint;
                            HASH_ADD_STR(var->northdevs->devs[i].dataentry, uatag, nentry);
                        }
                    }
                }
            }
        }

        // 解析 specialparam
        cJSON *specialparam = cJSON_GetObjectItem(devnodemap, "specialparam");
        if (specialparam && cJSON_IsArray(specialparam)) {
            int special_count = cJSON_GetArraySize(specialparam);
            var->northdevs->devs[i].special_count = special_count;
            var->northdevs->devs[i].special = (special_point *)malloc(special_count * sizeof(special_point));
            for (int m = 0; m < var->channels_size; m++)
            {
                for (int n = 0; n < var->channels[m]->devs_size; n++)
                {
                    if (strcmp(var->channels[m]->devs[n]->no,var->northdevs->devs[i].devname) == 0)
                    {
                        dev = var->channels[m]->devs[n];
                        chan_ptr = var->channels[m];
                        break;
                    }
                }
            }
            if((dev == NULL) || (chan_ptr == NULL))
            {
                ems_syslog(LOG_ERR, "nodemap device[%d] is null", i);
                continue;
            }

            for (int k = 0; k < special_count; k++) {
                cJSON *special_item = cJSON_GetArrayItem(specialparam, k);
                cJSON *newtag = cJSON_GetObjectItemCaseSensitive(special_item, "newtag");
                cJSON *name = cJSON_GetObjectItemCaseSensitive(special_item, "name");
                cJSON *desc = cJSON_GetObjectItemCaseSensitive(special_item, "desc");
                cJSON *othervalue = cJSON_GetObjectItemCaseSensitive(special_item, "othervalue");
                cJSON *srctag = cJSON_GetObjectItemCaseSensitive(special_item, "srctag");

                if (cJSON_IsString(newtag) && cJSON_IsString(name) && cJSON_IsString(desc) && cJSON_IsNumber(othervalue) && cJSON_IsArray(srctag)) {
                    var->northdevs->devs[i].special[k].newtag = strdup(newtag->valuestring);
                    var->northdevs->devs[i].special[k].name = strdup(name->valuestring);
                    var->northdevs->devs[i].special[k].desc = strdup(desc->valuestring);
                    var->northdevs->devs[i].special[k].othervalue = othervalue->valueint;
                    if(strcmp(var->northdevs->devs[i].devname,DEV_NO_EMS) == 0)
                    {
                        north_var.dev = strdup(DEV_NO_EMS);
                        north_var.variant = strdup(newtag->valuestring);
                        north_var.name = strdup(name->valuestring);
                        north_var.desc = strdup(desc->valuestring);
                        ems_base_variant_sing_add(&north_var);
                        north_var.addr++;
                    }
                    else
                    {
                        tag_t *tag = sys_special_tags_create(chan_ptr,var,dev,&var->northdevs->devs[i].special[k],newtag,name,desc);
                        dev->tags.sys_info_tags[dev->tags.sys_info_tags_size] = tag;
                        dev->tags.sys_info_tags_size++;
                    }
                    var->northdevs->devs[i].special[k].node = parse_inside_node(srctag, &var->northdevs->devs[i].special[k].node_count);
                } else {
                    ems_syslog(LOG_ERR, "%s %d Invalid specialparam format", var->northdevs->devs[i].devname, k);
                    cJSON_Delete(nodemap);
                    ret = -1;
                    goto out;
                }
            }
        }
    }
    cJSON_Delete(nodemap);

out:
    return ret;
}
#endif



int load_device_cfg(proto_forward_t *var,char *device_config_pwd, int is_file)
{
    char *pdata = NULL;
    cJSON *root = NULL;
    // char type[32] = {'\0'};

    proto_syslog(LOG_INFO,"proto_forward current channels size%d \n",var->channels_size);
    if (device_config_pwd == NULL)
    {
        proto_syslog(LOG_ERR, "device_config_pwd is NULL");
        goto out;
    }
    
    pdata = (is_file != 0) ? read_file_data(device_config_pwd) : device_config_pwd;
    if (pdata == NULL)
    {
        if (is_file)
        {
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_001, NULL, "[数采]读取设备配置文件[%s]失败",device_config_pwd);
        }
        proto_syslog(LOG_ERR, "read cfg file %s error", device_config_pwd);
        goto out;
    }
    root = json_parse_string_with_comments(pdata);
    if (!root)
    {
        proto_syslog(LOG_ERR, "parse pdata %s error", pdata);
        if (is_file)
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_002, NULL, "[数采]配置文件[%s]内容不是json",device_config_pwd);
        goto out;
    }
    cJSON *dev_class = cJSON_GetObjectItemCaseSensitive(root, "dev_class");
    if (!dev_class)
    {
        BUSINESS_LOG(BLOG_ERR, DC_START_ID_003, NULL, "[数采]无法获取到dev_class对象");
        proto_syslog(LOG_ERR, "get object dev_class error");
        goto out;
    }
    int dev_class_cnt = cJSON_GetArraySize(dev_class);
    for (int i = 0; i < dev_class_cnt; i++)
    {
        char dev_type[64] = {0};
        cJSON *dev_list_item = cJSON_GetArrayItem(dev_class, i);
        GET_JSON_VALUE_STRING(dev_list_item, "type", dev_type);
        if (strlen(dev_type) < 1)
        {
            proto_syslog(LOG_ERR, "type is NULL");
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_003, NULL, "[数采]无法获取到dev_class的type对象");
            continue;
        }
        cJSON *devs_info = cJSON_GetObjectItemCaseSensitive(dev_list_item, "devs");
        if (devs_info == NULL)
        {
            BUSINESS_LOG(BLOG_ERR, DC_START_ID_003, NULL, "[数采]无法获取到dev_class的devs对象");
            continue;
        }

        int devs_info_cnt = cJSON_GetArraySize(devs_info);
        for (int i = 0; i < devs_info_cnt; i++)
        {
            cJSON *dev_info = cJSON_GetArrayItem(devs_info, i);
            // 根据配置自动创建通道,如果重复不创建
            channel_t *channel = channel_create(var, dev_info,dev_type);
            if (!channel)
            {
                proto_syslog(LOG_ERR, "failed to create channel");
            }
        }
        
    }
    for (int i = 0; i < dev_class_cnt; i++) //删除字符串中的CELL
    {
        cJSON *dev_list_item = cJSON_GetArrayItem(dev_class, i);
        cJSON *devs_info = cJSON_GetObjectItemCaseSensitive(dev_list_item, "devs");
        if (devs_info == NULL)
        {
            continue;
        }
        int devs_info_cnt = 0;
RETRUY:
        devs_info_cnt = cJSON_GetArraySize(devs_info);
        for (int i = 0; i < devs_info_cnt; i++)
        {
            cJSON *dev_info = cJSON_GetArrayItem(devs_info, i);
        
            cJSON *no = cJSON_GetObjectItemCaseSensitive(dev_info, "no");
            if (no == NULL)
            {
                continue;
            }
            if (strstr(no->valuestring,"CELLS"))
            {
                proto_syslog(LOG_INFO,"delete:%s",no->valuestring);
                cJSON_DeleteItemFromArray(devs_info,i);
                goto RETRUY;
            }
        }
    }
    if (strstr(device_config_pwd, "/device_config.json"))
    {
        var->dev_info_string = cJSON_Print(root);
        proto_syslog(LOG_DEBUG,"device_config.json context len:%lu", (long unsigned int)strlen(var->dev_info_string));
    }
    proto_syslog(LOG_DEBUG, "channel size:%d", var->channels_size);
    for (int i = 0; i < var->channels_size; i++)
    {
        proto_syslog(LOG_NOTICE, "channels[%d]:%s type:%d", i, var->channels[i]->channel, var->channels[i]->proto_type);
        for (int k = 0; k < var->channels[i]->devs_size; k++)
        {
            proto_syslog(LOG_DEBUG, "  dev no:%s type:%d", var->channels[i]->devs[k]->no, var->channels[i]->devs[k]->proto_type);

            proto_syslog(LOG_NOTICE, "      ro tags size:%d", var->channels[i]->devs[k]->tags.ro_tags_size);
            for (int m = 0; m < var->channels[i]->devs[k]->tags.ro_tags_size; m++)
            {
                tag_t *tag = var->channels[i]->devs[k]->tags.ro_tags[m];
                proto_syslog(LOG_DEBUG, "          tag name:%s type:%d", tag->name, tag->data_type);
            }
            proto_syslog(LOG_NOTICE, "      rw tags size:%d", var->channels[i]->devs[k]->tags.rw_tags_size);
            for (int m = 0; m < var->channels[i]->devs[k]->tags.rw_tags_size; m++)
            {
                tag_t *tag = var->channels[i]->devs[k]->tags.rw_tags[m];
                proto_syslog(LOG_DEBUG, "          tag name:%s type:%d", tag->name, tag->data_type);
            }
        }

        switch (var->channels[i]->proto_type)
        {
        case PROTOCOL_TYPE_MODBUS_RTU:
        case PROTOCOL_TYPE_MODBUS_RTU_RCRC:
            for (int k = 0; k < var->channels[i]->devs_size; k++)
            {
                uint32_t slave = 0;
                memcpy(&slave, var->channels[i]->devs[k]->proto_ptr, sizeof(slave));
                proto_syslog(LOG_DEBUG, "  dev no:%s addr:%d type:%d", var->channels[i]->devs[k]->no, slave, var->channels[i]->devs[k]->proto_type);
            } /* fall through */
        case PROTOCOL_TYPE_MODBUS_TCP:
            for (int k = 0; k < var->channels[i]->devs_size; k++)
            {
                proto_syslog(LOG_DEBUG, "  dev no:%s type:%d", var->channels[i]->devs[k]->no, var->channels[i]->devs[k]->proto_type);
            }
            proto_syslog(LOG_NOTICE, "  chunks_interv_size:%d", var->channels[i]->chunks_interv_size);
            for (int k = 0; k < var->channels[i]->chunks_interv_size; k++)
            {
                proto_syslog(LOG_DEBUG, "      index:%d", k);
                chunk_interv_t *info = var->channels[i]->chunks_interv[k];
                modbus_poll_info_t *poll_info = info->chunk_proto_ptr;
                proto_syslog(LOG_DEBUG, "      interval:%d modbus[%d %x %d]", info->poll_interv, poll_info->func_code, poll_info->reg_addr, poll_info->size);
            }
            proto_syslog(LOG_NOTICE, "  chunks_nonstop_size:%d", var->channels[i]->chunks_nonstop_size);
            for (int k = 0; k < var->channels[i]->chunks_nonstop_size; k++)
            {
                proto_syslog(LOG_DEBUG, "      index:%d", k);
                chunk_nonstop_t *info = var->channels[i]->chunks_nonstop[k];
                modbus_poll_info_t *poll_info = info->chunk_proto_ptr;
                proto_syslog(LOG_DEBUG, "      current %d modbus[%d %x %d]", var->channels[i]->chunks_nonstop_index, poll_info->func_code, poll_info->reg_addr, poll_info->size);
            }
            break;
        case PROTOCOL_TYPE_DLT645_2007:
        case PROTOCOL_TYPE_DLT645_1997:
            for (int k = 0; k < var->channels[i]->devs_size; k++)
            {
                proto_syslog(LOG_DEBUG, "  dev no:%s type:%d", var->channels[i]->devs[k]->no, var->channels[i]->devs[k]->proto_type);
            }
            proto_syslog(LOG_NOTICE, "  chunks_interv_size:%d", var->channels[i]->chunks_interv_size);
            for (int k = 0; k < var->channels[i]->chunks_interv_size; k++)
            {
                proto_syslog(LOG_DEBUG, "      index:%d", k);
                chunk_interv_t *info = var->channels[i]->chunks_interv[k];
                dlt645_poll_info_t *poll_info = info->chunk_proto_ptr;
                proto_syslog(LOG_DEBUG, "      interval:%d dlt645[%08x %d]", info->poll_interv, poll_info->data_id, poll_info->pos);
            }
            proto_syslog(LOG_NOTICE, "  chunks_nonstop_size:%d", var->channels[i]->chunks_nonstop_size);
            for (int k = 0; k < var->channels[i]->chunks_nonstop_size; k++)
            {
                proto_syslog(LOG_DEBUG, "      index:%d", k);
                chunk_nonstop_t *info = var->channels[i]->chunks_nonstop[k];
                dlt645_poll_info_t *poll_info = info->chunk_proto_ptr;
                proto_syslog(LOG_DEBUG, "      current %d modbus[%08x %d]", var->channels[i]->chunks_nonstop_index, poll_info->data_id, poll_info->pos);
            }
            break;
        case PROTOCOL_TYPE_UPS:
        break;

        case PROTOCOL_TYPE_BMSER_BIN:
        break;

        case PROTOCOL_TYPE_CAN:
        break;

        case PROTOCOL_TYPE_IEC104_CLIENT:
            // TODO
            break;

        case PROTOCOL_TYPE_CUSTOM:
        break;

        default:
            proto_syslog(LOG_WARNING, "unspported type of protocol");
            break;
        }
    }

out:
    if (root != NULL)
    {
        cJSON_Delete(root);
    }
    if ((pdata != NULL) && (is_file != 0))
    {
        free(pdata);
        pdata= NULL;
    }
    return 0;
}


static const char *nondisplay_tag_json =
    "["
    "{\"type\":\"EMS\",\"nondisplay_tag\":[\"PowerLimit\"]},"
    "{\"type\":\"LC\",\"child_type\":["
        "{\"type\":\"EMS\",\"nondisplay_tag\":[\"SingleVoltageTime\",\"PowerLimit\"]},"
        "{\"type\":\"VLC\",\"nondisplay_tag\":[\"SingleVoltageTime\",\"AlarmCnt\",\"AlarmHisCnt\"]},"
        "{\"type\":\"LC\",\"nondisplay_tag\":[\"SingleVoltageTime\"]}"
    "]},"
    "{\"type\":\"METER\",\"nondisplay_tag\":[]},"
    "{\"type\":\"PCS\",\"nondisplay_tag\":[]},"
    "{\"type\":\"CELL\",\"nondisplay_tag\":[]},"
    "{\"type\":\"BMS\",\"nondisplay_tag\":[]},"
    "{\"type\":\"AC\",\"nondisplay_tag\":[]},"
    "{\"type\":\"PV\",\"nondisplay_tag\":[]},"
    "{\"type\":\"TMS\",\"nondisplay_tag\":[]},"
    "{\"type\":\"DG\",\"nondisplay_tag\":[]},"
    "{\"type\":\"FIRE\",\"nondisplay_tag\":[]},"
    "{\"type\":\"OTHERS\",\"nondisplay_tag\":[]}"
    "]";


typedef struct {
    char key[128];
    UT_hash_handle hh;
} nondisplay_tag_t;

static nondisplay_tag_t *nondisplay_tags = NULL;

void init_nondisplay_tags() {
    cJSON *root = cJSON_Parse(nondisplay_tag_json);
    if (!root) {
        proto_syslog(LOG_ERR, "Failed to parse nondisplay_tag_json");
        return;
    }

    int array_size = cJSON_GetArraySize(root);
    for (int i = 0; i < array_size; i++) {
        cJSON *item = cJSON_GetArrayItem(root, i);
        cJSON *type = cJSON_GetObjectItem(item, "type");
        cJSON *tags = cJSON_GetObjectItem(item, "nondisplay_tag");
        cJSON *child_types = cJSON_GetObjectItem(item, "child_type");

        if (type && tags && cJSON_IsArray(tags)) {
            int tag_count = cJSON_GetArraySize(tags);
            for (int j = 0; j < tag_count; j++) {
                cJSON *tag = cJSON_GetArrayItem(tags, j);
                if (tag && cJSON_IsString(tag)) {
                    char key[128];
                    snprintf(key, sizeof(key), "%s:%s", type->valuestring, tag->valuestring);

                    nondisplay_tag_t *entry = malloc(sizeof(nondisplay_tag_t));
                    snprintf(entry->key, sizeof(entry->key), "%s", key);
                    HASH_ADD_STR(nondisplay_tags, key, entry);
                }
            }
        }

        if (child_types && cJSON_IsArray(child_types)) {
            int child_count = cJSON_GetArraySize(child_types);
            for (int k = 0; k < child_count; k++) {
                cJSON *child = cJSON_GetArrayItem(child_types, k);
                cJSON *child_type = cJSON_GetObjectItem(child, "type");
                cJSON *child_tags = cJSON_GetObjectItem(child, "nondisplay_tag");

                if (child_type && child_tags && cJSON_IsArray(child_tags)) {
                    int child_tag_count = cJSON_GetArraySize(child_tags);
                    for (int l = 0; l < child_tag_count; l++) {
                        cJSON *child_tag = cJSON_GetArrayItem(child_tags, l);
                        if (child_tag && cJSON_IsString(child_tag)) {
                            char key[128];
                            snprintf(key, sizeof(key), "%s:%s", child_type->valuestring, child_tag->valuestring);

                            nondisplay_tag_t *entry = malloc(sizeof(nondisplay_tag_t));
                            snprintf(entry->key, sizeof(entry->key), "%s", key);
                            HASH_ADD_STR(nondisplay_tags, key, entry);
                        }
                    }
                }
            }
        }
    }

    cJSON_Delete(root);
}


int is_nondisplay_tag(char *dev_type, const char *tag_name, const char *dev_child_type) {
    char key[128];
    nondisplay_tag_t *entry = NULL;
    //有子类型的设备
    if (dev_child_type && strlen(dev_child_type) > 0) {
        snprintf(key, sizeof(key), "%s:%s", dev_child_type, tag_name);
        HASH_FIND_STR(nondisplay_tags, key, entry);
        if (entry != NULL) {
            return 1;
        }
    }
    //无子类型的设备
    snprintf(key, sizeof(key), "%s:%s", dev_type, tag_name);
    HASH_FIND_STR(nondisplay_tags, key, entry);
    return entry != NULL;
}

void free_nondisplay_tags() {
    nondisplay_tag_t *entry, *tmp;
    HASH_ITER(hh, nondisplay_tags, entry, tmp) {
        HASH_DEL(nondisplay_tags, entry);
        free(entry);
    }
}

static cJSON *tag_info_to_json(const reg_info_t *info ,const device_t *dev)
{
    if(is_nondisplay_tag(dev->dev_type, info->name, dev->dev_child_type))
    {
        return NULL;
    }

    cJSON *json_obj = cJSON_CreateObject();
    if (json_obj != NULL)
    {
        cJSON_AddItemToObject(json_obj, "NAME", cJSON_CreateString(info->name));
        cJSON_AddItemToObject(json_obj, "DISP", cJSON_CreateString(info->disp_name));
        cJSON_AddNumberToObject(json_obj, "TYPE", info->data_type);
        cJSON_AddNumberToObject(json_obj, "SCALE", info->scale);
        cJSON_AddNumberToObject(json_obj, "PROPERTY", info->property);
        int has_history = (info->history_type == HISTORY_TYPE_OFF) ? 0 : 1;
        cJSON_AddNumberToObject(json_obj, "HAS_HISTORY", has_history);
        if (info->desc)
        {
            cJSON_AddItemToObject(json_obj, "DESC", cJSON_CreateString(info->desc));
        }
        else
        {
            cJSON_AddItemToObject(json_obj, "DESC", cJSON_CreateString(""));
        }
    }
    return json_obj;
}

static cJSON *dev_node_sort(const device_t *dev)
{
    template_t *p_temp = dev->use_template;
    if (p_temp == NULL)
    {
        proto_syslog(LOG_ERR, "device[%s] template is null", dev->no);
        return NULL;
    }
    cJSON *json_obj = cJSON_CreateObject();
    cJSON_AddItemToObject(json_obj, "TYPE", cJSON_CreateString(dev->dev_type));
    cJSON_AddItemToObject(json_obj, "DEV_NO", cJSON_CreateString(dev->no));
    cJSON_AddItemToObject(json_obj, "DISP", cJSON_CreateString(dev->disp_name));
    cJSON_AddItemToObject(json_obj, "CHILD_TYPE", cJSON_CreateString(dev->dev_child_type));

    cJSON *alarm_arr = cJSON_CreateArray();
    cJSON *status_arr = cJSON_CreateArray();
    cJSON *setting_arr = cJSON_CreateArray();
    cJSON *data_arr = cJSON_CreateArray();

    cJSON *p_arr = NULL;
    cJSON *tmp_obj = NULL;
    reg_info_t *reg_info = NULL;
    // 读
    for (int i = 0; i < p_temp->read_tags_size; i++)
    {
        reg_info = &p_temp->read_tags[i]->read_reg;
        switch (reg_info->property)
        {
        case PROPERTY_REALDATA:
            p_arr = data_arr;
            break;

        case PROPERTY_SYSTEM:
            p_arr = status_arr;
            break;

        case PROPERTY_ALARM:
            p_arr = alarm_arr;
            break;

        default:
            proto_syslog(LOG_ERR, "node[%s] property error, property:%d", reg_info->name, reg_info->property);
            continue;
        }
        tmp_obj = tag_info_to_json(reg_info, dev);
        if (tmp_obj != NULL)
        {
            cJSON_AddItemToArray(p_arr, tmp_obj);
        }
    }
    // 写
    for (int i = 0; i < p_temp->write_tags_size; i++)
    {
        reg_info = &p_temp->write_tags[i]->write_reg;
        switch (reg_info->property)
        {
        case PROPERTY_REALDATA:
            p_arr = setting_arr;
            break;

        case PROPERTY_SYSTEM:
            p_arr = status_arr;
            break;

        case PROPERTY_ALARM:
            p_arr = alarm_arr;
            break;

        default:
            proto_syslog(LOG_ERR, "node[%s] property error, property:%d", reg_info->name, reg_info->property);
            continue;
        }
        tmp_obj = tag_info_to_json(reg_info, dev);
        if (tmp_obj != NULL)
        {
            cJSON_AddItemToArray(p_arr, tmp_obj);
        }
    }
    // 不再模板中的node_id，现在主要是状态的，现在直接写死的，修改的时候注意需要和状态添加点位的时候同步
    reg_info_t status_info[] = {
        {.name = "Online", .disp_name = "Status", .data_type = 0, .desc = _("0:Offline;1:Online;"), .history_type = HISTORY_TYPE_CHANGE},
        {.name = "vendor", .disp_name = "Vendor", .data_type = 3},
        {.name = "model", .disp_name = "Model", .data_type = 3},
        {.name = FAULT_WORDS, .disp_name = "Number of fault words", .data_type = 0},
    };
    sprintf(status_info[0].disp_name, "%s", _("Status"));
    sprintf(status_info[1].disp_name, "%s", _("Vendor"));
    sprintf(status_info[2].disp_name, "%s", _("Model"));
    sprintf(status_info[3].disp_name, "%s", _("Number of fault words"));

    for (int i = 0; i < sizeof(status_info) / sizeof(status_info[0]); i++)
    {
        tmp_obj = tag_info_to_json(&status_info[i], dev);
        if (tmp_obj != NULL)
        {
            cJSON_AddItemToArray(status_arr, tmp_obj);
        }
    }

    cJSON_AddItemToObject(json_obj, "ALARM", alarm_arr);
    cJSON_AddItemToObject(json_obj, "STATUS", status_arr);
    cJSON_AddItemToObject(json_obj, "SETTING", setting_arr);
    cJSON_AddItemToObject(json_obj, "DATA", data_arr);

    return json_obj;
}

cJSON *get_dev_tree_from_file(char *file_path)
{
    char *data = read_file_data(file_path);
    if (!data)
    {
        proto_syslog(LOG_ERR, "read file %s error", file_path);
        return NULL;
    }

    cJSON *root = cJSON_Parse(data);
    if (!root)
    {
        proto_syslog(LOG_ERR, "parse file %s error", file_path);
        free(data);
        return NULL;
    }

    cJSON *dev_class = cJSON_GetObjectItem(root, "dev_class");
    if (!dev_class)
    {
        proto_syslog(LOG_ERR, "no dev_class in file %s", file_path);
        cJSON_Delete(root);
        free(data);
        return NULL;
    }

    cJSON *dev_tree = cJSON_CreateArray();
    int dev_class_cnt = cJSON_GetArraySize(dev_class);
    for (int i = 0; i < dev_class_cnt; i++)
    {
        cJSON *dev_list_item = cJSON_GetArrayItem(dev_class, i);
        cJSON *type = cJSON_GetObjectItem(dev_list_item, "type");
        cJSON *devs = cJSON_GetObjectItem(dev_list_item, "devs");
        if (!devs || cJSON_GetArraySize(devs) == 0)
            continue;

        int devs_cnt = cJSON_GetArraySize(devs);
        for (int j = 0; j < devs_cnt; j++)
        {
            cJSON *dev = cJSON_GetArrayItem(devs, j);
            cJSON *no = cJSON_GetObjectItem(dev, "no");
            cJSON *name = cJSON_GetObjectItem(dev, "name");
             cJSON *sub_type = cJSON_GetObjectItem(dev, "type");
            if (!no || !name)
                continue;

            cJSON *item = cJSON_CreateObject();
            cJSON_AddStringToObject(item, "type", type->valuestring);
            if (sub_type != NULL)
            {
                cJSON_AddStringToObject(item, "subtype", sub_type->valuestring);
            }
            char no_buf[64] = {0};
            snprintf(no_buf, sizeof(no_buf), "%s", no->valuestring);
            cJSON_AddStringToObject(item, "no", no_buf);

            char name_buf[128] = {0};
            snprintf(name_buf, sizeof(name_buf), "%s", name->valuestring);
            cJSON_AddStringToObject(item, "name", name_buf);

            cJSON_AddItemToArray(dev_tree, item);
        }
    }

    cJSON *result = cJSON_CreateObject();
    cJSON_AddItemToObject(result, "dev_tree", dev_tree);

    cJSON_Delete(root);
    free(data);
    return result;
}

char *get_node_sort(int *out_len)
{
    proto_forward_t *dev_var = get_proto_forward_var();
    cJSON *main_arr = cJSON_CreateArray();
    for (int i = 0; i < dev_var->channels_size; i++)
    {
        proto_syslog(LOG_DEBUG, "channel:%s", dev_var->channels[i]->channel);
        for (int j = 0; j < dev_var->channels[i]->devs_size; j++)
        {
            device_t *dev = dev_var->channels[i]->devs[j];
            if (dev == NULL)
            {
                proto_syslog(LOG_ERR, "device[%d] is null", i);
                continue;
            }

            cJSON *json_obj = dev_node_sort(dev);
            if (json_obj == NULL)
            {
                proto_syslog(LOG_ERR, "device[%s] template_node_sort return null", dev->no);
            }
            else
            {
                cJSON_AddItemToArray(main_arr, json_obj);
            }
        }
    }

    char *str = cJSON_PrintUnformatted(main_arr);
    cJSON_Delete(main_arr);
    if (str != NULL)
    {
        *out_len = strlen(str);
    }
    return str;
}

int get_same_type_devs_cnt(char *type)
{
    int cnt = 0;
    if(!type)
    {
        proto_syslog(LOG_ERR, "Invalid param ,need a specific type");
        return -1;
    }
    proto_forward_t *dev_var = get_proto_forward_var();
    for (int i = 0; i < dev_var->channels_size; i++)
    {
        for (int j = 0; j < dev_var->channels[i]->devs_size; j++)
        {
            device_t *dev = dev_var->channels[i]->devs[j];
            if (dev == NULL)
            {
                proto_syslog(LOG_ERR, "device[%d] is null", i);
                continue;
            }
            if (strcmp(dev->dev_type,type) == 0)
            {
                cnt++;
            }
        }
    }
    return cnt;
}

void *get_tag_value_point_by_name(const char *name, int type)
{
    tag_t *tag_finded = NULL;
    if (this_p == NULL)
    {
        return NULL;
    }
    if (hash_intptr_findptr(this_p->tag_map_hash, name, strlen(name), (void **)&tag_finded) < 0)
    {
        return NULL;
    }

    if (tag_finded->data_type != type)
    {
        return NULL;
    }

    if (type == 0)
    {
        return &tag_finded->read_cache.to_int;
    }
    else
    {
        return &tag_finded->read_cache.to_float;
    }
}

int get_same_child_type_devs_cnt(char *type_main, char *child_type)
{
    int cnt = 0;
    if(!type_main || !child_type)
    {
        proto_syslog(LOG_ERR, "Invalid param ,need a specific type");
        return -1;
    }
    proto_forward_t *dev_var = get_proto_forward_var();
    for (int i = 0; i < dev_var->channels_size; i++)
    {
        for (int j = 0; j < dev_var->channels[i]->devs_size; j++)
        {
            device_t *dev = dev_var->channels[i]->devs[j];
            if (dev == NULL)
            {
                proto_syslog(LOG_ERR, "device[%d] is null", i);
                continue;
            }
            if (strcmp(dev->dev_type,type_main) == 0 && strcmp(dev->dev_child_type, child_type) == 0)
            {
                cnt++;
            }
        }
    }
    return cnt;
}


#if 1 //ems心跳相关的函数
static UA_SERVER_PARAM *bms_connect_ua_par = NULL;
int read_ems_connect_cb(char *name, void *value_v)
{
    *((int *)value_v) = bms_connect_ua_par->ems_online;
    return 0;
}

void check_ems_heartbeat(UA_SERVER_PARAM *ua_param, int call_cycle)
{
    static int ems_heartbeat = 0;
    static int heartbeat_stop = 0;
    if (ems_heartbeat != ua_param->ems_heartbeat)
    {
        heartbeat_stop = 0;
        ems_heartbeat = ua_param->ems_heartbeat;
    }
    else
    {
        heartbeat_stop++;
        if (heartbeat_stop >= (5 / call_cycle))
        {
            if (ua_param->ems_online > 0)
            {
                ua_param->ems_online = 0;
            }
        }
    }
}

int get_ems_online(void)
{
    return bms_connect_ua_par->ems_online;
}

int get_ems_disable(void)
{
    return bms_connect_ua_par->disable;
}

int read_ems_heartbeat_cb(char *name, void *value_v)
{
    *((int *)value_v) = bms_connect_ua_par->ems_heartbeat;
    return 0;
}

int write_ems_heartbeat_cb(char *name, void *value_v)
{
    int *value = value_v;
    if (*value == 255)
    {
        bms_connect_ua_par->disable = 1;
    }
    else 
    {
        bms_connect_ua_par->disable = 0;
    }
    //
    if (bms_connect_ua_par->ems_heartbeat < 255)
    {
        bms_connect_ua_par->ems_heartbeat++;
    }
    else
    {
        bms_connect_ua_par->ems_heartbeat = 0;
    }
    bms_connect_ua_par->ems_online = 1;
    return 0;
}


 static cJSON* find_dev_class_by_type(cJSON* dev_class_arr, const char* type) {
    if (!dev_class_arr || !cJSON_IsArray(dev_class_arr) || !type) return NULL;
    int n = cJSON_GetArraySize(dev_class_arr);
    for (int i = 0; i < n; ++i) {
        cJSON* item = cJSON_GetArrayItem(dev_class_arr, i);
        cJSON* jtype = cJSON_GetObjectItemCaseSensitive(item, "type");
        if (item && jtype && cJSON_IsString(jtype) && strcmp(jtype->valuestring, type) == 0) {
            return item;
        }
    }
    return NULL;
}

static void append_devs_array(cJSON* dst_dev_class, cJSON* src_dev_class) {
    if (!dst_dev_class || !src_dev_class) return;
    cJSON* dst_devs = cJSON_GetObjectItemCaseSensitive(dst_dev_class, "devs");
    cJSON* src_devs = cJSON_GetObjectItemCaseSensitive(src_dev_class, "devs");
    if (!src_devs || !cJSON_IsArray(src_devs)) return;
    if (!dst_devs || !cJSON_IsArray(dst_devs)) {
        dst_devs = cJSON_CreateArray();
        cJSON_AddItemToObject(dst_dev_class, "devs", dst_devs);
    }

    int m = cJSON_GetArraySize(src_devs);
    for (int i = 0; i < m; ++i) {
        cJSON* dev = cJSON_GetArrayItem(src_devs, i);
        cJSON* dev_copy = cJSON_Duplicate(dev, 1);
        cJSON_AddItemToArray(dst_devs, dev_copy);
    }
}

int merge_device_cfg_files(const char* base_path, const char* add_path) {
    if (!base_path || !add_path) {
        proto_syslog(LOG_ERR, "merge_device_cfg_files: invalid params");
        return -1;
    }

    char* base_txt = read_file_data(base_path);
    char* add_txt  = read_file_data(add_path);
    if (!base_txt || !add_txt) {
        proto_syslog(LOG_ERR, "merge_device_cfg_files: read file failed");
        if (base_txt) free(base_txt);
        if (add_txt)  free(add_txt);
        return -2;
    }

    cJSON* base_root = json_parse_string_with_comments(base_txt);
    if (!base_root) base_root = cJSON_Parse(base_txt);
    cJSON* add_root  = json_parse_string_with_comments(add_txt);
    if (!add_root) add_root = cJSON_Parse(add_txt);

    free(base_txt);
    free(add_txt);

    if (!base_root || !add_root) {
        if (base_root) cJSON_Delete(base_root);
        if (add_root)  cJSON_Delete(add_root);
        proto_syslog(LOG_ERR, "merge_device_cfg_files: parse json failed");
        return -3;
    }

    cJSON* base_dev_class = cJSON_GetObjectItemCaseSensitive(base_root, "dev_class");
    cJSON* add_dev_class  = cJSON_GetObjectItemCaseSensitive(add_root,  "dev_class");
    if (!base_dev_class || !cJSON_IsArray(base_dev_class) || !add_dev_class || !cJSON_IsArray(add_dev_class)) {
        cJSON_Delete(base_root);
        cJSON_Delete(add_root);
        proto_syslog(LOG_ERR, "merge_device_cfg_files: dev_class missing or invalid");
        return -4;
    }

    int add_cnt = cJSON_GetArraySize(add_dev_class);
    for (int i = 0; i < add_cnt; ++i) {
        cJSON* add_item = cJSON_GetArrayItem(add_dev_class, i);
        cJSON* add_type = cJSON_GetObjectItemCaseSensitive(add_item, "type");
        if (!add_item || !add_type || !cJSON_IsString(add_type)) {
            proto_syslog(LOG_WARNING, "merge_device_cfg_files: skip dev_class without type");
            continue;
        }

        cJSON* base_item = find_dev_class_by_type(base_dev_class, add_type->valuestring);
        if (base_item) {
            append_devs_array(base_item, add_item);
        } else {
            cJSON* add_copy = cJSON_Duplicate(add_item, 1);
            cJSON_AddItemToArray(base_dev_class, add_copy);
        }
    }

    char* merged_txt = cJSON_Print(base_root);
    if (!merged_txt) {
        cJSON_Delete(base_root);
        cJSON_Delete(add_root);
        proto_syslog(LOG_ERR, "merge_device_cfg_files: serialize json failed");
        return -5;
    }

    int rc = write_file_data(base_path, merged_txt, strlen(merged_txt));
    free(merged_txt);
    cJSON_Delete(base_root);
    cJSON_Delete(add_root);

    if (rc < 0) {
        proto_syslog(LOG_ERR, "merge_device_cfg_files: write_file_data failed");
        return -6;
    }

    proto_syslog(LOG_NOTICE, "merge_device_cfg_files: merged '%s' into '%s' successfully", add_path, base_path);
    return 0;
}


int check_device_cfg_update()
{
    if(cabinet_info.ctrl_mode == EMS_MODE_ZC && cabinet_info.en_lc == 0)
    {
        char *zc_device_config_path = "/app/config/lc/device_config.json";
        char *device_config_path = "/app/config/device_config.json";
        //将主从模式时，LC配置的设备添合并到/app/config/device_cfg中
        merge_device_cfg_files(device_config_path, zc_device_config_path);
        //清空app/config/lc/device_cfg的设备配置文件
        write_file_data(zc_device_config_path, "{\"dev_class\":[]}", strlen("{\"dev_class\":[]}"));
    }

    return 0;
}

#endif
