#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include "xieneng_car_common.h"

gw_port_e ports[] = {CANBUS_0, CANBUS_1};

char slave_ver[128] = {0};
char balance_ver[128] = {0};

struct detail_info detail[MAX_LECU_CNT] = {{0}};
node_cfg_t *nodes[MAX_CAN_PORT][MAX_LECU_CNT] = {{0}};
time_t last_time[MAX_CAN_PORT][MAX_LECU_CNT] = {{0}};
time_t data_time[MAX_CAN_PORT][MAX_LECU_CNT] = {{0}};
int current_cached = 0;
unsigned long long ChargeSum = 0;    //充电电流和
unsigned long long DischargeSum = 0; //放电电流和
unsigned int ChargeCnt = 0;          //充电次数
unsigned int DischargeCnt = 0;       //放电次数
unsigned int EmptyCnt = 0;           //无充放电次数
unsigned int current_watchdog = 0x7FFFFFFF;

unsigned char raw_data[MAX_CAN_PORT][MAX_LECU_CNT][FUNC_MAX][MAX_FRAME_NUMBER][CAN_DATA_LEN] = {{{{{0}}}}};

static int update_cell_vol(xieneng_car_var_t *var)
{
    tag_table_t ptag = {0};
    int i, j, k;
    node_cfg_t *node = NULL;
    time_t now = time(NULL);
    unsigned char function_code = FUNC_DETAIL_INFO;
    cJSON *vol_json = NULL;
    ptag.data_type = DATA_TYPE_SERVICE;
    strcpy(ptag.identifier, "CellVolUpdate");
    ptag.report_period = 0;
    ptag.time = now;

    for (i = 0; i < MAX_CAN_PORT; i++)
    {
        for (j = 0; j < MAX_LECU_CNT; j++)
        {
            node = nodes[i][j];
            if (node != NULL)
            {
                if (now - data_time[i][j] < 30)
                {
                    unsigned short vol;
                    unsigned char b1 = 0;
                    unsigned char b2 = 0;
                    unsigned char b3 = 0;
                    unsigned char b4 = 0;
                    unsigned char b5 = 0;
                    unsigned char b6 = 0;
                    unsigned char b7 = 0;
                    unsigned char b8 = 0;
                    char key_str[8] = {0};

                    for (k = 1; k <= 100; k++)
                    {
                        b1 = raw_data[i][j][function_code][k][0];
                        if (b1 == k)
                        {
                            if (vol_json == NULL)
                            {
                                vol_json = cJSON_CreateObject();
                            }
                            b2 = raw_data[i][j][function_code][k][1];
                            b3 = raw_data[i][j][function_code][k][2];
                            b4 = raw_data[i][j][function_code][k][3];
                            b5 = raw_data[i][j][function_code][k][4];
                            b6 = raw_data[i][j][function_code][k][5];
                            b7 = raw_data[i][j][function_code][k][6];
                            b8 = raw_data[i][j][function_code][k][7];
                            if (b2 & 0x80)
                            {
                                sprintf(key_str, "V%d", (b1 - 1) * 3 + 1);
                                vol = b2 & 0x7F;
                                vol = (vol << 8) + b3;
                                cJSON_AddNumberToObject(vol_json, key_str, vol);
                            }

                            if (b4 & 0x80)
                            {
                                sprintf(key_str, "V%d", (b1 - 1) * 3 + 2);

                                vol = b4 & 0x7F;
                                vol = (vol << 8) + b5;
                                cJSON_AddNumberToObject(vol_json, key_str, vol);
                            }

                            if (b6 & 0x80)
                            {
                                sprintf(key_str, "V%d", (b1 - 1) * 3 + 3);
                                vol = b6 & 0x7F;
                                vol = (vol << 8) + b7;
                                cJSON_AddNumberToObject(vol_json, key_str, vol);
                            }
                        }
                    }

                    if (vol_json != NULL)
                    {
                        char *data_tmp = NULL;

                        strcpy(ptag.sn, node->sn);
                        ptag.port = ports[i];
                        data_tmp = cJSON_Print(vol_json);
                        if (strlen(data_tmp) > 2)
                        {
                            ptag.tag_node = data_tmp;
                            pp_send_tag_data(var->session, &ptag);
                        }
                        cJSON_Delete(vol_json);
                        vol_json = NULL;
                        free(data_tmp);
                    }
                }
            }
        }
    }

    return 0;
}

static int update_cell_temp(xieneng_car_var_t *var)
{
    tag_table_t ptag = {0};
    int i, j, k;
    node_cfg_t *node = NULL;
    time_t now = time(NULL);
    unsigned char function_code = FUNC_DETAIL_INFO;
    cJSON *temp_json = NULL;
    ptag.data_type = DATA_TYPE_SERVICE;
    strcpy(ptag.identifier, "TempUpdate");
    ptag.report_period = 0;
    ptag.time = now;

    for (i = 0; i < MAX_CAN_PORT; i++)
    {
        for (j = 0; j < MAX_LECU_CNT; j++)
        {
            node = nodes[i][j];
            if (node != NULL)
            {
                if (now - data_time[i][j] < 30)
                {
                    unsigned short temp;
                    double tmp = 0.0;
                    unsigned char b1 = 0;
                    unsigned char b2 = 0;
                    unsigned char b3 = 0;
                    unsigned char b4 = 0;
                    unsigned char b5 = 0;
                    unsigned char b6 = 0;
                    unsigned char b7 = 0;
                    unsigned char b8 = 0;
                    char key_str[8] = {0};

                    for (k = 102; k <= 201; k++)
                    {
                        b1 = raw_data[i][j][function_code][k][0];
                        if (b1 == k)
                        {
                            if (temp_json == NULL)
                            {
                                temp_json = cJSON_CreateObject();
                            }
                            b2 = raw_data[i][j][function_code][k][1];
                            b3 = raw_data[i][j][function_code][k][2];
                            b4 = raw_data[i][j][function_code][k][3];
                            b5 = raw_data[i][j][function_code][k][4];
                            b6 = raw_data[i][j][function_code][k][5];
                            b7 = raw_data[i][j][function_code][k][6];
                            b8 = raw_data[i][j][function_code][k][7];
                            if (b2 & 0x80)
                            {
                                sprintf(key_str, "T%d", (b1 - 102) * 3 + 1);
                                temp = b2 & 0x7F;
                                temp = (temp << 8) + b3;
                                temp = temp * 0.1 - 40;
                                cJSON_AddNumberToObject(temp_json, key_str, temp);
                            }

                            if (b4 & 0x80)
                            {
                                sprintf(key_str, "T%d", (b1 - 102) * 3 + 2);
                                temp = b4 & 0x7F;
                                temp = (temp << 8) + b5;
                                temp = temp * 0.1 - 40;
                                cJSON_AddNumberToObject(temp_json, key_str, temp);
                            }

                            if (b6 & 0x80)
                            {
                                sprintf(key_str, "T%d", (b1 - 102) * 3 + 3);
                                temp = b6 & 0x7F;
                                temp = (temp << 8) + b7;
                                temp = temp * 0.1 - 40;
                                cJSON_AddNumberToObject(temp_json, key_str, temp);
                            }
                        }
                    }

                    if (temp_json != NULL)
                    {
                        char *data_tmp = NULL;

                        strcpy(ptag.sn, node->sn);
                        ptag.port = ports[i];
                        data_tmp = cJSON_Print(temp_json);
                        if (strlen(data_tmp) > 2)
                        {
                            ptag.tag_node = data_tmp;
                            pp_send_tag_data(var->session, &ptag);
                        }
                        cJSON_Delete(temp_json);
                        temp_json = NULL;
                        free(data_tmp);
                    }
                }
            }
        }
    }

    return 0;
}

