#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 "ess_protocol.h"
#include "dy_utils/dy_common.h"

static gw_port_e ports[] = {CANBUS_0, CANBUS_1};

struct identifier_map {
    const char * ess_name;
    const char * new_ident;
};

static const struct identifier_map g_ident_map_[] = {
    { "S2A_ALARM",               "hardwarefault"  },
    { "S2A_SUMDATA2",            "zhuangtai" },
    { "S2A_SUMDATA4",            "rackxinxi" },
    { NULL,                      NULL },
};

/* simple hash table to map identifiers */
static struct hash_intptr * g_ident_map;

/* print every CAN frame verbosely */
static int g_dump_can_data;

struct ess_simple_info {
    unsigned int      msgid;
    const char *      tagname;
    ess_parse_func    parse_func;
    int               period;       /* periodic data report in seconds */
    unsigned int      canport;      /* 0/1 */
    const char *      devsn;        /* device SN */
};

#define ESS_GWSN            "%s"
#define ESS_CANPORTX        1
#define ESS_PROTO_DEVSN     ESS_GWSN "_Rack1_1"
#define CAN_ALARM_DEVSN     ESS_GWSN "_DI"
#define CAN_AIRCON_DEVSN    ESS_GWSN "_aircond"
#define ESS_CLUSTER_DEVSN	ESS_GWSN "_Cluster_1"