static int update_current(xieneng_car_var_t *var)
{
    tag_table_t ptag = {0};
    int i, j, k;
    time_t now = time(NULL);
    cJSON *current_json = NULL;
    static time_t last_report = 0;

    if (last_report == 0)
    {
        last_report = now;
    }

    if (var->CurrentTransformer_node == NULL)
    {
        return 0;
    }

    if (now - last_report < var->CurrentUpdate_period)
    {
        return 0;
    }

    if (current_watchdog > 30)
    {
        //dy_syslog(LOG_ERR, "Current data not availiable");
        return 0;
    }

    ptag.data_type = DATA_TYPE_SERVICE;
    strcpy(ptag.identifier, "CurrentReport");
    ptag.report_period = 0;
    ptag.time = now;

    current_json = cJSON_CreateObject();

    if (current_json != NULL)
    {
        char *data_tmp = NULL;
        double Charge = 0.0;
        double Discharge = 0.0;
        unsigned int duration = now - last_report;
        float charge_current_avg = 0;
        float discharge_current_avg = 0;

        if ((ChargeCnt + DischargeCnt + EmptyCnt) == 0)
        {
            goto out;
        }

        if (ChargeCnt != 0)
        {
            Charge = (double)ChargeSum * duration / (ChargeCnt + DischargeCnt + EmptyCnt); //mAs
            Charge = Charge / 3600;                                                        //mAh
            charge_current_avg = ChargeSum / ChargeCnt;
        }
        if (DischargeCnt != 0)
        {
            Discharge = (double)DischargeSum * duration / (ChargeCnt + DischargeCnt + EmptyCnt); //mAs
            Discharge = Discharge / 3600;                                                        //mAh
            discharge_current_avg = DischargeSum / DischargeCnt;
        }

        Charge = ((double)((int)((Charge + 0.005) * 100))) / 100.0;
        Discharge = ((double)((int)((Discharge + 0.005) * 100))) / 100.0;

        dy_syslog(LOG_DEBUG, "ChargeSum:%llu DischargeSum:%llu ChargeCnt:%d DischargeCnt:%d Charge:%f Discharge:%f duration:%d",
                  ChargeSum, DischargeSum, ChargeCnt, DischargeCnt, Charge, Discharge, duration);

        cJSON_AddNumberToObject(current_json, "current", current_cached);
        cJSON_AddNumberToObject(current_json, "ChargeCurrent", charge_current_avg);
        cJSON_AddNumberToObject(current_json, "DischargeCurrent", discharge_current_avg);
        cJSON_AddNumberToObject(current_json, "ChargeSum", Charge);
        ChargeSum = 0;
        ChargeCnt = 0;
        cJSON_AddNumberToObject(current_json, "DischargeSum", Discharge);
        DischargeSum = 0;
        DischargeCnt = 0;
        EmptyCnt = 0;
        strcpy(ptag.sn, var->CurrentTransformer_node->sn);
        data_tmp = cJSON_Print(current_json);
        if (strlen(data_tmp) > 2)
        {
            ptag.tag_node = data_tmp;
            pp_send_tag_data(var->session, &ptag);
        }
    out:
        cJSON_Delete(current_json);
        current_json = NULL;
        free(data_tmp);
        last_report = now;
    }
}

static int analysis_nodes(xieneng_car_var_t *var)
{
    int i = 0, j = 0;
    template_cfg_t *ptemplate = NULL;

    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        node_cfg_t *node = &var->nodes_cfg_table->node[i];
        ptemplate = NULL;
        if (node->port == CANBUS_0 || node->port == CANBUS_1)
        {
            int port = can_port_map(node->port);
            int lecu_addr = node->term_addr[0] - 1;
            detail[lecu_addr].id = lecu_addr;
            detail[lecu_addr].start = &raw_data[port][lecu_addr][FUNC_DETAIL_INFO][0][0];
            detail[lecu_addr].end = &raw_data[port][lecu_addr][FUNC_DETAIL_INFO][MAX_FRAME_NUMBER / 4 - 1][CAN_DATA_LEN - 1];

            if (ptemplate == NULL)
            {
                ptemplate = pp_find_template(var->template_table, node->template_id);
            }

            if (ptemplate != NULL)
            {
                for (j = 0; j < ptemplate->service_tab->serviceCnt; j++)
                {
                    if (ptemplate->service_tab->service[j].direction == 1 && strcmp(ptemplate->service_tab->service[j].identifier, "CellVolUpdate") == 0)
                    {
                        nodes[port][lecu_addr] = node;
                    }
                }
            }

            if (var->CellVolUpdate_period == 0)
            {
                if (ptemplate == NULL)
                {
                    ptemplate = pp_find_template(var->template_table, node->template_id);
                }

                if (ptemplate != NULL)
                {
                    for (j = 0; j < ptemplate->service_tab->serviceCnt; j++)
                    {
                        if (ptemplate->service_tab->service[j].direction == 1 && strcmp(ptemplate->service_tab->service[j].identifier, "CellVolUpdate") == 0)
                        {
                            var->CellVolUpdate_period = ptemplate->service_tab->service[j].report_period;
                        }
                        else if (ptemplate->service_tab->service[j].direction == 1 && strcmp(ptemplate->service_tab->service[j].identifier, "TempUpdate") == 0)
                        {
                            var->TempUpdate_period = ptemplate->service_tab->service[j].report_period;
                        }
                    }
                }
            }
            if (var->CurrentUpdate_period == 0)
            {
                if (ptemplate == NULL)
                {
                    ptemplate = pp_find_template(var->template_table, node->template_id);
                }

                if (ptemplate != NULL)
                {
                    for (j = 0; j < ptemplate->service_tab->serviceCnt; j++)
                    {
                        if (ptemplate->service_tab->service[j].direction == 1 && strcmp(ptemplate->service_tab->service[j].identifier, "CurrentReport") == 0)
                        {
                            var->CurrentUpdate_period = ptemplate->service_tab->service[j].report_period;
                            var->CurrentTransformer_node = node;
                            break;
                        }
                    }
                }
            }
        }
    }

    if (var->CellVolUpdate_period == 0)
    {
        var->CellVolUpdate_period = 60;
    }

    if (var->TempUpdate_period == 0)
    {
        var->TempUpdate_period = 60;
    }
}