static const struct ess_simple_info ess_info_[] = {
    { 0x0701FF41,   "S2A_ALARM",           ess_parse_hardwarefault /* ess_parse_S2A_ALARM */, 1, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0B01FF41,   "S2A_SUMDATA1",        ess_parse_S2A_SUMDATA1, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0B02FF41,   "S2A_SUMDATA2",        ess_parse_zhuangtai /* ess_parse_S2A_SUMDATA2 */, 1, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0B03FF41,   "S2A_SUMDATA3",        ess_parse_S2A_SUMDATA3, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0B04FF41,   "S2A_SUMDATA4",        ess_parse_rackxinxi /* ess_parse_S2A_SUMDATA4 */, 1, ESS_CANPORTX, ESS_PROTO_DEVSN },

    { 0x0B05FF41,   "S2A_SUMDATA5",        ess_parse_S2A_SUMDATA5, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0B06FF41,   "S2A_SUMDATA6",        ess_parse_S2A_SUMDATA6, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0B08FF41,   "S2A_SUMDATA8",        ess_parse_S2A_SUMDATA8, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0B09FF41,   "S2A_LiquidInfo",      ess_parse_S2A_LiquidInfo, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0B0AFF41,   "S2A_DryContact",      ess_parse_S2A_DryContact, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },

#if 0
    { 0x0F11EE41,   "S2P_Detail_Ucell1",   ess_parse_S2P_Detail_Ucell1, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell2",   ess_parse_S2P_Detail_Ucell2, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell3",   ess_parse_S2P_Detail_Ucell3, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell4",   ess_parse_S2P_Detail_Ucell4, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell5",   ess_parse_S2P_Detail_Ucell5, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },

    { 0x0F11EE41,   "S2P_Detail_Ucell6",   ess_parse_S2P_Detail_Ucell6, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell7",   ess_parse_S2P_Detail_Ucell7, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell8",   ess_parse_S2P_Detail_Ucell8, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell9",   ess_parse_S2P_Detail_Ucell9, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell10",  ess_parse_S2P_Detail_Ucell10, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },

    { 0x0F11EE41,   "S2P_Detail_Ucell11",  ess_parse_S2P_Detail_Ucell11, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell12",  ess_parse_S2P_Detail_Ucell12, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell13",  ess_parse_S2P_Detail_Ucell13, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell14",  ess_parse_S2P_Detail_Ucell14, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell15",  ess_parse_S2P_Detail_Ucell15, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },

    { 0x0F11EE41,   "S2P_Detail_Ucell16",  ess_parse_S2P_Detail_Ucell16, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell17",  ess_parse_S2P_Detail_Ucell17, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell18",  ess_parse_S2P_Detail_Ucell18, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F11EE41,   "S2P_Detail_Ucell19",  ess_parse_S2P_Detail_Ucell19, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F12EE41,   "S2P_Detail_Tcell1",   ess_parse_S2P_Detail_Tcell1, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },

    { 0x0F12EE41,   "S2P_Detail_Tcell2",   ess_parse_S2P_Detail_Tcell2, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x0F12EE41,   "S2P_Detail_Tcell3",   ess_parse_S2P_Detail_Tcell3, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x00117F91,   "M2S_CMD1",            ess_parse_M2S_CMD1, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x00127F91,   "M2S_CMD2",            ess_parse_M2S_CMD2, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
    { 0x01117FEE,   "P2S_CMD",             ess_parse_P2S_CMD, 0, ESS_CANPORTX, ESS_PROTO_DEVSN },
#endif

    /* 消防控制器 报警状态 */
    { 0x1C000109,   "alarmdata0",          ess_parse_dummy,           0, 0x0, CAN_ALARM_DEVSN },
    { 0x1C00010A,   "alarmdata",           ess_parse_ALARM_STATUS,    1, 0x0, CAN_ALARM_DEVSN },
    { 0x1C00010B,   "alarmdata1",          ess_parse_dummy,           0, 0x0, CAN_ALARM_DEVSN },
    { 0x18FF45F4,   "readtemp",            ess_parse_READ_TEMP,       1, 0x0, CAN_AIRCON_DEVSN },
    { 0x18FFC13A,   "readon",              ess_parse_READ_ON,         1, 0x0, CAN_AIRCON_DEVSN },

    { 0x18FFC13B,   "read1",               ess_parse_READ1,           1, 0x0, CAN_AIRCON_DEVSN },
    { 0x18FFC13C,   "read2",               ess_parse_READ2,           1, 0x0, CAN_AIRCON_DEVSN },
    /* 多级告警，该CANID不存在，仅用于数据上报 */
    { 0xFFFFFFFF,   "gaojingxinxi",        ess_parse_gaojingxinxi,    1, ESS_CANPORTX, ESS_PROTO_DEVSN },

	/*虚拟簇设备，仅用于数据上报*/
    {0xFFFFFFFF,    "Read1001_100B",       ess_parse_Read1001_100B,   1, ESS_CANPORTX, ESS_CLUSTER_DEVSN },
    {0xFFFFFFFF,    "Read100C_1016",       ess_parse_ClusterTotal,    1, ESS_CANPORTX, ESS_CLUSTER_DEVSN },
    {0xFFFFFFFF,    "Read0x1017_1028",     ess_parse_ClusterSystem,    1, ESS_CANPORTX, ESS_CLUSTER_DEVSN },
    {0xFFFFFFFF,    "Cluster_charge_discharge", ess_parse_Cluster_charge_discharge, 1, ESS_CANPORTX, ESS_CLUSTER_DEVSN },

    { 0x0,          NULL,                  NULL },
};

static void get_time_string(char * out, size_t outlen)
{
    struct tm tim, * ptm;
    struct timespec spec;

    if (clock_gettime(CLOCK_REALTIME, &spec) == -1) {
        spec.tv_sec = 0;
        spec.tv_nsec = 0;
    }

    ptm = localtime_r(&spec.tv_sec, &tim);
    if (ptm == NULL)
        ptm = &tim;
    snprintf(out, outlen, "%d-%02d-%02d %02d:%02d:%02d.%03d",
        ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,
        ptm->tm_hour, ptm->tm_min, ptm->tm_sec, (int) (spec.tv_nsec / 1000000));
}

static void can_dump_frame(const struct can_frame * canp, const char * prefix)
{
    char nowt[128];
    if (prefix == NULL)
        prefix = "";
	
    get_time_string(nowt, sizeof(nowt));
    fprintf(stdout, "%s\n", nowt);
    fprintf(stdout, "%scan_id:  %#x\n", prefix, canp->can_id);
    fprintf(stdout, "%scan_dlc: %#x\n", prefix, (unsigned int) canp->can_dlc);
    fprintf(stdout, "%sdata:    %02x %02x %02x %02x %02x %02x %02x %02x\n", prefix,
        (unsigned int) canp->data[0], (unsigned int) canp->data[1],
        (unsigned int) canp->data[2], (unsigned int) canp->data[3],
        (unsigned int) canp->data[4], (unsigned int) canp->data[5],
        (unsigned int) canp->data[6], (unsigned int) canp->data[7]);
    fputs("-------------------------------------------\n", stdout);
}

static int find_caninfo(ess_info_var_t * varp,
    const can_frame_node_t * nodep, struct ess_caninfo * * caninfp)
{
    int found;
    unsigned int msgid;
    unsigned int idx, nume;
    struct ess_caninfo * caninfo;
    const struct can_frame * pframe;
    const struct ess_simple_info * sinfo;

    found = -1;
    pframe = &nodep->frame;
    msgid = pframe->can_id;
    /* important: extract only the CAN identifier: */
    if (msgid & CAN_EFF_FLAG)
        msgid &= CAN_EFF_MASK;
    else
        msgid &= CAN_SFF_MASK;

    /* S2P_Detail_Ucell */
    if (msgid == ESS_UCELL_MSGID) {
        uint8_t csc_no, fidx;
        csc_no = pframe->data[0];
        if (csc_no < 1 || csc_no > 5) {
            fprintf(stderr, "Error, invalid CSC NO: %u\n", (unsigned int) csc_no);
            fflush(stderr);
            return -1;
        }

        caninfo = varp->ucell[csc_no - 1];
        fidx = pframe->data[1];
        if (fidx >= ESS_UCELL_NUM) {
            fprintf(stderr, "Error, invalid frame index for CSC %u: %u\n",
                (unsigned int) csc_no, (unsigned int) fidx);
            fflush(stderr);
            return -1;
        }

        found = (int) fidx;
        *caninfp = &caninfo[found];
        return 0;
    }

    /* S2P_Detail_Tcell */
    if (msgid == ESS_TCELL_MSGID) {
        uint8_t csc_no, fidx;
        csc_no = (pframe->data[1] & 0x3f);
        if (csc_no < 1 || csc_no > 5) {
            fprintf(stderr, "Error, invalid CSC NO: %u\n", (unsigned int) csc_no);
            fflush(stderr);
            return -1;
        }

        caninfo = varp->tcell[csc_no - 1];
        fidx = (pframe->data[1] >> 6) & 0x3;
        if (fidx >= ESS_TCELL_NUM) {
            fprintf(stderr, "Error, invalid frame index for CSC %u: %u\n",
                (unsigned int) csc_no, (unsigned int) fidx);
            fflush(stderr);
            return -1;
        }

        found = (int) fidx;
        *caninfp = &caninfo[found];
        return 0;
    }

    nume = varp->num_can;
    caninfo = varp->caninfp;

    for (idx = 0; idx < nume; ++idx) {
        sinfo = &ess_info_[idx];
        if (sinfo->msgid == 0)
            break;
        if (msgid == sinfo->msgid) {
            found = (int) idx;
            caninfo = &caninfo[found];
            break;
        }
    }

    if (found != -1) {
        *caninfp = caninfo;
        return 0;
    }
    return -1;
}

static void ess_caninfo_report(
    struct ess_caninfo * caninfo, ess_info_var_t * varp)
{
    cJSON * jdat;
    char * json;
    int newtag, maxtag;
    const char * tagname;

    json = NULL;
    jdat = NULL;
    if (caninfo->parse_func == NULL)
        return;

    newtag = -1;
    tagname = caninfo->tagname;
    hash_intptr_findint(g_ident_map, tagname, strlen(tagname), &newtag);
    maxtag = (int) (sizeof(g_ident_map_) / sizeof(g_ident_map_[0]) - 0x1);
    if (newtag >= 0 && newtag < maxtag)
        tagname = g_ident_map_[newtag].new_ident;

    caninfo->parse_func(caninfo, varp, (void * *) &jdat, 0);
    if (jdat != NULL) {
        char * tagn;
        cJSON * jmsg;
        unsigned int nowt;

        tagn = cJSON_PrintUnformatted(jdat);
        nowt = (unsigned int) time(NULL);
        jmsg = cJSON_CreateObject();
        cJSON_AddStringToObject(jmsg, "sn", caninfo->devsn);
        cJSON_AddNumberToObject(jmsg, "mi", 0);
        cJSON_AddNumberToObject(jmsg, "time", nowt);
        cJSON_AddStringToObject(jmsg, "identifier", tagname);
        cJSON_AddStringToObject(jmsg, "tag_node", tagn);
        cJSON_AddNumberToObject(jmsg, "report_period", 0);
        cJSON_AddNumberToObject(jmsg, "data_type", 2);
        json = cJSON_PrintUnformatted(jmsg);

        free(tagn);
        cJSON_Delete(jdat);
        cJSON_Delete(jmsg);
    }

    if (json != NULL) {
        size_t tlen;
        char topic[256];
        snprintf(topic, sizeof(topic),
            "ipc/%s/CANBUS_%u/device/%s/data/service/%s",
            varp->sn_str, caninfo->canport, caninfo->devsn, tagname);

        tlen = strlen(json);
        ipc_session_publish(varp->session, topic, (unsigned char *) json, (int) tlen);
        free(json);
    }
}

static void ess_cell_report(struct ess_caninfo * caninfo,
    ess_info_var_t * varp, const char * identifier)
{
    cJSON * jdat;
    char * json, * tagn;

    jdat = NULL;
    json = tagn = NULL;
    if (caninfo->parse_func == NULL)
        return;

    caninfo->parse_func(caninfo, varp, (void * *) &jdat, 0);
    if (jdat != NULL) {
        cJSON * jmsg;
        unsigned int nowt;

        tagn = cJSON_PrintUnformatted(jdat);
        nowt = (unsigned int) time(NULL);
        jmsg = cJSON_CreateObject();
        cJSON_AddStringToObject(jmsg, "sn", caninfo->devsn);
        cJSON_AddNumberToObject(jmsg, "mi", 0);
        cJSON_AddNumberToObject(jmsg, "time", nowt);
        cJSON_AddStringToObject(jmsg, "identifier", identifier);
        cJSON_AddStringToObject(jmsg, "tag_node", tagn);
        cJSON_AddNumberToObject(jmsg, "report_period", 0);
        cJSON_AddNumberToObject(jmsg, "data_type", 2);
        json = cJSON_PrintUnformatted(jmsg);

        cJSON_Delete(jdat);
        cJSON_Delete(jmsg);
    }

    if (json != NULL) {
        size_t tlen;
        char topic[256];
        snprintf(topic, sizeof(topic),
            "ipc/%s/CANBUS_%u/device/%s/data/service/%s",
            varp->sn_str, caninfo->canport, caninfo->devsn, identifier);

        tlen = strlen(json);
        ipc_session_publish(varp->session, topic, (unsigned char *) json, (int) tlen);
        free(json);
    }

    if (tagn)
        free(tagn);
}

static int can_rcv_frame(void *obj, can_frame_node_t *node)
{
    int ret, portno;
    struct can_frame * pframe;
    struct ess_caninfo * caninfo;
    ess_info_var_t * varp = (ess_info_var_t *) obj;

    caninfo = NULL;
    pframe = &node->frame;
    portno = node->port == CANBUS_1;
    if (pframe->can_id & (CAN_RTR_FLAG | CAN_ERR_FLAG)) {
        fprintf(stderr, "CAN frame with ID dropped: %#x\n", pframe->can_id);
        fflush(stderr);
        return 0;
    }

    ret = pthread_mutex_lock(&varp->ess_lock);
    if (ret) {
        fprintf(stderr, "Error, failed to acquire mutex lock: %d\n", ret);
        fflush(stderr);
        return 0;
    }

    ret = find_caninfo(varp, node, &caninfo);
    if (ret < 0) {
        if (g_dump_can_data != 0) {
            fprintf(stdout, "CAN frame not found on port %d:\n", portno);
            can_dump_frame(&node->frame, "\t");
        }
    } else {
        caninfo->count++;
        if (0) /* portno == 1) */ {
            /* swap can data for CANBUS_1 */
            uint64_t data = 0;
            memcpy(&data, &node->frame.data, sizeof(data));
            data = __builtin_bswap64(data);
            memcpy(&node->frame.data,&data,8);
        }
        caninfo->canfrm = node->frame;
        caninfo->lastup = uptime_msec(NULL);
        if (g_dump_can_data != 0) 
		{
            fprintf(stdout, "New CAN frame arrived on port %d\n", portno);
            can_dump_frame(&node->frame, "\t");
        }
    }

    ret = pthread_mutex_unlock(&varp->ess_lock);
    if (ret) {
        fprintf(stderr, "Error, failed to release mutex lock: %d\n", ret);
        fflush(stderr);
    }
    return 0;
}

static void ess_P2S_CMD_DOWN(ess_info_var_t * varp, const char * topic, cJSON * p2scmd)
{
    int tmpval, can_port;
    struct can_frame fcan;
    uint8_t crc_p2s_cmd;
    uint8_t p2s_sbmu_no;
    uint8_t rq_read_detail_data;
    uint8_t p2s_cmd_data[6];

    can_port = (topic && strstr(topic, "CANBUS_1")) ? 1 : 0;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "CRC_P2S_CMD", tmpval);
    crc_p2s_cmd = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "P2S_SBMU_No", tmpval);
    p2s_sbmu_no = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "RQ_Read_Detail_Data", tmpval);
    rq_read_detail_data = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "P2S_CMD_Data0", tmpval);
    p2s_cmd_data[0] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "P2S_CMD_Data1", tmpval);
    p2s_cmd_data[1] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "P2S_CMD_Data2", tmpval);
    p2s_cmd_data[2] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "P2S_CMD_Data3", tmpval);
    p2s_cmd_data[3] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "P2S_CMD_Data4", tmpval);
    p2s_cmd_data[4] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "P2S_CMD_Data5", tmpval);
    p2s_cmd_data[5] = (uint8_t) tmpval;

    memset(&fcan, 0, sizeof(fcan));
    fcan.can_id = CAN_EFF_FLAG | 0x01117FEE;
    fcan.can_dlc = 0x8;
    fcan.data[0] = crc_p2s_cmd;
    fcan.data[1] = p2s_sbmu_no;
    fcan.data[2] = rq_read_detail_data & 0x3;
    fcan.data[2] |= (p2s_cmd_data[0] & 0x3f) << 0x2;
    fcan.data[3] = p2s_cmd_data[1];
    fcan.data[4] = p2s_cmd_data[2];
    fcan.data[5] = p2s_cmd_data[3];
    fcan.data[6] = p2s_cmd_data[4];
    fcan.data[7] = p2s_cmd_data[5];

    if (0) {
        uint64_t data = 0;
        memcpy(&data, fcan.data, sizeof(data));
        data = __builtin_bswap64(data);
        memcpy(fcan.data, &data, sizeof(data));
    }

    fprintf(stdout, "Send P2S_CMD: %02x %02x %02x %02x %02x %02x %02x %02x\n",
        (unsigned int) fcan.data[0], (unsigned int) fcan.data[1],
        (unsigned int) fcan.data[2], (unsigned int) fcan.data[3],
        (unsigned int) fcan.data[4], (unsigned int) fcan.data[5],
        (unsigned int) fcan.data[6], (unsigned int) fcan.data[7]);
    fflush(stdout);
    can_send(varp->can_cfg->can_dev[can_port].fd, &fcan);
}