static int send_query_broadcast(xieneng_car_var_t *var)
{
    static struct can_frame *can = NULL;
    int i = 0, j = 0;
    int max_func = 6;

    if (can == NULL)
    {
        can = calloc(1, sizeof(struct can_frame));
    }

    if (can == NULL)
    {
        return -1;
    }

    can->can_id = 0x730;
    can->can_dlc = 8;
    can->data[0] = 0xFF; // 广播

    for (i = 0; i < MAX_CAN_PORT; i++)
    {
        for (j = 0; j <= max_func; j++)
        {
            can->data[1] = j;
            usleep(20 * 1000);
            can_send(var->can_cfg->can_dev[i].fd, can);
        }
    }

    return 0;
}

static int store_can_frame_to_modbus_table(xieneng_car_var_t *var, can_frame_node_t *node)
{
    unsigned char lecu_addr;
    unsigned char function_code = FUNC_MAX;
    unsigned char frame_no;
    unsigned char *addr = NULL;
    struct can_frame *frame = &node->frame;
    int port = can_port_map(node->port);
    int can_id_hi = (frame->can_id >> 8) & 7;
    unsigned short *offset;
    unsigned short *term_start;
    time_t now = time(NULL);

    switch (can_id_hi)
    {
    case 2:
        // CAN ID 0x2XX
        function_code = FUNC_BRIEF_INFO;
        lecu_addr = frame->can_id & 0xFF;
        frame_no = frame->data[0];
        break;
    case 3:
        // CAN ID 0x3XX
        function_code = FUNC_DETAIL_INFO;
        lecu_addr = frame->can_id & 0xFF;
        frame_no = frame->data[0];
        break;
    case 4:
        // CAN ID 0x4XX
        function_code = FUNC_CONF_INFO;
        lecu_addr = frame->can_id & 0xFF;
        frame_no = frame->data[0];
        break;
    case 5:
        // CAN ID 0x5XX
        function_code = FUNC_CHARGE_CUR;
        lecu_addr = frame->can_id & 0xFF;
        frame_no = frame->data[0];
        break;
    case 7:
        // CAN ID 0x7XX
        switch (frame->can_id)
        {
        case 0x731:
            function_code = FUNC_BALANCE_STATUS_731;
            lecu_addr = frame->data[0] + 1;
            frame_no = frame->data[1];
            break;
        case 0x732:
            function_code = FUNC_BALANCE_STATUS_732;
            lecu_addr = frame->data[0] + 1;
            frame_no = 0;
            break;
        case 0x733:
            function_code = FUNC_BALANCE_STATUS_733;
            lecu_addr = frame->data[0] + 1;
            frame_no = frame->data[1];
            break;
        case 0x734:
            function_code = FUNC_BALANCE_STATUS_734;
            lecu_addr = frame->data[0] + 1;
            frame_no = frame->data[1];
            break;
        case 0x735:
            function_code = FUNC_BALANCE_STATUS_735;
            lecu_addr = frame->data[0] + 1;
            frame_no = frame->data[1];
            break;
        case 0x736:
            function_code = FUNC_BALANCE_STATUS_736;
            lecu_addr = frame->data[0] + 1;
            frame_no = frame->data[1];
            break;
        case 0x720:
            function_code = FUNC_CTRL_720;
            lecu_addr = frame->data[0] + 1;
            frame_no = 0;
            break;
        case 0x721:
            function_code = FUNC_CTRL_721;
            lecu_addr = frame->data[0] + 1;
            frame_no = 0;
            break;

        default:
            return -1;
        }
        break;

    default:
        return -1;
    }

    addr = (unsigned char *)&raw_data[port][lecu_addr][function_code][frame_no];
    memcpy(addr, frame->data, frame->can_dlc);

    if (now - last_time[port][lecu_addr] > 20)
    {
        if (nodes[port][lecu_addr] != NULL)
        {
            flush_device_status_by_sn(var->ds, ACTION_RCV, nodes[port][lecu_addr]->sn);
        }
        last_time[port][lecu_addr] = now;
    }

    data_time[port][lecu_addr] = now;

    if (function_code == FUNC_DETAIL_INFO && detail[lecu_addr].update_time != now)
    {
        detail[lecu_addr].uplast[2] = detail[lecu_addr].uplast[1];
        detail[lecu_addr].uplast[1] = detail[lecu_addr].uplast[0];
        detail[lecu_addr].uplast[0] = detail[lecu_addr].update_time;
        detail[lecu_addr].update_time = now;
    }

    return 0;
}

static int transparent_rsp(xieneng_car_var_t *var, regulate_cmd_backup_t *fields, can_frame_node_t *node)
{
    can_frame_packed_t can_packed;
    pp_real_time_data_t real_data = {0};

    can_packed.can_id = node->frame.can_id;
    can_packed.can_dlc = node->frame.can_dlc;
    memcpy(can_packed.data, node->frame.data, 8);

    if (fields != NULL)
    {
        real_data.mi = fields->mi;
        strcpy(real_data.sn, fields->sn);
        strncpy(real_data.src_identifier, fields->src_identifier, sizeof(real_data.src_identifier));
    }

    real_data.port = node->port;
    real_data.len = sizeof(can_packed);
    real_data.instruction_code = 0;
    real_data.data = (unsigned char *)&can_packed;

    send_to_proto_parser(var->session, &real_data);

    return 0;
}

static int getversion_rsp(xieneng_car_var_t *var, regulate_cmd_backup_t *fields, can_frame_node_t *node)
{
    unsigned int header;
    header = ((node->frame.data[3] << 24) | (node->frame.data[2] << 16) | (node->frame.data[1] << 8) | node->frame.data[0]);
    int index = header & 0xFFFF;
    static int slave_len = 0;
    static int balance_len = 0;
    static int cur_slave_len = 0;
    static int cur_balance_len = 0;
    char *p = NULL;
    int report = 0;
    int i;

    switch (node->frame.can_id)
    {
    case 0x7FF:
        p = slave_ver;
        if ((index & 0xF000) == 0x1000)
        {
            slave_len = node->frame.data[4];
            cur_slave_len = 0;
            return 0;
        }
        index &= 0xFFF;
        for (i = 4; i < node->frame.can_dlc; i++)
        {
            p[(index - 1) * 4 + i - 4] = node->frame.data[i];
        }
        cur_slave_len += (node->frame.can_dlc - 4);
        if (cur_slave_len >= slave_len)
        {
            report = 1;
        }
        break;
    case 0x7FA:
        p = balance_ver;
        if ((index & 0xF000) == 0x1000)
        {
            balance_len = node->frame.data[4];
            cur_balance_len = 0;
            return 0;
        }
        index &= 0xFFF;
        for (i = 4; i < node->frame.can_dlc; i++)
        {
            p[(index - 1) * 4 + i - 4] = node->frame.data[i];
        }
        cur_balance_len += (node->frame.can_dlc - 4);
        if (cur_balance_len >= balance_len)
        {
            report = 1;
        }
        break;
    default:
        dy_syslog(LOG_ERR, "bad can id");
        return -1;
    }

    if (report == 1)
    {
        tag_table_t ptag = {0};
        time_t now = time(NULL);
        cJSON *ver_json = NULL;
        ptag.data_type = DATA_TYPE_SERVICE;
        strcpy(ptag.identifier, "getDeviceVersion");
        ptag.report_period = 0;
        ptag.time = now;

        //restore the identifier
        {
            char key_str[64] = {0};
            data_info_t *data_info = NULL;

            sprintf(key_str, "CAN_%d", node->frame.can_id);
            data_info = var->identifier_backup.get(&var->identifier_backup, key_str);
            if (data_info)
            {
                fields = (regulate_cmd_backup_t *)data_info->data;
            }
        }

        if (fields != NULL)
        {
            ptag.mi = fields->mi;
            strcpy(ptag.sn, fields->sn);
        }

        ver_json = cJSON_CreateObject();

        if (node->frame.can_id == 0x7FF)
        {
            cJSON_AddStringToObject(ver_json, "deviceVersion", slave_ver);
        }
        else if (node->frame.can_id == 0x7FA)
        {
            cJSON_AddStringToObject(ver_json, "deviceVersion", balance_ver);
        }

        if (ver_json != NULL)
        {
            char *data_tmp = NULL;
            ;
            ptag.port = node->port;
            data_tmp = cJSON_Print(ver_json);
            if (strlen(data_tmp) > 2)
            {
                ptag.tag_node = data_tmp;
                pp_send_tag_data(var->session, &ptag);
            }
            cJSON_Delete(ver_json);
            ver_json = NULL;
            free(data_tmp);
        }
    }

    return 0;
}

static int can_rcv_frame(void *obj, can_frame_node_t *node)
{
    xieneng_car_var_t *var = (xieneng_car_var_t *)obj;

    if (node->frame.can_id == CURRENT_TRANSFORMER_CAN_ID)
    {
        unsigned int current = 0;
        current_watchdog = 0;
        int t = 0;

        current = (((unsigned int)node->frame.data[0] << 24) | ((unsigned int)node->frame.data[1] << 16) | ((unsigned int)node->frame.data[2] << 8) | (unsigned int)node->frame.data[3]);
        t = current - 0x80000000;
        current_cached = t;

        if (t <= 10 && t >= -10)
        {
            t = 0;
        }

        if (t > 0)
        {
            DischargeSum += t;
            DischargeCnt++;
        }
        else if (t == 0)
        {
            EmptyCnt++;
        }
        else
        {
            ChargeSum += (t * -1);
            ChargeCnt++;
        }
        //dy_syslog(LOG_DEBUG, "DischargeSum:%llu t:%d DischargeCnt:%d current_cached:%d", DischargeSum, t, DischargeCnt, current_cached);
        return 0;
    }

    store_can_frame_to_modbus_table(var, node);

    if (node->frame.can_id == 0x7FA || node->frame.can_id == 0x721 || node->frame.can_id == 0x7FF)
    {
        // 配置均衡返回结果
        regulate_cmd_backup_t *fields = NULL;
        pp_real_time_data_t real_data = {0};
        char rsp[64] = {0};
        char *p = NULL;
        int rsp_len;

        //restore the identifier
        {
            char key_str[64] = {0};
            data_info_t *data_info = NULL;

            sprintf(key_str, "CAN_%d", node->port);
            data_info = var->identifier_backup.get(&var->identifier_backup, key_str);
            if (data_info)
            {
                fields = (regulate_cmd_backup_t *)data_info->data;
            }
        }

        if (fields != NULL)
        {
            if (strcmp(fields->src_identifier, "__transparent") == 0 || strcmp(fields->src_identifier, "transparent") == 0)
            {
                return transparent_rsp(var, fields, node);
            }
            else if ((node->frame.can_id == 0x7FA || node->frame.can_id == 0x7FF) && strcmp(fields->src_identifier, "getDeviceVersion") == 0)
            {
                return getversion_rsp(var, fields, node);
            }
        }
        if (1)
        {
            p = rsp;
            if (node->frame.can_id == 0x7FA)
            {
                int can_id_internal = (node->frame.data[0] | (node->frame.data[1] << 8) | (node->frame.data[2] << 16) | (node->frame.data[3] << 24));
                int dev_no = (can_id_internal >> 11) & 0x1F;
                int func = (can_id_internal >> 16) & 0x7;

                *p = dev_no;
                p++; // 地址码
                if (func == 2)
                {
                    // 写寄存器响应
                    *p = MODBUS_FC_BALANCE_CONF_SET;
                    p++; // 功能码
                    *p = node->frame.can_dlc - 4 + 1;
                    p++;
                    *p = 0;
                    p++;
                    memcpy(p, node->frame.data + 4, node->frame.can_dlc - 4);
                    p += node->frame.can_dlc - 4;
                }
                else if (func == 4)
                {
                    // 读寄存器响应
                    *p = MODBUS_FC_BALANCE_CONF_GET;
                    p++; // 功能码
                    *p = node->frame.can_dlc - 4 + 1;
                    p++;
                    *p = 0;
                    p++;
                    memcpy(p, node->frame.data + 4, node->frame.can_dlc - 4);
                    p += node->frame.can_dlc - 4;
                }
                else if (func == 5)
                {
                    // 差错响应
                    *p = MODBUS_FC_BALANCE_CONF_SET;
                    p++; // 功能码
                    *p = 1;
                    p++; // 字节数
                    *p = node->frame.data[4];
                    p++;
                }
            }
            else if (node->frame.can_id == 0x7FF)
            {
                int can_id_internal = (node->frame.data[0] | (node->frame.data[1] << 8) | (node->frame.data[2] << 16) | (node->frame.data[3] << 24));
                int dev_no = (can_id_internal >> 11) & 0x1F;
                int func = (can_id_internal >> 16) & 0x7;

                *p = dev_no;
                p++; // 地址码
                if (func == 2)
                {
                    // 写寄存器响应
                    *p = MODBUS_FC_SLAVE_CTRL_SET;
                    p++; // 功能码
                    *p = node->frame.can_dlc - 4 + 1;
                    p++;
                    *p = 0;
                    p++;
                    memcpy(p, node->frame.data + 4, node->frame.can_dlc - 4);
                    p += node->frame.can_dlc - 4;
                }
                else if (func == 4)
                {
                    // 读寄存器响应
                    *p = MODBUS_FC_SLAVE_CTRL_GET;
                    p++; // 功能码
                    *p = node->frame.can_dlc - 4 + 1;
                    p++;
                    *p = 0;
                    p++;
                    memcpy(p, node->frame.data + 4, node->frame.can_dlc - 4);
                    p += node->frame.can_dlc - 4;
                }
                else if (func == 5)
                {
                    // 差错响应
                    *p = MODBUS_FC_SLAVE_CTRL_SET;
                    p++; // 功能码
                    *p = node->frame.can_dlc - 4 + 1;
                    p++;
                    *p = 0;
                    p++; // 字节数
                    *p = node->frame.data[4];
                    p++;
                }
            }
            else if (node->frame.can_id == 0x721)
            {
                *p = node->frame.data[0];
                p++; // 地址码
                *p = 0x10;
                p++; // 功能码
                *p = 2;
                p++; // 字节数
                *p = node->frame.data[1];
                p++;
                *p = node->frame.data[2];
                p++;
            }
            rsp_len = calculate_crc(rsp, p - rsp);

            if (fields != NULL)
            {
                real_data.mi = fields->mi;
                strcpy(real_data.sn, fields->sn);
                strncpy(real_data.src_identifier, fields->src_identifier, sizeof(real_data.src_identifier));
            }

            real_data.port = node->port;
            real_data.len = rsp_len;
            real_data.instruction_code = rsp[1];
            real_data.data = rsp;

            send_to_proto_parser(var->session, &real_data);
            dy_syslog_hex(LOG_DEBUG, rsp, rsp_len, "rsp can service");
        }
    }

    return 0;
}