static void ess_M2S_CMD_DOWN(ess_info_var_t * varp, const char * topic, cJSON * p2scmd)
{
    int tmpval, can_port;
    struct can_frame fcan;
    uint8_t crc_m2s_cmd1;
    uint8_t m2s_sbmu_no;
    uint8_t mainrlycmd;
	uint8_t cmdflg_ins_en;
	uint8_t m2s_cmd1_heater;
    uint8_t m2s_cmd1_data[6];
    static unsigned int log_count = 0;

    can_port = (topic && strstr(topic, "CANBUS_1")) ? 1 : 0;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "CRC_M2S_CMD1", tmpval);
    crc_m2s_cmd1 = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "SBMU_No", tmpval);
    m2s_sbmu_no = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "MainRlyCmd", tmpval);
    mainrlycmd = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "CmdFlg_Ins_En", tmpval);
    cmdflg_ins_en = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "M2S_CMD1_Heater", tmpval);
    m2s_cmd1_heater = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "M2S_CMD1_Data0", tmpval);
    m2s_cmd1_data[0] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "M2S_CMD1_Data1", tmpval);
    m2s_cmd1_data[1] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "M2S_CMD1_Data2", tmpval);
    m2s_cmd1_data[2] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "M2S_CMD1_Data3", tmpval);
    m2s_cmd1_data[3] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "M2S_CMD1_Data4", tmpval);
    m2s_cmd1_data[4] = (uint8_t) tmpval;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "M2S_CMD1_Data5", tmpval);
    m2s_cmd1_data[5] = (uint8_t) tmpval;

    memset(&fcan, 0, sizeof(fcan));
    fcan.can_id = CAN_EFF_FLAG | 0x00117F91;
    fcan.can_dlc = 0x8;
    fcan.data[0] = crc_m2s_cmd1;
    fcan.data[1] = m2s_sbmu_no;

    fcan.data[2] = mainrlycmd & 0x3;
    fcan.data[2] |= (cmdflg_ins_en & 0x1) << 0x2;
	fcan.data[2] |= (m2s_cmd1_heater & 0x3) << 0x3;
	fcan.data[2] |= (m2s_cmd1_data[0] & 0x7) << 0x5;

    fcan.data[3] = m2s_cmd1_data[1];
    fcan.data[4] = m2s_cmd1_data[2];
    fcan.data[5] = m2s_cmd1_data[3];
    fcan.data[6] = m2s_cmd1_data[4];
    fcan.data[7] = m2s_cmd1_data[5];

    if (0) {
        uint64_t data = 0;
        memcpy(&data, fcan.data, sizeof(data));
        data = __builtin_bswap64(data);
        memcpy(fcan.data, &data, sizeof(data));
    }

    if ((log_count++ & 0x1f) == 0 || g_dump_can_data) {
        fprintf(stdout, "Send M2S_CMD: %02x %02x %02x %02x %02x %02x %02x %02x\n",
            (unsigned int) fcan.data[0], (unsigned int) fcan.data[1],
            (unsigned int) fcan.data[2], (unsigned int) fcan.data[3],
            (unsigned int) fcan.data[4], (unsigned int) fcan.data[5],
            (unsigned int) fcan.data[6], (unsigned int) fcan.data[7]);
        fflush(stdout);
    }
    can_send(varp->can_cfg->can_dev[can_port].fd, &fcan);
}

static void ess_start_connection(ess_info_var_t * varp, const char * topic, cJSON * p2scmd)
{
    int tmpval, can_port;
    struct can_frame fcan;
    static unsigned int log_count = 0;

    can_port = (topic && strstr(topic, "CANBUS_1")) ? 1 : 0;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "TAG_7530", tmpval);

    memset(&fcan, 0, sizeof(fcan));
    fcan.can_id = CAN_EFF_FLAG | 0x00117F91;
    fcan.can_dlc = 0x8;

    fcan.data[0] = 0;
    fcan.data[1] = 255;
    fcan.data[2] = 0;
    if (tmpval == 1)
        fcan.data[2] = 2;
    else if (tmpval == 2)
        fcan.data[2] = 3;

    if ((log_count++ & 0x1f) == 0 || g_dump_can_data) {
        fprintf(stdout, "Start Connection: %02x %02x %02x %02x %02x %02x %02x %02x\n",
            (unsigned int) fcan.data[0], (unsigned int) fcan.data[1],
            (unsigned int) fcan.data[2], (unsigned int) fcan.data[3],
            (unsigned int) fcan.data[4], (unsigned int) fcan.data[5],
            (unsigned int) fcan.data[6], (unsigned int) fcan.data[7]);
        fflush(stdout);
    }
    can_send(varp->can_cfg->can_dev[can_port].fd, &fcan);
}