static int xieneng_car_send_rglt(xieneng_car_var_t *var, pp_regulate_signal_t *regulate_data)
{
    pp_real_time_data_t real_data = {0};
    int ret = 0;
    static unsigned char *rsp_data = NULL;
    int len = 0;

    if (regulate_data == NULL)
        return -1;

    if (strcmp(regulate_data->src_identifier, "transparent") == 0 || strcmp(regulate_data->src_identifier, "__transparent") == 0)
    {
        char key_str[64] = {0};
        regulate_cmd_backup_t fields;
        int port_idx;
        can_frame_packed_t *can_packed = NULL;
        struct can_frame can;

        strcpy(fields.src_identifier, regulate_data->src_identifier);
        strcpy(fields.sn, regulate_data->sn);
        fields.mi = regulate_data->mi;

        sprintf(key_str, "CAN_%d", regulate_data->port);
        var->identifier_backup.insert(&var->identifier_backup, key_str, &fields, sizeof(fields));

        port_idx = can_port_map(regulate_data->port);
        can_packed = (can_frame_packed_t *)regulate_data->data;

        can.can_id = can_packed->can_id;
        can.can_dlc = can_packed->can_dlc;
        memcpy(can.data, can_packed->data, 8);

        can_send(var->can_cfg->can_dev[port_idx].fd, &can);

        return 0;
    }

    if (var->upgrading == 1)
    {
        dy_syslog(LOG_ERR, "upgrading now, don't handle the ctrl data");
        return 0;
    }

    if (rsp_data == NULL)
    {
        rsp_data = malloc(1024);
    }
    if (rsp_data == NULL)
    {
        return -1;
    }

    len = modbus_recv(var, regulate_data->port, regulate_data->data, regulate_data->len, rsp_data);
    if (len < 0)
    {
        dy_syslog(LOG_ERR, "error happend");
        return -1;
    }
    if (len > 0)
    {
        real_data.mi = regulate_data->mi;
        strcpy(real_data.sn, regulate_data->sn);
        strncpy(real_data.src_identifier, regulate_data->src_identifier, sizeof(real_data.src_identifier));

        real_data.port = regulate_data->port;
        real_data.len = len;
        real_data.instruction_code = rsp_data[1];
        real_data.data = rsp_data;

        send_to_proto_parser(var->session, &real_data);
    }
    else
    {
        // 存储identifier，等待总线返回
        char key_str[64] = {0};
        regulate_cmd_backup_t fields;

        strcpy(fields.src_identifier, regulate_data->src_identifier);
        strcpy(fields.sn, regulate_data->sn);
        fields.mi = regulate_data->mi;

        sprintf(key_str, "CAN_%d", regulate_data->port);
        var->identifier_backup.insert(&var->identifier_backup, key_str, &fields, sizeof(fields));
    }
    flush_device_status_by_sn(var->ds, ACTION_SND, regulate_data->sn);

    return 0;
}

static int xieneng_car_msg_tag_data_ctrl(xieneng_car_var_t *var, ipc_msg_t *mqtt_msg)
{
    pp_regulate_signal_t regulate_data;
    int ret = 0;

    ret = get_rglt_data_from_json(&regulate_data, mqtt_msg->payload);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "parse real data structure failed");
        return -1;
    }
    if (regulate_data.period > 0)
    {
        period_msg_t once;

        dy_syslog(LOG_INFO, "Recevied interval ctrl cmd periad %d", regulate_data.period);
        snprintf(once.key, 128, "%s_%s", regulate_data.sn, regulate_data.src_identifier);
        once.interval = regulate_data.period;
        once.last_poll = 0;
        once.rs = calloc(1, sizeof(pp_regulate_signal_t));
        memcpy(once.rs, &regulate_data, sizeof(pp_regulate_signal_t));
        once.started = 1;
        once.rs->data = calloc(1, regulate_data.len);
        memcpy(once.rs->data, regulate_data.data, regulate_data.len);

        period_msg_add(var->period, &once);
    }
    else
    {
        ret = xieneng_car_send_rglt(var, &regulate_data);
        if (ret < 0)
        {
            dy_syslog(LOG_ERR, "parse real data structure failed");
            return -1;
        }
    }