static void ess_M2S_CMD2_DOWN(ess_info_var_t * varp, const char * topic, cJSON * p2scmd)
{
    int tmpval, can_port;
    struct can_frame fcan;
    static unsigned int log_count = 0;

    can_port = (topic && strstr(topic, "CANBUS_1")) ? 1 : 0;

    tmpval = 0;
    GET_JSON_VALUE_INT(p2scmd, "TAG_9DBF", tmpval);

    memset(&fcan, 0, sizeof(fcan));
    fcan.can_id = CAN_EFF_FLAG | 0x00127F91;
    fcan.can_dlc = 0x8;
    fcan.data[2] = (__u8) tmpval;

    if ((log_count++ & 0x1f) == 0 || g_dump_can_data) {
        fprintf(stdout, "M2S_CMD2: %02x %02x %02x %02x %02x %02x %02x %02x\n",
            (unsigned int) fcan.data[0], (unsigned int) fcan.data[1],
            (unsigned int) fcan.data[2], (unsigned int) fcan.data[3],
            (unsigned int) fcan.data[4], (unsigned int) fcan.data[5],
            (unsigned int) fcan.data[6], (unsigned int) fcan.data[7]);
        fflush(stdout);
    }
    can_send(varp->can_cfg->can_dev[can_port].fd, &fcan);
}

static void ess_P2S_air_control(ess_info_var_t * varp, const char * topic, cJSON * p2scmd)
{
    int loopback;
    struct can_frame fcan;
    int can_port, ret, can_fd;
    int reqmode = 0;
    int settemp = 0, newtemp;
    static unsigned int log_count = 0;

    can_port = (topic && strstr(topic, "CANBUS_1")) ? 1 : 0;

    GET_JSON_VALUE_INT(p2scmd, "reqmode", reqmode);
    GET_JSON_VALUE_INT(p2scmd, "settemp", settemp);
    reqmode &= 0x3;
    if (settemp < -40)
        newtemp = 0;
    else if (settemp > 210)
        newtemp = 250;
    else
        newtemp = settemp + 40; /* transform range -40~210 to 0~250 */
    memset(&fcan, 0, sizeof(fcan));
    fcan.can_id = CAN_EFF_FLAG | 0x18FF45F4;
    fcan.can_dlc = 0x8;
    fcan.data[0] = (__u8) reqmode;
    fcan.data[4] = (__u8) newtemp;
    can_fd = varp->can_cfg->can_dev[can_port].fd;

    loopback = 1;
    ret = setsockopt(can_fd, SOL_CAN_RAW,
        CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
    if (ret == -1) {
        int error = errno;
        fprintf(stderr, "Error, setsockopt(%d) has failed: %s\n",
            can_fd, strerror(error));
        fflush(stderr);
    }

    ret = can_send(can_fd, &fcan);
    loopback = 0;
    if (setsockopt(can_fd, SOL_CAN_RAW,
        CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)) == -1) {
        int error = errno;
        fprintf(stderr, "Error, setsockopt(%d) has failed: %s\n",
            can_fd, strerror(error));
        fflush(stderr);
    }

    if ((log_count++ & 0x1f) == 0 || g_dump_can_data) {
        fprintf(stdout, "AIRCOND control returned: %d, canid: %#x, data: %02x %02x %02x %02x %02x %02x %02x %02x\n",
            ret, (unsigned int) fcan.can_id,
            (unsigned int) fcan.data[0], (unsigned int) fcan.data[1],
            (unsigned int) fcan.data[2], (unsigned int) fcan.data[3],
            (unsigned int) fcan.data[4], (unsigned int) fcan.data[5],
            (unsigned int) fcan.data[6], (unsigned int) fcan.data[7]);
            fflush(stdout);
    }

    ret = -1;
    /* update 设置温度 TAG_3F3 */
    hash_intptr_findint(varp->canhash, "readtemp", 0x8, &ret);
    if (ret >= 0) {
        struct ess_caninfo * caninfo;
        caninfo = &varp->caninfp[ret];
        caninfo->canfrm = fcan;
    }
}

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

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

    identifier[0] = '\0';
    identifier[sizeof(identifier) - 1] = '\0';
    GET_JSON_VALUE_STRING(msg, "identifier", identifier);
    if (strcmp(identifier, "P2S_CMD_DOWN") == 0) {
        ess_P2S_CMD_DOWN(var, mqtt_msg->topic, msg);
	}else if (strcmp(identifier, "M2S_CMD_DOWN") == 0) {
		 ess_M2S_CMD_DOWN(var, mqtt_msg->topic, msg);
    } else if (strcmp(identifier, "airwater_control") == 0) {
        ess_P2S_air_control(var, mqtt_msg->topic, msg);
    } else if (strcmp(identifier, "WritePExpect") == 0) {
        /* silently ignored */
        reply = 0;
    } else if (strcmp(identifier, "Start_Connection_set") == 0) {
        ess_start_connection(var, mqtt_msg->topic, msg);
    } else if (strcmp(identifier, "clear_bms_error") == 0) {
        ess_M2S_CMD2_DOWN(var, mqtt_msg->topic, msg);
    } else {
        reply = 0;
        fprintf(stderr, "Warning, unsupported identifier: %s\n", identifier);
        fflush(stderr);
    }

out:
    if (reply && msg) {
        int msgid = 0;
        cJSON * rsp = NULL;
        char topic[256], * slash;

        topic[sizeof(topic) - 1] = '\0';
        strncpy(topic, mqtt_msg->topic, sizeof(topic) - 1);
        slash = strrchr(topic, '/');
        if (slash == NULL)
            goto next;

        slash++;
        strcpy(slash, "Rsp_Rglt");
        GET_JSON_VALUE_INT(msg, "mi", msgid);
        rsp = cJSON_CreateObject();
        cJSON_AddNumberToObject(rsp, "mi", msgid);
        cJSON_AddNumberToObject(rsp, "status", 1);
        cJSON_AddNumberToObject(rsp, "timestamp", time(NULL));
        slash = cJSON_PrintUnformatted(rsp);
        cJSON_Delete(rsp);
        if (slash != NULL) {
            ipc_session_publish(var->session, topic, (unsigned char *) slash, strlen(slash));
            free(slash);
        }
    }