out:
    free(regulate_data.data);

    return 0;
}

static int xieneng_car_msg_change_current_report_interval(xieneng_car_var_t *var, cJSON *msg)
{
    char *data = NULL;
    cJSON *root = NULL;
    char *data_tmp = NULL;
    int ret = 0;
    char identifier[64];
    int interval = 0;
    int mi;
    tag_table_t ptag = {0};
    cJSON *resp = NULL;
    time_t now = time(NULL);
    char *t = NULL;

    GET_JSON_VALUE_INT(msg, "Interval", interval);
    GET_JSON_VALUE_INT(msg, "mi", mi);

    if (interval == 0)
    {
        goto out;
    }

    data = read_file_data(XIENENG_CAR_CFG_PATH);
    if (data == NULL)
    {
        goto new;
    }

    root = cJSON_Parse(data);
    if (!root)
    {
        goto new;
    }

    new : if (root == NULL)
    {
        root = cJSON_CreateObject();
        if (root == NULL)
        {
            goto out;
        }
    }
    cJSON_DeleteItemFromObject(root, "current_report_interval");
    cJSON_AddNumberToObject(root, "current_report_interval", interval);
    var->CurrentUpdate_period = interval;
    data_tmp = cJSON_Print(root);
    write_file_data(XIENENG_CAR_CFG_PATH, data_tmp, strlen(data_tmp));

    if (var->CurrentTransformer_node == NULL)
    {
        goto out;
    }

    ptag.data_type = DATA_TYPE_SERVICE;
    strcpy(ptag.identifier, "ChangeInterval");
    ptag.report_period = 0;
    ptag.time = now;
    ptag.mi = mi;

    resp = cJSON_CreateObject();
    if (resp != NULL)
    {
        cJSON_AddNumberToObject(resp, "Result", 0);
        strcpy(ptag.sn, var->CurrentTransformer_node->sn);
        t = cJSON_Print(resp);
        if (strlen(t) > 2)
        {
            ptag.tag_node = t;
            pp_send_tag_data(var->session, &ptag);
        }
    }

out:
    cJSON_Delete(root);
    cJSON_Delete(resp);
    if (data != NULL)
    {
        free(data);
    }
    free(data_tmp);
    free(t);

    return ret;
}

static int can_send_command_data(xieneng_car_var_t *var, unsigned int can_id, unsigned char *data, int len)
{
    unsigned int header = 0;
    unsigned short crc;
    static struct can_frame *can = NULL;
    int offset = 0;
    int index;

    if (can == NULL)
    {
        can = calloc(1, sizeof(struct can_frame));
    }

    if (can == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed");
        return -1;
    }

    header = header | (1 << 12);
    header |= (6 << 16);
    header |= (1 << 21);
    crc = crc16(data, len);

    can->can_id = can_id;
    can->can_dlc = 8;
    can->data[0] = header & 0xFF;
    can->data[1] = (header >> 8) & 0xFF;
    can->data[2] = (header >> 16) & 0xFF;
    can->data[3] = (header >> 24) & 0xFF;
    can->data[4] = len & 0xFF;
    can->data[5] = 0xCC;
    can->data[6] = crc & 0xFF;
    can->data[7] = (crc >> 8) & 0xFF;
    can_send(var->can_cfg->can_dev[0].fd, can);

    index = 1;
    while (1)
    {
        if (offset >= len)
        {
            break;
        }
        header = 2 << 12;
        header |= index;
        header |= (6 << 16);
        header |= (1 << 21);
        can->data[0] = header & 0xFF;
        can->data[1] = (header >> 8) & 0xFF;
        can->data[2] = (header >> 16) & 0xFF;
        can->data[3] = (header >> 24) & 0xFF;
        can->data[4] = data[offset];
        can->data[5] = data[offset + 1];
        can->data[6] = data[offset + 2];
        can->data[7] = data[offset + 3];
        offset += 4;
        index += 1;
        can_send(var->can_cfg->can_dev[0].fd, can);
    }
}

static int xieneng_car_get_version(xieneng_car_var_t *var, cJSON *msg)
{
    int offset_Level1Id = 2;
    int offset_Level2Id = 1;
    int offset_Level3Id = 0;
    int L1_DevID, L2_DevID, L3_DevID, Dev_Type;
    unsigned int cmdHwID = 0;
    unsigned int can_id;
    char buff[256];
    unsigned int L1_ID, L2_ID, L3_ID;
    char sn[64];
    int mi;

    GET_JSON_VALUE_INT(msg, "L1_DevId", L1_DevID);
    GET_JSON_VALUE_INT(msg, "L2_DevId", L2_DevID);
    GET_JSON_VALUE_INT(msg, "L3_DevId", L3_DevID);
    GET_JSON_VALUE_INT(msg, "Dev_Type", Dev_Type);
    GET_JSON_VALUE_INT(msg, "mi", mi);
    GET_JSON_VALUE_STRING(msg, "sn", sn);

    // L3从控-0; L2设备-1; L1设备-2; L3-均衡-3
    switch (Dev_Type)
    {
    case 0:
        // 从控设备
        L1_ID = L3_DevID + 1 + offset_Level1Id;
        L2_ID = L2_DevID + offset_Level2Id;
        can_id = 0x7FF;
        break;
    case 3:
        // 均衡设备
        L1_ID = L3_DevID + 1 + offset_Level1Id;
        L2_ID = L2_DevID + offset_Level2Id;
        can_id = 0x7FA;
        break;
    default:
        dy_syslog(LOG_DEBUG, "not supported dev type");
        return -1;
    }

    L3_ID = L1_DevID + offset_Level3Id;

    cmdHwID = L1_ID << 15;
    cmdHwID |= (L2_ID << 20);
    cmdHwID |= (L3_ID << 24);
    cmdHwID |= 7;
    cmdHwID |= (0xFFF << 3);
    {
        char key_str[64] = {0};
        regulate_cmd_backup_t fields;

        strcpy(fields.src_identifier, "getDeviceVersion");
        strcpy(fields.sn, sn);
        fields.mi = mi;

        sprintf(key_str, "CAN_%d", CANBUS_0);
        var->identifier_backup.insert(&var->identifier_backup, key_str, &fields, sizeof(fields));

        sprintf(key_str, "CAN_%d", can_id);
        var->identifier_backup.insert(&var->identifier_backup, key_str, &fields, sizeof(fields));
    }

    snprintf(buff, 256, "0x%08X SetDebugPara 0x12345678 0 0x12345678", cmdHwID);
    can_send_command_data(var, can_id, buff, strlen(buff));

    snprintf(buff, 256, "0x%08X ShowVersion", cmdHwID);
    can_send_command_data(var, can_id, buff, strlen(buff));

    return 0;
}