next:
    cJSON_Delete(msg);
    return ret;
}

static int ds_flush_timer(ess_info_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",
                (int) var->ds->online_status_changed, (int) strlen(node_status), (int) (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, (unsigned char *) 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);
        }
    }
    return 0;
}

static int period_service_poll(ess_info_var_t *varp)
{
    int ret;
    uint64_t upmsec;
    unsigned int nowt;
    unsigned int idx, num_can;
    TIMER_CONFIRM(varp->period_poll_timer);

    ret = pthread_mutex_lock(&varp->ess_lock);
    if (ret) {
        fprintf(stderr, "Error, failed to acquire mutex lock: %d\n", ret);
        fflush(stderr);
        return -1;
    }

    upmsec = 0;
    uptime_msec(&upmsec);
    nowt = (unsigned int) ess_uptime(NULL, NULL);
    num_can = varp->num_can;
    for (idx = 0; idx < num_can; ++idx) {
        unsigned int period;
        struct ess_caninfo * caninfo;

        caninfo = &(varp->caninfp[idx]);
        if (caninfo->period <= 0)
            continue;
        period = (unsigned int) caninfo->period;
        if (caninfo->lastper > (nowt + period)) {
            caninfo->lastper = nowt;
            continue;
        }

        if (nowt < (caninfo->lastper + period))
            continue;
        caninfo->lastper += period;
        if (caninfo->lastper < nowt)
            caninfo->lastper = nowt;
        ess_caninfo_report(caninfo, varp);
    }

    for (idx = 0; idx < ESS_CSC_MAX; ++idx) {
        unsigned int period;
        struct ess_caninfo * caninfo;

        caninfo = varp->ucell[idx];
        if (caninfo == NULL || caninfo->parse_func == NULL)
            continue;

        period = (unsigned int) caninfo->period;
        if (period == 0)
            continue;
        if (nowt < (caninfo->lastper + period))
            continue;
        caninfo->lastper += period;
        if (caninfo->lastper < nowt)
            caninfo->lastper = nowt;

        if (upmsec < (caninfo->lastup + ESS_DATA_VALIDTIME))
            ess_cell_report(caninfo, varp, "RackCellVoltage");
    }

    for (idx = 0; idx < ESS_CSC_MAX; ++idx) {
        unsigned int period;
        struct ess_caninfo * caninfo;

        caninfo = varp->tcell[idx];
        if (caninfo == NULL || caninfo->parse_func == NULL)
            continue;

        period = (unsigned int) caninfo->period;
        if (period == 0)
            continue;
        if (nowt < (caninfo->lastper + period))
            continue;
        caninfo->lastper += period;
        if (caninfo->lastper < nowt)
            caninfo->lastper = nowt;
        if (upmsec < (caninfo->lastup + ESS_DATA_VALIDTIME))
            ess_cell_report(caninfo, varp, "RackCellTemperature");
    }

    ret = pthread_mutex_unlock(&varp->ess_lock);
    if (ret) {
        fprintf(stderr, "Error, failed to release mutex lock: %d\n", ret);
        fflush(stderr);
        return -1;
    }
    return 0;
}

static void xieneng_car_loop(ess_info_var_t *var)
{
    int ret = -1, maxfd;
    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)
        {
            int error = errno;
            dy_syslog(LOG_INFO, "errno %d\n", error);
            if (error == 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(ess_info_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);
    }

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

#if 0
static int xieneng_car_set_filter(ess_info_var_t *var)
{
    int i;

    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));
            }
        }
    }
    return 0;
}
#endif

static int xieneng_car_mqtt_handle_recv_msg(void *obj, ipc_msg_t *mqtt_msg)
{
    ess_info_var_t *var = (ess_info_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)) {
        /* NOTHING TODO */
    } else if (strstr(mqtt_msg->topic, TOPIC_EVT_SET_RGLT)) {
        xieneng_car_handle_set_rglt(var, mqtt_msg);
    }
    return 0;
}

// 建立与内部broker之间的MQTT连接
int xieneng_car_mqtt_client_init(ess_info_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);
    return 0;
}

int xieneng_car_init(ess_info_var_t *var)
{
    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");
    }

    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); */

    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;
    }
    return 0;
}

static int allocate_ess_info(ess_info_var_t * var)
{
    size_t idx, nume;
    char gwsn[SN_MAX_LEN];
    struct ess_caninfo * cani;

    cani = NULL;
    nume = sizeof(ess_info_) / sizeof(ess_info_[0]) - 1;
    fprintf(stdout, "Number of ess_caninfo: %zu\n", nume);
    fflush(stdout);

    memset(gwsn, 0, sizeof(gwsn));
    get_board_sn(gwsn);

    cani = (struct ess_caninfo *) calloc(nume, sizeof(*cani));
    if (cani == NULL) {
        fprintf(stderr, "Error, system out of memory: %u\n",
            (unsigned int) (nume * sizeof(*cani)));
        fflush(stderr);
        return -1;
    }

    for (idx = 0; idx < nume; ++idx) {
        int ret;
        size_t tlen;
        struct ess_caninfo * info;
        const struct ess_simple_info * sinfo;

        info = &cani[idx];
        sinfo = &ess_info_[idx];
        if (sinfo->msgid == 0 || sinfo->tagname == NULL)
            break;

        info->msgid = sinfo->msgid;
        info->tagname = sinfo->tagname;
        info->parse_func = sinfo->parse_func;
        info->period = sinfo->period;
        info->lastper = (unsigned int) ess_uptime(NULL, NULL);
        info->canport = sinfo->canport;
        snprintf(info->devsn, SN_MAX_LEN, sinfo->devsn, gwsn);
        info->devsn[SN_MAX_LEN - 1] = '\0';
        tlen = strlen(sinfo->tagname);
        ret = hash_intptr_addint(&var->canhash,
            sinfo->tagname, (unsigned int) tlen, (int) idx);
        if (ret < 0) {
            fprintf(stderr, "Error, failed to insert hashkey: '%s': %d\n",
                sinfo->tagname, ret);
            fflush(stderr);
        }
    }
    var->num_can = nume;
    var->caninfp = cani;

    for (idx = 0; idx < ESS_CSC_MAX; ++idx) {
        int jdx;
        struct ess_caninfo * info;
        struct ess_caninfo * caninfo = NULL;

        caninfo = (struct ess_caninfo *) calloc(ESS_UCELL_NUM, sizeof(*caninfo));
        if (!caninfo)
            abort();
        info = caninfo;
        for (jdx = 0; jdx < ESS_UCELL_NUM; ++jdx) {
            info->msgid = ESS_UCELL_MSGID;
            info->tagname = "S2P_Detail_Ucell";
            info->canport = ESS_CANPORTX;
            snprintf(info->devsn, sizeof(info->devsn),
                ESS_GWSN "_Pack1_1_%d", gwsn, idx + 1);
            info->parse_func = NULL;
            info++;
        }

        caninfo->period = 1;
        caninfo->parse_func = ess_parse_ucell;
        caninfo->lastper = (unsigned int) ess_uptime(NULL, NULL);
        var->ucell[idx] = caninfo;
    }

    for (idx = 0; idx < ESS_CSC_MAX; ++idx) {
        int jdx;
        struct ess_caninfo * info;
        struct ess_caninfo * caninfo = NULL;

        caninfo = (struct ess_caninfo *) calloc(ESS_TCELL_NUM, sizeof(*caninfo));
        if (!caninfo)
            abort();
        info = caninfo;
        for (jdx = 0; jdx < ESS_TCELL_NUM; ++jdx) {
            info->msgid = ESS_TCELL_MSGID;
            info->tagname = "S2P_Detail_Tcell";
            info->canport = ESS_CANPORTX;
            snprintf(info->devsn, sizeof(info->devsn),
                ESS_GWSN "_Pack1_1_%d", gwsn, idx + 1);
            info++;
        }

        caninfo->period = 1;
        caninfo->parse_func = ess_parse_tcell;
        caninfo->lastper = (unsigned int) ess_uptime(NULL, NULL);
        var->tcell[idx] = caninfo;
    }

    return 0;
}

static void init_ident_map(struct hash_intptr * * hashpp)
{
    int idx, nmaps;

    nmaps = (int) (sizeof(g_ident_map_) / sizeof(g_ident_map_[0]) - 0x1);
    for (idx = 0; idx < nmaps; ++idx) {
        int ret;
        const char * key;

        key = g_ident_map_[idx].ess_name;
        if (key == NULL)
            break;
        ret = hash_intptr_addint(hashpp,
            key, (unsigned int) strlen(key), idx);
        fprintf(stdout, "Inserting identifier hashmap (%s, %d): %d\n",
            key, idx, ret);
        fflush(stdout);
    }
}

int main(int argc, char *argv[])
{
    int ret;
    ess_info_var_t * varp = NULL;
    g_ident_map = NULL;
    g_dump_can_data = 0;

    do {
        const char * verbose = getenv("DUMP_CAN_DATA");
        if (verbose && verbose[0] == '1')
            g_dump_can_data = -1;
    } while (0);

    varp = (ess_info_var_t *) calloc(0x1, sizeof(*varp));
    if (varp == NULL) {
        fprintf(stderr, "Error, system out of memory: %u\n",
            (unsigned int) sizeof(*varp));
        fflush(stderr);
        return 1;
    }

    ret = pthread_mutex_init(&varp->ess_lock, NULL);
    if (ret) {
        free(varp);
        fprintf(stderr, "Error, failed to initialize mutex lock: %d\n", ret);
        fflush(stderr);
        return 2;
    }

    if (allocate_ess_info(varp) < 0) {
        free(varp);
        return 3;
    }

    init_ident_map(&g_ident_map);
    xieneng_car_init(varp);
    xieneng_car_loop(varp);

    free(varp);
    hash_intptr_removeall(&g_ident_map);
    return 0;
}