static int xieneng_car_handle_set_rglt(xieneng_car_var_t *var, ipc_msg_t *mqtt_msg)
{
    cJSON *msg = NULL;
    int ret = 0;
    char identifier[64];

    msg = cJSON_Parse(mqtt_msg->payload);
    if (msg == NULL)
    {
        ret = -1;
        goto out;
    }

    GET_JSON_VALUE_STRING(msg, "identifier", identifier);
    if (strcmp(identifier, "ChangeInterval") == 0)
    {
        xieneng_car_msg_change_current_report_interval(var, msg);
    }
    else if (strcmp(identifier, "getDeviceVersion") == 0)
    {
        xieneng_car_get_version(var, msg);
    }
    else if (strcmp(identifier, "downLoadHisData") == 0)
    {
        xieneng_car_history_data(var, msg);
    }

out:
    cJSON_Delete(msg);
    return ret;
}

static int ds_flush_timer(xieneng_car_var_t *var)
{
    time_t now = time(NULL);

    TIMER_CONFIRM(var->node_status_timer);

    if (now >= var->nodes_status_sync_date + 30)
    {
        static time_t lasttime = 0;
        var->nodes_status_sync_date = now;
        char *node_status = ds_print(var->ds);

        dy_syslog(LOG_INFO, "online_status_changed:%d strlen(node_status):%d, (now-lasttime):%d",
                  var->ds->online_status_changed, strlen(node_status), now - lasttime);
        if (node_status && strlen(node_status) > 20 &&
            (var->ds->online_status_changed || now > lasttime + 3600)) //在线状态变化上报，1小时也会上报
        {
            char topic[TOPIC_MAX_LEN];
            int ret = 0;

            snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/%s", var->sn_str, var->proc_name, TOPIC_EVT_NODESSTATUS);

            ret = ipc_session_publish(var->session, topic, node_status, strlen(node_status));
            if (ret == 0)
            {
                lasttime = now;
                var->ds->online_status_changed = false;
            }
            node_sta_backup(var->ds, CAN_NODE_STATUS_BAK_FILE);
        }
        if (node_status)
        {
            write_file_data(NODES_CACHE "/xieneng_car_status.json", node_status, strlen(node_status));
            free(node_status);
        }
    }
}

static int period_service_poll(xieneng_car_var_t *var)
{
    TIMER_CONFIRM(var->period_poll_timer);
    int ret = 0;
    static int all_started = 0;
    time_t now = time(NULL);
    static time_t last_trigger_ts = 0;
    static time_t last_query = 0;
    static time_t last_update_vol_temp = 0;
    static time_t last_update_temp_temp = 0;
    static time_t last_store = 0;

    current_watchdog++;

    if (var->upgrading == 1)
    {
        return 0;
    }

    if (now < last_update_vol_temp)
        last_update_vol_temp = now;
    if (now - last_update_vol_temp > var->CellVolUpdate_period)
    {
        update_cell_vol(var);
        last_update_vol_temp = now;
    }

    if (now < last_update_temp_temp)
        last_update_temp_temp = now;
    if (now - last_update_temp_temp > var->TempUpdate_period)
    {
        update_cell_temp(var);
        last_update_temp_temp = now;
    }

    if (now < last_store)
        last_store = now;
    if ((now - last_store) >= 30) {
        last_store += 30;
        if (now >= (last_store + 30))
            last_store = now;
        store_vol_temp(var, (unsigned int) last_store);
    }

    update_current(var);

    if (now < last_query)
        last_query = now;
    // send broadcast to query
    if (now - last_query > 20)
    {
        send_query_broadcast(var);
        last_query = now;
    }

    period_msg_t *msg = period_msg_next(var->period);
    if (msg && msg->started)
    {
        ret = xieneng_car_send_rglt(var, msg->rs);
        if (ret < 0)
        {
            dy_syslog(LOG_ERR, "parse real data structure failed");
        }
    }

    if (now - last_trigger_ts > 20)
    {
        if (all_started == 0)
        {
            all_started = period_services_retrigger(var->session, var->period, var->nodes_cfg_table, var->template_table, ports, ARRAY_SIZE(ports));
        }
        last_trigger_ts = now;
    }
    return 0;
}

static void xieneng_car_loop(xieneng_car_var_t *var)
{
    int ret = -1, maxfd, i;
    fd_set rset;
    struct timeval timeout;

    while (1)
    {
        SELECT_INIT();
        SELECT_ADD_FD(var->node_status_timer);
        SELECT_ADD_FD(var->period_poll_timer);

        timeout.tv_usec = 0;
        timeout.tv_sec = 5;

        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            dy_syslog(LOG_INFO, "errno %d\n", errno);

            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret > 0)
        {
            if (var->node_status_timer > 0 && FD_ISSET(var->node_status_timer, &rset))
            {
                FD_CLR(var->node_status_timer, &rset);
                ds_flush_timer(var);
            }
            if (var->period_poll_timer > 0 && FD_ISSET(var->period_poll_timer, &rset))
            {
                FD_CLR(var->period_poll_timer, &rset);
                period_service_poll(var);
            }
        }
    }
}

static void xieneng_car_subscribe_all(xieneng_car_var_t *var)
{
    ipc_session_t *ipc_session = var->session;
    char topic[TOPIC_MAX_LEN] = {0};
    int i = 0;

    for (i = 0; i < ARRAY_SIZE(ports); i++)
    {
        snprintf(topic, TOPIC_MAX_LEN, "ipc/+/%s/device/+/data/%s", port_enum2char(ports[i]), TOPIC_SEND_RGLT_SIGNAL_RAW_DATA);
        ipc_session_subscribe(ipc_session, topic);
    }

    if (var->CurrentTransformer_node != NULL)
    {
        dy_syslog(LOG_DEBUG, "====>Current report node:%s", var->CurrentTransformer_node->sn);
        //ipc/21880400006D/CANBUS_0/device/Gn7BGs0N/data/Set_Rglt
        snprintf(topic, TOPIC_MAX_LEN, "ipc/+/+/device/%s/data/%s", var->CurrentTransformer_node->sn, TOPIC_EVT_SET_RGLT);
        ipc_session_subscribe(ipc_session, topic);
    }

    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/+/device/+/data/%s", TOPIC_EVT_SET_RGLT);
    ipc_session_subscribe(ipc_session, topic);
}

static int xieneng_car_set_filter(xieneng_car_var_t *var)
{
    int i;

    if (var->upgrading == 1)
    {
        struct can_filter *filters = NULL;

        filters = malloc(2 * sizeof(struct can_filter));
        if (filters == NULL)
        {
            dy_syslog(LOG_ERR, "malloc failed");
            return -1;
        }
        filters[0].can_id = 0x7FF;
        filters[0].can_mask = 0x7FF;
        filters[1].can_id = 0x7FA;
        filters[1].can_mask = 0x7FF;

        for (i = 0; i < MAX_CAN_PORT; i++)
        {
            if (var->can_cfg->can_dev[i].fd > 0)
                setsockopt(var->can_cfg->can_dev[i].fd, SOL_CAN_RAW, CAN_RAW_FILTER, filters, 2 * sizeof(struct can_filter));
        }

        free(filters);
    }
    else
    {
        for (i = 0; i < MAX_CAN_PORT; i++)
        {
            if (var->can_cfg->can_dev[i].fd > 0)
            {
                if (var->can_cfg->can_dev[i].filters)
                {
                    setsockopt(var->can_cfg->can_dev[i].fd, SOL_CAN_RAW, CAN_RAW_FILTER, var->can_cfg->can_dev[i].filters, var->can_cfg->can_dev[i].filter_cnt * sizeof(struct can_filter));
                }
                else
                {
                    struct can_filter filter;
                    filter.can_mask = 0;
                    filter.can_id = 0;
                    setsockopt(var->can_cfg->can_dev[i].fd, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(struct can_filter));
                }
            }
        }
    }
}

static int xieneng_car_mqtt_handle_recv_msg(void *obj, ipc_msg_t *mqtt_msg)
{
    xieneng_car_var_t *var = (xieneng_car_var_t *)obj;

    if (strstr(mqtt_msg->topic, TOPIC_SEND_RGLT_SIGNAL_RAW_DATA))
    {
        //发送下行数据
        xieneng_car_msg_tag_data_ctrl(var, mqtt_msg);
    }
    else if (strstr(mqtt_msg->topic, TOPIC_NOTIFY_UPGRADE))
    {
        if (strcmp(mqtt_msg->payload, START_UPGRADE) == 0)
        {
            dy_syslog(LOG_ERR, "receive start upgrade, stop period poll");
            var->upgrading = 1;
            xieneng_car_set_filter(var);
        }
        else
        {
            dy_syslog(LOG_ERR, "receive stop upgrade, start period poll");
            var->upgrading = 0;
            xieneng_car_set_filter(var);
        }
    }
    else if (strstr(mqtt_msg->topic, TOPIC_EVT_SET_RGLT))
    {
        xieneng_car_handle_set_rglt(var, mqtt_msg);
    }
}

static int xieneng_car_load_cfg(xieneng_car_var_t *var)
{
    char *data = NULL;
    cJSON *root = NULL;
    int ret = 0;

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

    root = cJSON_Parse(data);
    if (!root)
    {
        dy_syslog(LOG_ERR, "parse cfg file %s error", XIENENG_CAR_CFG_PATH);
        ret = -1;
        goto out;
    }

    GET_JSON_VALUE_INT(root, "current_report_interval", var->CurrentUpdate_period);

out:
    cJSON_Delete(root);
    if (data != NULL)
    {
        free(data);
    }
    return ret;
}

// 建立与内部broker之间的MQTT连接
int xieneng_car_mqtt_client_init(xieneng_car_var_t *var)
{
    char clientId[MAX_CLIENT_ID_LEN] = {0};

    snprintf(clientId, MAX_CLIENT_ID_LEN, "INT_xieneng_car_%s", var->sn_str);
    var->session = ipc_session_new(clientId, (void*)var, IPC_DEFAULT);
    if (var->session == NULL)
        return -1;

    ipc_session_set_callbacks(var->session, xieneng_car_mqtt_handle_recv_msg, NULL);
    xieneng_car_subscribe_all(var);
    ipc_session_start(var->session);

}

int xieneng_car_init(xieneng_car_var_t *var)
{
    int i, j;
    const char *board_name = NULL;

    check_make_dir(NODES_CACHE);
    check_make_dir(NODES_CFG);
    get_board_sn(var->sn_str);
    get_process_name(var->proc_name);
    board_name = get_board_name();

    dy_syslog(LOG_INFO, "board name:%s", board_name);
    dy_syslog(LOG_INFO, "board SN:%s", var->sn_str);

    if (load_nodes_cfg(&var->nodes_cfg_table, NODES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load nodes cfg fail");
    }
    if (load_templates_cfg(&var->template_table, TEMPLATES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load template cfg fail");
    }
    analysis_nodes(var);

    dev_status_init(&var->ds, var->nodes_cfg_table, var->template_table, ports, ARRAY_SIZE(ports));
    node_sta_recovery(var->ds, CAN_NODE_STATUS_BAK_FILE);

    kv_array_init(&var->identifier_backup, 32);

    var->node_status_timer = my_timer_create();
    if (var->node_status_timer > 0)
    {
        my_timer_set(var->node_status_timer, 1, 5000);
    }
    var->period_poll_timer = my_timer_create();
    if (var->period_poll_timer > 0)
    {
        my_timer_set(var->period_poll_timer, 1, 1000);
    }

    xieneng_car_mqtt_client_init(var);
    period_msg_init(&var->period, 512);
    period_services_trigger(var->session, var->period, var->nodes_cfg_table, var->template_table, ports, ARRAY_SIZE(ports));

    var->can_cfg = dy_can_cfg_init((void *)var, NULL, can_rcv_frame);

    if (load_can_cfg_and_init(var->can_cfg, CAN_MODULE_CFG) < 0)
    {
        dy_syslog(LOG_ERR, "load can cfg failed");
        return -1;
    }
    xieneng_car_load_cfg(var);

    xieneng_car_db_init(var);
    return 0;
}

int main(int argc, char *argv[])
{
    xieneng_car_var_t var = {0};

    memset(&var, 0, sizeof(var));

    xieneng_car_init(&var);
    xieneng_car_loop(&var);

    return 0;
}
