/*
 * Created by jiaqiang.ye@lnxall.com
 *
 * Parse ESS protocol data
 *
 * 2022/10/28
 */

#include "ess_protocol.h"
#include "dy_utils/dy_pp.h"
#include "dy_utils/cJSON.h"
#include <math.h>

/* 多字节大端 */
#define ESS_FLAG_EB    0x1

struct ess_parser {
    const char *       essname;
    uint16_t           startbit;  /* initial bit field */
    uint16_t           length;    /* length in bits */
    double             factor;    /* multiply factor, usually 1 */
    int                offset;    /* signed integer, value offset */
    int                valtype;   /* value type */

    /* TAG name for LNXALL platform */
    const char *       new_tagname;
};

union ess_integer {
    int           ess_int;
    unsigned int  ess_uint;
};

time_t ess_uptime(time_t * timp, unsigned int * msec)
{
    struct timespec spec;

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

    if (msec != NULL)
        *msec = (unsigned int) (spec.tv_nsec / 1000000);
    if (timp != NULL)
        *timp = spec.tv_sec;
    return spec.tv_sec;
}

uint64_t uptime_msec(uint64_t * upt)
{
    uint64_t rval;
    struct timespec spec;

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

    rval = (uint64_t) spec.tv_sec;
    rval = rval * 1000 + (uint64_t) (spec.tv_nsec / 1000000);
    if (upt != NULL)
        *upt = rval;
    return rval;
}

static int float_near(double fval, double tval, double delta)
{
    double tval0 = tval - delta;
    double tval1 = tval + delta;
    if (fval < tval0 || fval > tval1)
        return 0;
    return 1;
}

static void ess_factor_value(cJSON * pobj,
    union ess_integer essi, const struct ess_parser * essp)
{
    const char * tagname;

    tagname = essp->new_tagname;
    if (tagname == NULL) {
        tagname = essp->essname;
#if 0 /* SHUTUP! */
        fprintf(stderr, "Warning, using ESS protocol name: '%s'\n",
            tagname);
        fflush(stderr);
#endif
        /* quite impossible: */
        if (__builtin_expect(tagname == NULL, 0)) {
            fputs("Error, invalid ess tagname found.\n", stderr);
            fflush(stderr);
            return;
        }
    }

    if (essp->valtype == TAG_TYPE_INT32) {
        int eint;
        eint = essi.ess_int;
        eint += essp->offset;
        if (essp->factor != 1)
            eint = (int) (essp->factor * eint);
        cJSON_AddNumberToObject(pobj, tagname, eint);
    } else {
        double essd;
        double fv;
        essd = (double) essi.ess_int;
        if (essp->factor != 1)
            essd *= essp->factor;
        essd += essp->offset;
        fv = round(1000.0 * essd);
        fv /= 1000.0;
        cJSON_AddNumberToObject(pobj, tagname, fv);
    }
}

static void ess_extract_value(cJSON * pobj,
    const struct can_frame * pframe,
    const struct ess_parser * essp, int flags)
{
    uint16_t endbyte, bleft;
    uint16_t startbyte, sbyte, tbyte, idx;
    union ess_integer essi;

    bleft = essp->length;
    sbyte = essp->startbit & 0x7;
    endbyte = essp->startbit + bleft;
    if (bleft == 0 || endbyte > 64) {
        fprintf(stderr, "Error, invalid startbit and/or bit length for '%s': %u, %u\n",
            essp->essname, (unsigned int) essp->startbit, (unsigned int) bleft);
        fflush(stderr);
        return;
    }

    tbyte = sbyte;
    essi.ess_uint = 0;
    startbyte = essp->startbit >> 0x3;
    endbyte = (endbyte - 0x1) >> 0x3;

    for (idx = startbyte; idx <= endbyte; ++idx) {
        uint16_t bits;
        unsigned char cha;
        unsigned int cval;

        cha = pframe->data[idx];
        if (sbyte > 0)
            cha >>= sbyte;
        bits = 0x8 - sbyte;

        if (bleft < bits) {
            if ((sbyte + bleft) < 0x8)
                cha &= (0x1 << bleft) - 0x1;
            cval = (unsigned int) cha;
            if (flags & ESS_FLAG_EB)
                essi.ess_uint = (essi.ess_uint << bleft) | cval;
            else {
                unsigned int sbit = (0x8 * ((idx - startbyte) & 0x3));
                if (sbit > 0)
                    sbit -= tbyte;
                essi.ess_uint |= cval << sbit;
            }
            break;
        }

        cval = (unsigned int) cha;
        if (flags & ESS_FLAG_EB)
            essi.ess_uint = (essi.ess_uint << bits) | cval;
        else {
            unsigned int sbit = (0x8 * ((idx - startbyte) & 0x3));
            if (sbit > 0)
                sbit -= tbyte;
            essi.ess_uint |= cval << sbit;
        }
        sbyte = 0;
        bleft -= bits;
        if (bleft == 0)
            break;
    }
	
    ess_factor_value(pobj, essi, essp);
}

static int ess_parse_common(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags,
    const struct ess_parser * essp)
{
    cJSON * retval;
    const struct can_frame * pframe;

    retval = cJSON_CreateObject();
    if (retval == NULL) {
        fputs("Error, system out of memory!\n", stderr);
        fflush(stderr);
        return -1;
    }

    pframe = &ci->canfrm;
    while (essp->essname != NULL) {
        /* only use LNXALL defined tag names: */
#if 0   /* NOTE: disabled due to `ess_parse_READ_TEMP/`ess_parse_READ_ON/`ess_parse_READ1/`ess_parse_READ2 functions */
        if (essp->new_tagname == NULL) {
            essp++;
            continue;
        }
#endif

        switch (essp->valtype) {
        case TAG_TYPE_INT32:
        case TAG_TYPE_FLOAT:
            ess_extract_value(retval, pframe, essp, flags);
            break;

        default:
            fprintf(stderr, "Error, unknown ESS type for '%s': %#x\n",
                essp->essname, (unsigned int) essp->valtype);
            fflush(stderr);
            break;
        }

        essp++;
    }

    *pretval = (void *) retval;
    return 0;
}

static void ess_factor_value1(union ess_integer essi,
    const struct ess_parser * essp, int * valpint, double * valpfloat)
{
    if (essp->valtype == TAG_TYPE_INT32) {
        int eint;
        eint = essi.ess_int;
        eint += essp->offset;
        if (essp->factor != 1)
            eint = (int) (essp->factor * eint);
        if (valpint != NULL)
            *valpint = eint;
    } else {
        double essd;
        essd = (double) essi.ess_int;
        if (essp->factor != 1)
            essd *= essp->factor;
        essd += essp->offset;
        if (valpfloat != NULL) {
            double fv = round(1000.0 * essd);
            *valpfloat = fv / 1000.0;
        }
    }
}

static void ess_extract_value1(
    const struct can_frame * pframe,
    const struct ess_parser * essp,
    int * intval, double * fval, int flags)
{
    uint16_t endbyte, bleft;
    uint16_t startbyte, sbyte, tbyte, idx;
    union ess_integer essi;

    bleft = essp->length;
    sbyte = essp->startbit & 0x7;
    endbyte = essp->startbit + bleft;
    if (bleft == 0 || endbyte > 64) {
        fprintf(stderr, "Error, invalid startbit and/or bit length for '%s': %u, %u\n",
            essp->essname, (unsigned int) essp->startbit, (unsigned int) bleft);
        fflush(stderr);
        return;
    }

    tbyte = sbyte;
    essi.ess_uint = 0;
    startbyte = essp->startbit >> 0x3;
    endbyte = (endbyte - 0x1) >> 0x3;

    for (idx = startbyte; idx <= endbyte; ++idx) {
        uint16_t bits;
        unsigned char cha;
        unsigned int cval;

        cha = pframe->data[idx];
        if (sbyte > 0)
            cha >>= sbyte;
        bits = 0x8 - sbyte;

        if (bleft < bits) {
            if ((sbyte + bleft) < 0x8)
                cha &= (0x1 << bleft) - 0x1;
            cval = (unsigned int) cha;
            if (flags & ESS_FLAG_EB)
                essi.ess_uint = (essi.ess_uint << bleft) | cval;
            else {
                unsigned int sbit = (0x8 * ((idx - startbyte) & 0x3));
                if (sbit > 0)
                    sbit -= tbyte;
                essi.ess_uint |= cval << sbit;
            }
            break;
        }

        cval = (unsigned int) cha;
        if (flags & ESS_FLAG_EB)
            essi.ess_uint = (essi.ess_uint << bits) | cval;
        else {
            unsigned int sbit = (0x8 * ((idx - startbyte) & 0x3));
            if (sbit > 0)
                sbit -= tbyte;
            essi.ess_uint |= cval << sbit;
        }
        sbyte = 0;
        bleft -= bits;
        if (bleft == 0)
            break;
    }
    ess_factor_value1(essi, essp, intval, fval);
}

static const struct ess_parser S2A_ALARM_parse[] = {
    /* 0 ~ 4 */
    { "S2A_AL_Over_Ucell",               0,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_Under_Ucell",              2,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_Over_Tcell",               4,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_Under_Tcell",              6,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_Over_U_HVS",               8,   2,    1,    0,        TAG_TYPE_INT32 },

    /* 5 ~ 9 */
    { "S2A_AL_Under_U_HVS",             10,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_Over_T_HVS",              12,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_Under_T_HVS",             14,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_Over_CC",                 16,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_Over_DC",                 18,   2,    1,    0,        TAG_TYPE_INT32 },

    /* 10 ~ 14 */
    { "S2A_AL_Diff_Tcell",              20,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_PreChFailThree",          22,   2,    1,    0,        TAG_TYPE_INT32, "TAG_9D9C" },
    { "S2A_AL_Under_SOC",               24,   2,    1,    0,        TAG_TYPE_INT32 },
    { "S2A_AL_HSD_Coil",                26,   2,    1,    0,        TAG_TYPE_INT32, "TAG_9D9D" },
    { "S2A_AL_Diff_Ucell",              28,   2,    1,    0,        TAG_TYPE_INT32 },

    /* 10 ~ 14 */
    { "S2A_AL_Err_Bal_Circuit",         30,   2,    1,    0,        TAG_TYPE_INT32, "TAG_9D9E" },
    { "S2A_AL_Fuse_Fault",              32,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9D9F" },
    { "S2A_AL_Relay_OverLoad",          33,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA0" },
    { "S2A_AL_Pre_Relay",               34,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA1" },
    { "S2A_AL_Main_Relay_Pos",          35,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA2" },

    /* 15 ~ 19 */
    { "S2A_AL_Main_Relay_Neg",          36,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA3" },
    { "S2A_AL_Relay_Stick_Pos",         37,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA4" },
    { "S2A_AL_Relay_Stick_Neg",         38,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA5" },
    { "S2A_AL_Over_Cur",                39,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA6" },
    { "S2A_AL_Err_Mea_Ucell",           40,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA7" },

    /* 20 ~ 24 */
    { "S2A_AL_Err_Mea_Tcell",           41,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA8" },
    { "S2A_AL_Diff_Tvol",               42,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DA9" },
    { "S2A_AL_Err_Mea_I_HVS",           43,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DAA" },
    { "S2A_AL_MCANComFault",            44,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DAB" },
    { "S2A_AL_Err_Mea_T_Env",           45,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DAC" },

    /* 25 ~ 29 */
    { "S2A_AL_Err_ToFuse",              46,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DAD" },
    { "S2A_AL_Err_IsoSwitch",           47,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DAE" },
    { "S2A_AL_Err_MCU",                 48,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DAF" },
    { "S2A_AL_Extre_Tvol",              49,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB0" },
    { "S2A_AL_Err_CCAN",                50,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB1" },

    /* 30 ~ 34 */
    { "S2A_AL_Err_SCAN",                51,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB2" },
    { "S2A_AL_Err_Mea_AllTcell",        52,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB3" },
    { "S2A_AL_Extre_T",                 53,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB4" },
    { "S2A_AL_Extre_Ucell",             54,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB5" },
    { "S2A_AL_PowerSample_LEM",         55,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB6" },

    /* 35 ~ 39 */
    { "S2A_AL_PowerSample_CSC",         56,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB7" },
    { "S2A_AL_SBMU_PowerDown",          57,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB8" },
    { "S2A_AL_Err_Fog",                 58,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DB9" },
    { "S2A_AL_Err_HvCrcuitBreak",       59,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DBA" },
    { "S2A_AL_Err_HvMsdFault",          60,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DBB" },

    /* 40 ~ 44 */
    { "S2A_AL_Err_Fan1Fault",           61,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DBC" },
    { "S2A_AL_Err_Fan2Fault",           62,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DBD" },
    { "S2A_AL_Err_Relay_STB",           63,   1,    1,    0,        TAG_TYPE_INT32, "TAG_9DBE" },
    { NULL,                              0,   0,    0,    0,        0 },
};

int ess_parse_S2A_ALARM(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_ALARM_parse);
}

int ess_parse_hardwarefault(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, &S2A_ALARM_parse[11]);
}

int ess_parse_gaojingxinxi(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    double dummy;
    cJSON * retval;
    int idx, ret;
    const char * alarm;
    uint64_t upmsec = 0;
    int S2A_AL_Over_U_HVS = 0;
    int S2A_AL_Under_U_HVS = 0;
    int S2A_AL_Over_Ucell = 0;
    int S2A_AL_Under_Ucell = 0;
    int S2A_AL_Over_DC = 0;
    int S2A_AL_Over_CC = 0;

    int S2A_AL_Under_Tcell = 0;
    int S2A_AL_Over_Tcell = 0;
    int S2A_AL_Diff_Tcell = 0;
    int S2A_AL_Diff_Ucell = 0;
    int S2A_AL_Under_SOC = 0;

    idx = -1;
    dummy = 0;
    alarm = "S2A_ALARM";
    ret = hash_intptr_findint(varp->canhash, alarm, 9, &idx);
    if (ret < 0 || idx < 0 || idx >= (int) varp->num_can) {
        fprintf(stderr, "Error, failed to find ALARM CAN data: %d, %d\n",
            ret, idx);
        fflush(stderr);
        return -1;
    }
    ci = &(varp->caninfp[idx]);
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;

    retval = cJSON_CreateObject();
    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[4], &S2A_AL_Over_U_HVS, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C50", S2A_AL_Over_U_HVS == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C5D", S2A_AL_Over_U_HVS == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C18D", S2A_AL_Over_U_HVS == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[5], &S2A_AL_Under_U_HVS, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C51", S2A_AL_Under_U_HVS == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C6D", S2A_AL_Under_U_HVS == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C19D", S2A_AL_Under_U_HVS == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[0], &S2A_AL_Over_Ucell, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C52", S2A_AL_Over_Ucell == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C7D", S2A_AL_Over_Ucell == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C20D", S2A_AL_Over_Ucell == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[1], &S2A_AL_Under_Ucell, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C53", S2A_AL_Under_Ucell == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C8D", S2A_AL_Under_Ucell == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C21D", S2A_AL_Under_Ucell == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[9], &S2A_AL_Over_DC, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C54", S2A_AL_Over_DC == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C9D", S2A_AL_Over_DC == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C22D", S2A_AL_Over_DC == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[8], &S2A_AL_Over_CC, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C55", S2A_AL_Over_CC == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C10D", S2A_AL_Over_CC == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C23D", S2A_AL_Over_CC == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[3], &S2A_AL_Under_Tcell, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C31D", S2A_AL_Under_Tcell == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C32D", S2A_AL_Under_Tcell == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C33D", S2A_AL_Under_Tcell == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[2], &S2A_AL_Over_Tcell, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C34D", S2A_AL_Over_Tcell == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C35D", S2A_AL_Over_Tcell == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C36D", S2A_AL_Over_Tcell == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[10], &S2A_AL_Diff_Tcell, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C37D", S2A_AL_Diff_Tcell == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C38D", S2A_AL_Diff_Tcell == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C39D", S2A_AL_Diff_Tcell == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[14], &S2A_AL_Diff_Ucell, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C40D", S2A_AL_Diff_Ucell == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C41D", S2A_AL_Diff_Ucell == 2);
    cJSON_AddNumberToObject(retval, "TAG_9C42D", S2A_AL_Diff_Ucell == 3);

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[12], &S2A_AL_Under_SOC, &dummy, 0);
    cJSON_AddNumberToObject(retval, "TAG_9C43D", S2A_AL_Under_SOC == 1);
    cJSON_AddNumberToObject(retval, "TAG_9C44D", S2A_AL_Under_SOC == 2);

    /*
     * TODO: clear existing `gaojingxinxi to avoid repeated data report
     * Or, if it automatically clears itself, no need to do this.
     * 需要了解BMS并讨论后决定.
     */

    *pretval = (void *) retval;
    return 0;
}

static const struct ess_parser S2A_SUMDATA1_parse[] = {
    { "CRC_S2A_S1",                      0,   8,    1,    0,        TAG_TYPE_INT32 },
    { "BAT_U_HVS",                       8,  16,    0.1,  0,        TAG_TYPE_FLOAT, "TAG_9C80" },
    { "BAT_U_TOT_HVS",                  24,  16,    0.1,  0,        TAG_TYPE_FLOAT },
    { "BAT_I_HVS",                      40,  16,    0.1,  -2000,    TAG_TYPE_FLOAT, "TAG_9C81" },
    { NULL,                              0,  0,     0,    0,        0 },
};

/* note: duplicate of `S2A_SUMDATA1_parse */
static const struct ess_parser S2A_SUMDATA1_cluster_parse[] = {
    { "CRC_S2A_S1",                      0,   8,    1,    0,        TAG_TYPE_INT32 },
    { "BAT_U_HVS",                       8,  16,    0.1,  0,        TAG_TYPE_FLOAT, "TAG_75E5" },
    { "BAT_U_TOT_HVS",                  24,  16,    0.1,  0,        TAG_TYPE_FLOAT },
    { "BAT_I_HVS",                      40,  16,    0.1,  -2000,    TAG_TYPE_FLOAT, "TAG_75E6" },
    { NULL,                              0,  0,     0,    0,        0 },
};

int ess_parse_S2A_SUMDATA1(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_SUMDATA1_parse);
}

static const struct ess_parser S2A_SUMDATA2_parse[] = {
    /* 0 ~ 4 */
    { "CRC_S2A_S2",                      0,   8,    1,     0,         TAG_TYPE_INT32 },
    { "ALIV_ST_SW_HVS",                  8,   4,    1,     0,         TAG_TYPE_INT32 },
    { "ST_PreCharge_SW_HVS",            12,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D88" },
    { "ST_Main_POS_SW_HVS",             13,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D89" },
    { "ST_Main_NEG_SW_HVS",             14,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D8A" },

    /* 5 ~ 9 */
    { "ST_Fan_SW_HVS",                  15,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D8B" },
    { "FireFault_Level2",               16,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D8C" },
    { "ST_System",                      17,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D8D" },
    { "ST_CALIBRATION",                 18,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D8E" },
    { "ST_Insulation",                  19,   2,    1,     0,         TAG_TYPE_INT32, "TAG_9D8F" },

    /* 10 ~ 14 */
    { "ST_PowerDown",                   21,   3,    1,     0,         TAG_TYPE_INT32, "TAG_9D90" },
    { "ST_pwr12V",                      24,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D91" },
    { "ST_pwr5V",                       25,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D92" },
    { "ST_pwr24V",                      26,   1,    1,     0,         TAG_TYPE_INT32, "TAG_9D93" },
    { "ST_pwrStatus",                   27,   3,    1,     0,         TAG_TYPE_INT32, "TAG_9D94" },

    /* 15 ~ 19 */
    { "ST_cscSupplyCur",                30,  16,    1,     0,         TAG_TYPE_INT32, "TAG_9D95" },
    { "ST_cscStandby",                  46,   2,    1,     0,         TAG_TYPE_INT32, "TAG_9D96" },
    { "ST_OffReasonMsg",                48,  16,    1,     0,         TAG_TYPE_INT32, "TAG_9D97" },
    { NULL,                              0,   0,    0,     0,         0 },
};

int ess_parse_S2A_SUMDATA2(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_SUMDATA2_parse);
}

static const struct ess_parser S2A_SUMDATA3_parse[] = {
    { "CRC_S2A_S3",                      0,   8,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Max_CSC",               8,   6,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Max_CELL",             14,   6,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Min_CSC",              20,   6,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Min_CELL",             26,   6,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Max",                  32,  16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_9C8B" },
    { "BAT_Ucell_Min",                  48,  16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_9C8D" },
    { NULL,                              0,   0,    0,     0,       0 },
};

static const struct ess_parser S2A_SUMDATA3_cluster_parse[] = {
    { "CRC_S2A_S3",                      0,   8,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Max_CSC",               8,   6,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Max_CELL",             14,   6,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Min_CSC",              20,   6,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Min_CELL",             26,   6,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_Ucell_Max",                  32,  16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_75F3" },
    { "BAT_Ucell_Min",                  48,  16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_75F7" },
    { NULL,                              0,   0,    0,     0,       0 },
};

int ess_parse_S2A_SUMDATA3(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_SUMDATA3_parse);
}

static const struct ess_parser S2A_SUMDATA4_parse[] = {
    /* 0 ~ 4 */
    { "CRC_S2A_S4",                     0,    8,    1,     0,       TAG_TYPE_INT32 },
    //{ "BAT_Ucell_Avg",                  8,   16,    1,     0,       TAG_TYPE_INT32, "TAG_9C92" },
    { "BAT_Ucell_Avg",                  8,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_9C92" },
    { "BAT_T_Max",                     24,    8,    1,     -50.0,     TAG_TYPE_FLOAT, "TAG_9C8F" },
    { "BAT_T_Min",                     32,    8,    1,     -50.0,     TAG_TYPE_FLOAT, "TAG_9C91" },
    { "BAT_T_Avg",                     40,    8,    1,     -50.0,     TAG_TYPE_FLOAT, "TAG_9C93" },

    /* 5 ~ 9 */
    { "BAT_T_Max_CSC",                 48,    8,    1,     0,       TAG_TYPE_INT32, "TAG_9C8E" },
    { "BAT_T_Min_CSC",                 56,    8,    1,     0,       TAG_TYPE_INT32, "TAG_9C90" },
    { NULL,                              0,   0,    0,     0,         0 },
};

static const struct ess_parser S2A_SUMDATA4_cluster_parse[] = {
    /* 0 ~ 4 */
    { "CRC_S2A_S4",                     0,    8,    1,     0,       TAG_TYPE_INT32 },
    //{ "BAT_Ucell_Avg",                  8,   16,    1,     0,       TAG_TYPE_INT32, "TAG_9C92" },
    { "BAT_Ucell_Avg",                  8,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_75F8" },
    { "BAT_T_Max",                     24,    8,    1,     -50.0,     TAG_TYPE_FLOAT, "TAG_75FC" },
    { "BAT_T_Min",                     32,    8,    1,     -50.0,     TAG_TYPE_FLOAT, "TAG_7600" },
    { "BAT_T_Avg",                     40,    8,    1,     -50.0,     TAG_TYPE_FLOAT, "TAG_7601" },

    /* 5 ~ 9 */
    { "BAT_T_Max_CSC",                 48,    8,    1,     0,       TAG_TYPE_INT32, "TAG_9C8E" },
    { "BAT_T_Min_CSC",                 56,    8,    1,     0,       TAG_TYPE_INT32, "TAG_9C90" },
    { NULL,                              0,   0,    0,     0,         0 },
};

int ess_parse_S2A_SUMDATA4(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_SUMDATA4_parse);
}

static const struct ess_parser S2A_SUMDATA5_parse[] = {
    /* 0 ~ 4 */
    { "CRC_S2A_S5",                     0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_SOH_HVS",                    8,   16,    0.1,   0,       TAG_TYPE_FLOAT, "TAG_9C84" },
    { "BAT_SOC_HVS",                   24,   16,    0.1,   0,       TAG_TYPE_FLOAT },
    { "BAT_User_SOC_HVS",              40,   16,    0.1,   0,       TAG_TYPE_FLOAT, "TAG_9C83" },
    { "BAT_SOC_CorrectState",          56,    3,   	1,     0,       TAG_TYPE_INT32 },

    /* 5 ~ 9 */
    { "BAT_Cool_CellBalStat",          59,    1,    1,     0,       TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,     0,       0 },
};

static const struct ess_parser S2A_SUMDATA5_cluster_parse[] = {
    /* 0 ~ 4 */
    { "CRC_S2A_S5",                     0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "BAT_SOH_HVS",                    8,   16,    0.1,   0,       TAG_TYPE_FLOAT, "TAG_75E8" },
    { "BAT_SOC_HVS",                   24,   16,    0.1,   0,       TAG_TYPE_FLOAT },
    { "BAT_User_SOC_HVS",              40,   16,    0.1,   0,       TAG_TYPE_FLOAT, "TAG_75E7" },
    { "BAT_SOC_CorrectState",          56,    3,   	1,     0,       TAG_TYPE_INT32 },

    /* 5 ~ 9 */
    { "BAT_Cool_CellBalStat",          59,    1,    1,     0,       TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2A_SUMDATA5(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_SUMDATA5_parse);
}

static const struct ess_parser S2A_SUMDATA6_parse[] = {
    /* 0 ~ 4 */
    { "CRC_S2A_S6",                     0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "SBMU_Evn_T1",                    8,    8,    1,     -50,     TAG_TYPE_INT32, "TAG_9D98" },
    { "SBMU_Evn_T2",                   16,    8,    1,     -50,     TAG_TYPE_INT32, "TAG_9D99" },
    { "SBMU_Evn_T3",                   24,    8,    1,     -50,     TAG_TYPE_INT32, "TAG_9D9A" },
    { "BAT_Lmt_ChCurrent",             32,   16,  0.1,     -2000,   TAG_TYPE_FLOAT, "TAG_9D77" },

    /* 5 ~ 9 */
    { "BAT_Lmt_DisChCurrent",          48,   16,  0.1,     -2000,   TAG_TYPE_FLOAT, "TAG_9D78" },
    { NULL,                             0,    0,    0,     0,       0 },
};

static const struct ess_parser S2A_SUMDATA6_cluster_parse[] = {
	/* 0 ~ 4 */
	{ "CRC_S2A_S6", 					0,	  8,	1,	   0,		TAG_TYPE_INT32 },
	{ "SBMU_Evn_T1",					8,	  8,	1,	   -50, 	TAG_TYPE_INT32, "TAG_9D98" },
	{ "SBMU_Evn_T2",				   16,	  8,	1,	   -50, 	TAG_TYPE_INT32, "TAG_9D99" },
	{ "SBMU_Evn_T3",				   24,	  8,	1,	   -50, 	TAG_TYPE_INT32, "TAG_9D9A" },
	{ "BAT_Lmt_ChCurrent",			   32,	 16,  0.1,	   -2000,	TAG_TYPE_FLOAT, "TAG_75EC" },

	/* 5 ~ 9 */
	{ "BAT_Lmt_DisChCurrent",		   48,	 16,  0.1,	   -2000,	TAG_TYPE_FLOAT, "TAG_75ED" },
	{ NULL, 							0,	  0,	0,	   0,		0 },
};


int ess_parse_S2A_SUMDATA6(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_SUMDATA6_parse);
}

int ess_parse_zhuangtai(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    double dummy;
    int idx, ret, dummyi;
    cJSON * retval;
    int TAG_9C52 = 0;
    int TAG_9C53 = 0;
    int TAG_9C40 = 0;

    double TAG_9C83 = 0;
    double TAG_9D77 = 0;
    double TAG_9D78 = 0;
    uint64_t upmsec = 0;

    dummyi = 0;
    dummy = 0;
    retval = NULL;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;

    ret = ess_parse_common(ci,
        varp, (void * *)&retval, flags, &S2A_SUMDATA2_parse[2]);
    if (retval == NULL)
        return ret;

    idx = -1;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA6", 12, &idx);
    if (idx < 0 && idx >= (int) varp->num_can) {
        *pretval = (void *) retval;
        return ret;
    }
    ci = &(varp->caninfp[idx]);
    ess_extract_value(retval, &ci->canfrm, &S2A_SUMDATA6_parse[1], 0);
    ess_extract_value(retval, &ci->canfrm, &S2A_SUMDATA6_parse[2], 0);
    ess_extract_value(retval, &ci->canfrm, &S2A_SUMDATA6_parse[3], 0);
    ess_extract_value1(&ci->canfrm, &S2A_SUMDATA6_parse[4], &dummyi, &TAG_9D77, 0);
    ess_extract_value1(&ci->canfrm, &S2A_SUMDATA6_parse[5], &dummyi, &TAG_9D78, 0);

    idx = -1;
    ret = hash_intptr_findint(varp->canhash, "S2A_ALARM", 9, &idx);
    if (ret < 0 || idx < 0 || idx >= (int) varp->num_can) {
        fprintf(stderr, "Error, failed to find ALARM CAN data: %d, %d\n",
            ret, idx);
        fflush(stderr);
    } else {
        ci = &(varp->caninfp[idx]);
        ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[0], &TAG_9C52 /* S2A_AL_Over_Ucell */, &dummy, 0);
        TAG_9C52 = TAG_9C52 == 1;

        ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[1], &TAG_9C53 /* S2A_AL_Under_Ucell */, &dummy, 0);
        TAG_9C53 = TAG_9C53 == 1;
    }

    idx = -1;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA5", 12, &idx);
    if (idx < 0) {
        fputs("Error, failed to find S2A_SUMDATA5 caninfo\n", stderr);
        fflush(stderr);
    } else {
        ci = &(varp->caninfp[idx]);
        ess_extract_value1(&ci->canfrm, &S2A_SUMDATA5_parse[3], &dummyi, &TAG_9C83, 0);
    }

    /*
     * 一、21881EFF002B_Rack1_1里面
     * 单体过压一级告警TAG_9C52=1；单体欠压一级告警TAG_9C53=1；这两个之前未解析，放在gaojingxinxi服务里
     * 1、（单体过压一级告警||SOC（已解析TAG_9C83)=100%）&&（当前充电限制电流(已解析TAG_9D77)==0)则禁充TAG_9C40=1
     * 2、（单体欠压一级告警||SOC（已解析TAG_9C83)=0%）&&（当前放电限制电流(已解析TAG_9D78)==0）则禁放TAG_9C40=2
     * 不满足上面条件则：TAG_9C40=0
     */
    if ((TAG_9C52 || float_near(TAG_9C83, 100, 0.001)) && float_near(TAG_9D77, 0, 0.001))
        TAG_9C40 = 1;
    else if ((TAG_9C53 || float_near(TAG_9C83, 0, 0.001)) && float_near(TAG_9D78, 0, 0.001))
        TAG_9C40 = 2;
    cJSON_AddNumberToObject(retval, "TAG_9C40", TAG_9C40);

    *pretval = (void *) retval;
    return ret;
}

int ess_parse_Read1001_100B(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    double dummy;
    cJSON * retval;
    int idx, ret, dummyi;
    const char * alarm;
    int TAG_760C = 0;
    int TAG_760D = 0;
    int TAG_75E3 = 0;

    double TAG_75EC = 0;
    double TAG_75E7 = 0;
    double TAG_75ED = 0;

    int S2A_AL_Over_Ucell = 0;
    int S2A_AL_Under_Ucell = 0;
    uint64_t upmsec = 0;

    idx = -1;
    dummy = 0;
    dummyi = 0;
    retval = NULL;
    alarm = "S2A_ALARM";
    ret = hash_intptr_findint(varp->canhash, alarm, 9, &idx);
    if (ret < 0 || idx < 0 || idx >= (int) varp->num_can) {
        fprintf(stderr, "Error, failed to find ALARM CAN data: %d, %d\n",
            ret, idx);
        fflush(stderr);
        return -1;
    }
    ci = &(varp->caninfp[idx]);
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;

    retval = cJSON_CreateObject();
    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[0], &S2A_AL_Over_Ucell, &dummy, 0);
    TAG_760C = S2A_AL_Over_Ucell == 1;
    cJSON_AddNumberToObject(retval, "TAG_760C", TAG_760C); /* TAG_9C52 */

    ess_extract_value1(&ci->canfrm, &S2A_ALARM_parse[1], &S2A_AL_Under_Ucell, &dummy, 0);
    TAG_760D = S2A_AL_Under_Ucell == 1;
    cJSON_AddNumberToObject(retval, "TAG_760D", TAG_760D); /* TAG_9C53 */

    idx = -1;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA5", 12, &idx);
    if (idx < 0) {
        fputs("Error, failed to find SUMDATA5!\n", stderr);
        fflush(stderr);
    } else {
        ci = &(varp->caninfp[idx]);
        ess_extract_value1(&ci->canfrm, &S2A_SUMDATA5_cluster_parse[4], &dummyi, &TAG_75E7, 0);
    }

    idx = -1;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA6", 12, &idx);
    if (idx < 0) {
        fputs("Error, failed to find SUMDATA6!\n", stderr);
        fflush(stderr);
    } else {
        ci = &(varp->caninfp[idx]);
        ess_extract_value1(&ci->canfrm, &S2A_SUMDATA6_cluster_parse[4], &dummyi, &TAG_75EC, 0);
        ess_extract_value1(&ci->canfrm, &S2A_SUMDATA6_cluster_parse[5], &dummyi, &TAG_75ED, 0);
    }

    idx = -1;
	hash_intptr_findint(varp->canhash, "S2A_SUMDATA1", 12, &idx);
	if (idx >= 0 && idx < (int) varp->num_can) {
        int TAG_75E4 = 0;
        double TAG_75E6 = 0;
		ci = &(varp->caninfp[idx]);
		ess_extract_value1(&ci->canfrm, &S2A_SUMDATA1_cluster_parse[3], &dummyi, &TAG_75E6, 0);
        if (TAG_75E6 > 0.00001)
            TAG_75E4 = 1;
        else if (TAG_75E6 < -0.00001)
            TAG_75E4 = 2;
        cJSON_AddNumberToObject(retval, "TAG_75E4", TAG_75E4);
	}

    /*
     * 二、21881EFF002B_Cluster_1里面
     * 单体过压一级告警TAG_760C=1；单体欠压一级告警TAG_760D=1；这两个之前未解析，放在Read1001_100B服务里
     * 1、（（单体过压一级告警||SOC（已解析TAG_75E7)=100%）&&（当前充电限制电流(已解析TAG_75EC)==0)）则禁充TAG_75E3=1
     * 2、（（单体欠压一级告警||SOC（已解析TAG_75E7)=0%）&&（当前放电限制电流(已解析TAG_75ED)==0））则禁放TAG_75E3=2
     * 不满足上面条件则：TAG_75E3=0
     */
    if ((TAG_760C || float_near(TAG_75E7, 100, 0.001)) && float_near(TAG_75EC, 0, 0.001))
        TAG_75E3 = 1;
    else if ((TAG_760D || float_near(TAG_75E7, 0, 0.001)) && float_near(TAG_75ED, 0, 0.001))
        TAG_75E3 = 2;

    cJSON_AddNumberToObject(retval, "TAG_75E3", TAG_75E3);
    *pretval = (void *) retval;
    return 0;
}

static const struct ess_parser S2A_SUMDATA8_parse[] = {
    { "S2A_HIS_Charge_Engery",          0,   32,  0.1,     0,       TAG_TYPE_FLOAT, "TAG_7673" },
    { "S2A_HIS_Discharge_Engery",      32,   32,  0.1,     0,       TAG_TYPE_FLOAT, "TAG_7674" },
    { NULL,                             0,    0,    0,     0,       0 },
};

static const struct ess_parser S2A_SUMDATA8_charge_discharge_parse[] = {
    { "S2A_HIS_Charge_Engery",          0,   32,  0.1,     0,       TAG_TYPE_FLOAT, "TAG_9D35" },
    { "S2A_HIS_Discharge_Engery",      32,   32,  0.1,     0,       TAG_TYPE_FLOAT, "TAG_9D36" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_Cluster_charge_discharge(struct ess_caninfo * ci,
	struct ess_info_var * varp, void * * pretval, int flags)
{
    int idx = -1;
    uint64_t upmsec = 0;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA8", 12, &idx);
    if (idx < 0)
        return -1;

    ci = &varp->caninfp[idx];
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_SUMDATA8_charge_discharge_parse);
}

int ess_parse_rackxinxi(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    int idx;
    cJSON * retval;
    int TAG_9C82 = 0;
    double TAG_9C81 = 0;
    struct ess_caninfo * caninfo;
    uint64_t upmsec = 0;

    uptime_msec(&upmsec);
    /* check the last update time for S2A_SUMDATA4 */
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;

    retval = cJSON_CreateObject();
    idx = -1;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA1", 12, &idx);
    if (idx >= 0 && idx < (int) varp->num_can) {
        int dummyi = 0;
        caninfo = &(varp->caninfp[idx]);
        ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA1_parse[1], 0);
#if 0
        ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA1_parse[3], 0);
#else
        ess_extract_value1(&caninfo->canfrm, &S2A_SUMDATA1_parse[3], &dummyi, &TAG_9C81, 0);
        if (TAG_9C81 > 0.00001)
            TAG_9C82 = 1;
        else if (TAG_9C81 < -0.00001)
            TAG_9C82 = 2;
        cJSON_AddNumberToObject(retval, "TAG_9C81", TAG_9C81);
        cJSON_AddNumberToObject(retval, "TAG_9C82", TAG_9C82);
#endif
    }

    idx = -1;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA5", 12, &idx);
    if (idx >= 0 && idx < (int) varp->num_can) {
        caninfo = &(varp->caninfp[idx]);
        ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA5_parse[1], 0);
        ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA5_parse[3], 0);
    }

    idx = -1;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA3", 12, &idx);
    if (idx >= 0 && idx < (int) varp->num_can) {
        caninfo = &(varp->caninfp[idx]);
        int BAT_Ucell_Max_CSC = 0;
        int BAT_Ucell_Max_CELL = 0;
        int BAT_Ucell_Min_CSC = 0;
        int BAT_Ucell_Min_CELL = 0;

#if 1
        __u8 tmpval[3];
        tmpval[0] = caninfo->canfrm.data[1];
        tmpval[1] = caninfo->canfrm.data[2];
        tmpval[2] = caninfo->canfrm.data[3];

        BAT_Ucell_Max_CSC = (int) (tmpval[0] & 0x3f);

        BAT_Ucell_Max_CELL = (int) (tmpval[1] & 0x0f);
        BAT_Ucell_Max_CELL = (BAT_Ucell_Max_CELL << 0x2) | ((tmpval[0] >> 6) & 0x3);

        BAT_Ucell_Min_CSC = (int) ((tmpval[1] >> 0x4) & 0x0f);
        BAT_Ucell_Min_CSC |= (int) ((tmpval[2] << 0x4) & 0x30);

        BAT_Ucell_Min_CELL = (int) ((tmpval[2] >> 0x2) & 0x3f);
#else
        double dummy = 0;
        ess_extract_value1(&ci->canfrm,
            &S2A_SUMDATA3_parse[1], &BAT_Ucell_Max_CSC, &dummy, 0);
        ess_extract_value1(&ci->canfrm,
            &S2A_SUMDATA3_parse[2], &BAT_Ucell_Max_CELL, &dummy, 0);
        ess_extract_value1(&ci->canfrm,
            &S2A_SUMDATA3_parse[3], &BAT_Ucell_Min_CSC, &dummy, 0);
        ess_extract_value1(&ci->canfrm,
            &S2A_SUMDATA3_parse[4], &BAT_Ucell_Min_CELL, &dummy, 0);
#endif

        BAT_Ucell_Max_CSC -= 1;
        BAT_Ucell_Min_CSC -= 1;

        BAT_Ucell_Max_CSC = BAT_Ucell_Max_CSC * 52 + BAT_Ucell_Max_CELL;
        cJSON_AddNumberToObject(retval, "TAG_9C8A", BAT_Ucell_Max_CSC);

        BAT_Ucell_Min_CSC = BAT_Ucell_Min_CSC * 52 + BAT_Ucell_Min_CELL;
        cJSON_AddNumberToObject(retval, "TAG_9C8C", BAT_Ucell_Min_CSC);
		
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA3_parse[5], 0);
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA3_parse[6], 0);
    }

    /* note that `ci comes from S2A_SUMDATA4, no need to find from hashtable */
    ess_extract_value(retval, &ci->canfrm, &S2A_SUMDATA4_parse[1], 0);
    ess_extract_value(retval, &ci->canfrm, &S2A_SUMDATA4_parse[2], 0);
    ess_extract_value(retval, &ci->canfrm, &S2A_SUMDATA4_parse[3], 0);
    ess_extract_value(retval, &ci->canfrm, &S2A_SUMDATA4_parse[4], 0);
    ess_extract_value(retval, &ci->canfrm, &S2A_SUMDATA4_parse[5], 0);
    ess_extract_value(retval, &ci->canfrm, &S2A_SUMDATA4_parse[6], 0);

    idx = -1;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA6", 12, &idx);
    if (idx >= 0 && idx < (int) varp->num_can) {
        caninfo = &(varp->caninfp[idx]);
        ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA6_parse[4], 0);
        ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA6_parse[5], 0);
    }

    idx = -1;
    hash_intptr_findint(varp->canhash, "S2A_SUMDATA8", 12, &idx);
    if (idx >= 0 && idx < (int) varp->num_can) {
        caninfo = &(varp->caninfp[idx]);
        ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA8_parse[0], 0);
        ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA8_parse[1], 0);
    }

    *pretval = (void *) retval;
    return 0;
}


	
int ess_parse_S2A_SUMDATA8(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_SUMDATA8_parse);
}

static const struct ess_parser S2A_LiquidInfo_parse[] = {
    { "B2T_ControlMode",                0,    2,    1,     0,       TAG_TYPE_INT32 },
    { "TMS_RunState",                   2,    2,    1,     0,       TAG_TYPE_INT32 },
    { "TMS_CommuFault",                 4,    1,    1,     0,       TAG_TYPE_INT32 },
    { "TMS_ModeConflict",               5,    1,    1,     0,       TAG_TYPE_INT32 },
    { "TMS_MakeBMSStop",                6,    1,    1,     0,       TAG_TYPE_INT32 },
    { "TMS_HVRealyState",               7,    1,    1,     0,       TAG_TYPE_INT32 },
    { "TMS_FaultCode",                  8,    6,    1,     0,       TAG_TYPE_INT32 },
    { "TMS_FaultLevel",                14,    2,    1,     0,       TAG_TYPE_INT32 },
    { "B2T_TmptSetPoint",              16,    8,    1,     -40,     TAG_TYPE_INT32 },
    { "TMS_OutWaterTemp",              24,    8,    1,     -40,     TAG_TYPE_INT32 },
    { "TMS_BackWaterTemp",             32,    8,    1,     -40,     TAG_TYPE_INT32 },
    { "TMS_EnvTemp",                   40,    8,    1,     -40,     TAG_TYPE_INT32 },
    { "TMS_RequestPower",              48,    8,  0.1,     0,       TAG_TYPE_FLOAT },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2A_LiquidInfo(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_LiquidInfo_parse);
}

static const struct ess_parser S2A_DryContact_parse[] = {
    { "FireFault_Level1",               0,    1,    1,     0,       TAG_TYPE_INT32 },
    { "FireFault_Level2",               1,    1,    1,     0,       TAG_TYPE_INT32 },
    { "TempState",                      2,    1,    1,     0,       TAG_TYPE_INT32 },
    { "FogState",                       3,    1,    1,     0,       TAG_TYPE_INT32 },
    { "AerosolState",                   4,    1,    1,     0,       TAG_TYPE_INT32 },
    { "AerosolFaultState",              5,    2,    1,     0,       TAG_TYPE_INT32 },
    { "DoorState",                      7,    1,    1,     0,       TAG_TYPE_INT32 },
    { "ControlBoxFanState",             8,    1,    1,     0,       TAG_TYPE_INT32 },
    { "ControlBoxTempHighWarning",      9,    1,    1,     0,       TAG_TYPE_INT32 },
    { "PowerSWState",                   10,   1,    1,     0,       TAG_TYPE_INT32 },
    { "FaultClearCommand",              11,   1,    1,     0,       TAG_TYPE_INT32 },
    { "TMS_OutWaterPress",              16,   8,    0.1,   0,       TAG_TYPE_FLOAT },
    { "TMS_BackWaterPress",             24,   8,    0.1,   0,       TAG_TYPE_FLOAT },
    { "ControlBox_Temp",                56,   8,    1,   -40,       TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2A_DryContact(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2A_DryContact_parse);
}

static const struct ess_parser S2P_Detail_Ucell1_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_01",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C350" },
    { "CSC_Ucell_02",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C351" },
    { "CSC_Ucell_03",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C352" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell1(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell1_parse);
}

static const struct ess_parser S2P_Detail_Ucell2_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_04",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C353" },
    { "CSC_Ucell_05",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C354" },
    { "CSC_Ucell_06",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C355" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell2(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell2_parse);
}

static const struct ess_parser S2P_Detail_Ucell3_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_07",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C356" },
    { "CSC_Ucell_08",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C357" },
    { "CSC_Ucell_09",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C358" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell3(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell3_parse);
}

static const struct ess_parser S2P_Detail_Ucell4_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_10",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C359" },
    { "CSC_Ucell_11",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C35A" },
    { "CSC_Ucell_12",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C35B" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell4(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell4_parse);
}

static const struct ess_parser S2P_Detail_Ucell5_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_13",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C35C" },
    { "CSC_Ucell_14",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C35D" },
    { "CSC_Ucell_15",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C35E" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell5(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell5_parse);
}

static const struct ess_parser S2P_Detail_Ucell6_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_16",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C35F" },
    { "CSC_Ucell_17",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C360" },
    { "CSC_Ucell_18",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C361" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell6(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell6_parse);
}

static const struct ess_parser S2P_Detail_Ucell7_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_19",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C362" },
    { "CSC_Ucell_20",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C363" },
    { "CSC_Ucell_21",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C364" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell7(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell7_parse);
}

static const struct ess_parser S2P_Detail_Ucell8_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_22",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C365" },
    { "CSC_Ucell_23",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C366" },
    { "CSC_Ucell_24",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C367" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell8(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell8_parse);
}

static const struct ess_parser S2P_Detail_Ucell9_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_25",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C368" },
    { "CSC_Ucell_26",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C369" },
    { "CSC_Ucell_27",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C36A" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell9(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell9_parse);
}

static const struct ess_parser S2P_Detail_Ucell10_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_28",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C36B" },
    { "CSC_Ucell_29",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C36C" },
    { "CSC_Ucell_30",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C36D" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell10(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell10_parse);
}

static const struct ess_parser S2P_Detail_Ucell11_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_31",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C36E" },
    { "CSC_Ucell_32",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C36F" },
    { "CSC_Ucell_33",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3B4" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell11(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell11_parse);
}

static const struct ess_parser S2P_Detail_Ucell12_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_34",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3B5" },
    { "CSC_Ucell_35",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3B6" },
    { "CSC_Ucell_36",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3B7" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell12(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell12_parse);
}

static const struct ess_parser S2P_Detail_Ucell13_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_37",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3B8" },
    { "CSC_Ucell_38",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3B9" },
    { "CSC_Ucell_39",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3BA" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell13(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell13_parse);
}

static const struct ess_parser S2P_Detail_Ucell14_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_40",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3BB" },
    { "CSC_Ucell_41",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3BC" },
    { "CSC_Ucell_42",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3BD" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell14(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell14_parse);
}

static const struct ess_parser S2P_Detail_Ucell15_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_43",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3BE" },
    { "CSC_Ucell_44",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3BF" },
    { "CSC_Ucell_45",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3C0" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell15(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell15_parse);
}

static const struct ess_parser S2P_Detail_Ucell16_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_46",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3C1" },
    { "CSC_Ucell_47",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3C2" },
    { "CSC_Ucell_48",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3C3" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell16(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell16_parse);
}

static const struct ess_parser S2P_Detail_Ucell17_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_49",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3C4" },
    { "CSC_Ucell_50",                  32,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3C5" },
    { "CSC_Ucell_51",                  48,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3C6" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell17(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell17_parse);
}

static const struct ess_parser S2P_Detail_Ucell18_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_52",                  16,   16,    0.001,     0,       TAG_TYPE_FLOAT, "TAG_C3C7" },
    { "CSC_Ucell_53",                  32,   16,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_54",                  48,   16,    1,     0,       TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell18(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell18_parse);
}

static const struct ess_parser S2P_Detail_Ucell19_parse[] = {
    { "CSC_U_No",                       0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_Frame_Index",          8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_55",                  16,   16,    1,     0,       TAG_TYPE_INT32 },
    { "CSC_Ucell_56",                  32,   16,    1,     0,       TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Ucell19(struct ess_caninfo * ci,
    struct ess_info_var * varp,void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Ucell19_parse);
}

static const struct ess_parser S2P_Detail_Tcell1_parse[] = {
    { "CRC_S2P_Detail_Tcell",          0,     8,   1,     0,        TAG_TYPE_INT32 },
    { "CSC_T_No",                      8,     6,   1,     0,        TAG_TYPE_INT32 },
    { "CSC_Tcell_Frame_Index",        14,     2,   1,     0,        TAG_TYPE_INT32 },
    { "CSC_Tcell_01",                 16,     8,   1,     -50.0,      TAG_TYPE_FLOAT, "TAG_C370" },
    { "CSC_Tcell_02",                 24,     8,   1,     -50.0,      TAG_TYPE_FLOAT, "TAG_C371" },
    { "CSC_Tcell_03",                 32,     8,   1,     -50.0,      TAG_TYPE_FLOAT, "TAG_C372" },
    { "CSC_Tcell_04",                 40,     8,   1,     -50.0,      TAG_TYPE_FLOAT, "TAG_C373" },
    { "CSC_Tcell_05",                 48,     8,   1,     -50.0,      TAG_TYPE_FLOAT, "TAG_C374" },
    { "CSC_Tcell_06",                 56,     8,   1,     -50.0,      TAG_TYPE_FLOAT, "TAG_C375" },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Tcell1(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Tcell1_parse);
}

static const struct ess_parser S2P_Detail_Tcell2_parse[] = {
    { "CRC_S2P_Detail_Tcell",          0,     8,   1,     0,        TAG_TYPE_INT32 },
    { "CSC_T_No",                      8,     6,   1,     0,        TAG_TYPE_INT32 },
    { "CSC_Tcell_Frame_Index",        14,     2,   1,     0,        TAG_TYPE_INT32 },
    { "CSC_Tcell_07",                 16,     8,   1,     -50.0,      TAG_TYPE_FLOAT, "TAG_C376" },
    { "CSC_Tcell_08",                 24,     8,   1,     -50.0,      TAG_TYPE_FLOAT, "TAG_C377" },
    { "CSC_Tcell_09",                 32,     8,   1,     -50,      TAG_TYPE_INT32 },
    { "CSC_Tcell_10",                 40,     8,   1,     -50,      TAG_TYPE_INT32 },
    { "CSC_Tcell_11",                 48,     8,   1,     -50,      TAG_TYPE_INT32 },
    { "CSC_Tcell_12",                 56,     8,   1,     -50,      TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Tcell2(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Tcell2_parse);
}

static const struct ess_parser S2P_Detail_Tcell3_parse[] = {
    { "CRC_S2P_Detail_Tcell",          0,     8,   1,     0,        TAG_TYPE_INT32 },
    { "CSC_T_No",                      8,     6,   1,     0,        TAG_TYPE_INT32 },
    { "CSC_Tcell_Frame_Index",        14,     2,   1,     0,        TAG_TYPE_INT32 },
    { "CSC_Tcell_13",                 16,     8,   1,     -50,      TAG_TYPE_INT32 },
    { "CSC_Tcell_14",                 24,     8,   1,     -50,      TAG_TYPE_INT32 },
    { "CSC_Tcell_15",                 32,     8,   1,     -50,      TAG_TYPE_INT32 },
    { "CSC_Tcell_16",                 40,     8,   1,     -50,      TAG_TYPE_INT32 },
    { "CSC_Tcell_17",                 48,     8,   1,     -50,      TAG_TYPE_INT32 },
    { "CSC_Tcell_18",                 56,     8,   1,     -50,      TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_S2P_Detail_Tcell3(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, S2P_Detail_Tcell3_parse);
}

int ess_parse_ClusterTotal(struct ess_caninfo * ci,
	struct ess_info_var * varp, void * * pretval, int flags)
{
	int idx;
	cJSON * retval;
	struct ess_caninfo * caninfo;
    uint64_t upmsec = 0;

	retval = cJSON_CreateObject();
    uptime_msec(&upmsec);

	idx = -1;
	hash_intptr_findint(varp->canhash, "S2A_SUMDATA1", 12, &idx);
	if (idx >= 0 && idx < (int) varp->num_can) {
		caninfo = &(varp->caninfp[idx]);
        if (upmsec > (caninfo->lastup + ESS_DATA_VALIDTIME)) {
            cJSON_Delete(retval);
            return -1;
        }
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA1_cluster_parse[1], 0);
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA1_cluster_parse[3], 0);
	}

	idx = -1;
	hash_intptr_findint(varp->canhash, "S2A_SUMDATA5", 12, &idx);
	if (idx >= 0 && idx < (int) varp->num_can) {
		caninfo = &(varp->caninfp[idx]);
        if (upmsec > (caninfo->lastup + ESS_DATA_VALIDTIME)) {
            cJSON_Delete(retval);
            return -1;
        }
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA5_cluster_parse[1], 0);
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA5_cluster_parse[3], 0);
	}

	idx = -1;
	hash_intptr_findint(varp->canhash, "S2A_SUMDATA6", 12, &idx);
	if (idx >= 0 && idx < (int) varp->num_can) {
		caninfo = &(varp->caninfp[idx]);
        if (upmsec > (caninfo->lastup + ESS_DATA_VALIDTIME)) {
            cJSON_Delete(retval);
            return -1;
        }
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA6_cluster_parse[4], 0);
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA6_cluster_parse[5], 0);
	}	

	*pretval = (void *) retval;
	return 0;

}

int ess_parse_ClusterSystem(struct ess_caninfo * ci,
	struct ess_info_var * varp, void * * pretval, int flags)
{
	int idx;
	cJSON * retval;
	struct ess_caninfo * caninfo;
    uint64_t upmsec = 0;

	retval = cJSON_CreateObject();
    uptime_msec(&upmsec);

	idx = -1;
	hash_intptr_findint(varp->canhash, "S2A_SUMDATA3", 12, &idx);
	if (idx >= 0 && idx < (int) varp->num_can) {
		caninfo = &(varp->caninfp[idx]);
        if (upmsec > (caninfo->lastup + ESS_DATA_VALIDTIME)) {
            cJSON_Delete(retval);
            return -1;
        }
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA3_cluster_parse[5], 0);
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA3_cluster_parse[6], 0);
	}

	idx = -1;
	hash_intptr_findint(varp->canhash, "S2A_SUMDATA4", 12, &idx);
	if (idx >= 0 && idx < (int) varp->num_can) {
		caninfo = &(varp->caninfp[idx]);
        if (upmsec > (caninfo->lastup + ESS_DATA_VALIDTIME)) {
            cJSON_Delete(retval);
            return -1;
        }
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA4_cluster_parse[1], 0);
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA4_cluster_parse[2], 0);
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA4_cluster_parse[3], 0);
		ess_extract_value(retval, &caninfo->canfrm, &S2A_SUMDATA4_cluster_parse[4], 0);
	}

	*pretval = (void *) retval;
	return 0;

}

static const struct ess_parser M2S_CMD1_parse[] = {
    { "CRC_M2S_CMD1",                   0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "SBMU_No",                        8,    8,    1,     0,       TAG_TYPE_INT32 },
    { "MainRlyCmd",                    16,    2,    1,     0,       TAG_TYPE_INT32 },
    { "CmdFlg_Ins_En",                 18,    1,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD1_Heater",               19,    2,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD1_Data0",                21,    3,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD1_Data1",                24,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD1_Data2",                32,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD1_Data3",                40,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD1_Data4",                48,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD1_Data5",                56,    8,    1,     0,       TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_M2S_CMD1(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, M2S_CMD1_parse);
}

static const struct ess_parser M2S_CMD2_parse[] = {
    { "M2S_CMD2_CRC",                   0,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_ALIV",                  8,    4,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_PowerDown",            12,    2,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_FanCtrl",              14,    1,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_Data0",                15,    1,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_Data1",                16,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_Data2",                24,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_Data3",                32,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_Data4",                40,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_Data5",                48,    8,    1,     0,       TAG_TYPE_INT32 },
    { "M2S_CMD2_Data6",                56,    8,    1,     0,       TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,     0,       0 },
};

int ess_parse_M2S_CMD2(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, M2S_CMD2_parse);
}

static const struct ess_parser P2S_CMD_parse[] = {
    { "CRC_P2S_CMD",                    0,    8,    1,    0,        TAG_TYPE_INT32 },
    { "P2S_SBMU_No",                    8,    8,    1,    0,        TAG_TYPE_INT32 },
    { "RQ_Read_Detail_Data",           16,    2,    1,    0,        TAG_TYPE_INT32 },
    { "P2S_CMD_Data0",                 18,    6,    1,    0,        TAG_TYPE_INT32 },
    { "P2S_CMD_Data1",                 24,    8,    1,    0,        TAG_TYPE_INT32 },
    { "P2S_CMD_Data2",                 32,    8,    1,    0,        TAG_TYPE_INT32 },
    { "P2S_CMD_Data3",                 40,    8,    1,    0,        TAG_TYPE_INT32 },
    { "P2S_CMD_Data4",                 48,    8,    1,    0,        TAG_TYPE_INT32 },
    { "P2S_CMD_Data5",                 56,    8,    1,    0,        TAG_TYPE_INT32 },
    { NULL,                             0,    0,    0,    0,        0 },
};

int ess_parse_P2S_CMD(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, P2S_CMD_parse);
}

int ess_parse_dummy(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    (void) ci;
    (void) varp;
    (void) pretval;
    (void) flags;
    return 0;
}

int ess_parse_ALARM_STATUS(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    cJSON * jdat;
    uint32_t warn;
    uint32_t col;
    int32_t temp;
    uint32_t smoke;
    uint32_t status;
    const struct can_frame * fcan;
    uint64_t msec = uptime_msec(NULL);
    int TAG_53F = 0;
    int TAG_521 = 0;

    if (msec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;

    jdat   = cJSON_CreateObject();
    fcan   = &ci->canfrm;
    warn   = (uint32_t) fcan->data[1];
    col    = (uint32_t) fcan->data[2];

    temp   = (int32_t) fcan->data[3];
    temp  &= 0xff;
    temp  -= 40;

    smoke  = (uint32_t) fcan->data[4];
    status = (uint32_t) fcan->data[5];
    cJSON_AddNumberToObject(jdat, "TAG_4CA", warn);
    cJSON_AddNumberToObject(jdat, "TAG_4C5", col);
    cJSON_AddNumberToObject(jdat, "TAG_4C7", temp);
    cJSON_AddNumberToObject(jdat, "TAG_4C6", smoke);
    cJSON_AddNumberToObject(jdat, "TAG_4CB", status);

    do {
        int idx = -1;
        const char * tagn = "alarmdata0";
        hash_intptr_findint(varp->canhash, tagn, strlen(tagn), &idx);
        if (idx >= 0) {
            ci = &(varp->caninfp[idx]);
            TAG_53F = (ci->canfrm.data[2] & 0x2) != 0;
        }

        idx = -1;
        tagn = "alarmdata1";
        hash_intptr_findint(varp->canhash, tagn, strlen(tagn), &idx);
        if (idx >= 0) {
            ci = &(varp->caninfp[idx]);
            TAG_521 = (ci->canfrm.data[1] & 0x1) != 0;
        }
    } while (0);
    cJSON_AddNumberToObject(jdat, "TAG_53F", TAG_53F);
    cJSON_AddNumberToObject(jdat, "TAG_521", TAG_521);

    *pretval = (void *) jdat;
    return 0;
}

#define AIR_BITS(x_, y_) ((x_ - 1) * 8 + (y_ - 1))

static const struct ess_parser READ_TEMP_parse[] = {
    { "TAG_3F3",              32,     8,        1,   -40,   TAG_TYPE_INT32 },
    { NULL,                    0,     0,        0,     0,   0 },
};

int ess_parse_READ_TEMP(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, READ_TEMP_parse);
}

static const struct ess_parser READ_ON_parse[] = {
    { "TAG_3FD",    AIR_BITS(1, 1),   2,        1,    0,    TAG_TYPE_INT32 },
    { "T1",         AIR_BITS(1, 3),   2,        1,    0,    TAG_TYPE_INT32 },
    { "T2",         AIR_BITS(1, 5),   2,        1,    0,    TAG_TYPE_INT32 },
    { "T3",         AIR_BITS(1, 7),   2,        1,    0,    TAG_TYPE_INT32 },
    { "TAG_EA61",   AIR_BITS(2, 1),   8,        1,  -40,    TAG_TYPE_INT32 },
    { "TAG_EA60",   AIR_BITS(3, 1),   8,        1,  -40,    TAG_TYPE_INT32 },
    { "TAG_EA63",   AIR_BITS(4, 1),   8,        1,  -40,    TAG_TYPE_INT32 },
    { "TAG_EAA3",   AIR_BITS(5, 1),   8,      0.1,    0,    TAG_TYPE_FLOAT },
    { "TAG_EAA4",   AIR_BITS(6, 1),   8,      0.1,    0,    TAG_TYPE_FLOAT },
    { "TAG_EAA2",   AIR_BITS(8, 1),   6,        1,    0,    TAG_TYPE_INT32 },
    { "T10",        AIR_BITS(8, 7),   2,        1,    0,    TAG_TYPE_INT32 },
    { NULL,         0,                   0,        0,    0,    0 },
};

int ess_parse_READ_ON(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, flags, READ_ON_parse);
}

static const struct ess_parser READ1_parse[] = {
    { "T11",        0,                   8,        1,    0,   TAG_TYPE_INT32 },
    { "T12",        8,                   8,        1,    0,   TAG_TYPE_INT32 },
    { "T13",        16,                  16,       1,    0,   TAG_TYPE_INT32 },
    { "T14",        32,                  16,       1,    0,   TAG_TYPE_INT32 },
    { "T15",        48,                  8,        1,    0,   TAG_TYPE_INT32 },
    { NULL,         0,                   0,        0,    0,   0 },
};

int ess_parse_READ1(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, ESS_FLAG_EB /* flags */, READ1_parse);
}

static const struct ess_parser READ2_parse[] = {
    { "T16",        0,                8,        4,    0,   TAG_TYPE_INT32 },
    { "T17",        8,                8,      0.1,    0,   TAG_TYPE_FLOAT },
    { "T18",        16,               8,        1,    0,   TAG_TYPE_INT32 },
    { "T19",        AIR_BITS(4, 1),   1,        1,    0,   TAG_TYPE_INT32 },
    { "T20",        AIR_BITS(4, 2),   1,        1,    0,   TAG_TYPE_INT32 },
    { "T21",        AIR_BITS(4, 4),   5,        1,    0,   TAG_TYPE_INT32 },
    { "T22",        32,              16,        1,    0,   TAG_TYPE_INT32 },
    { NULL,         0,                0,        0,    0,   0 },
};

int ess_parse_READ2(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    uint64_t upmsec = 0;
    uptime_msec(&upmsec);
    if (upmsec > (ci->lastup + ESS_DATA_VALIDTIME))
        return -1;
    return ess_parse_common(ci, varp, pretval, ESS_FLAG_EB /* flags */, READ2_parse);
}

#define PARSE_UCELL_INFO(idx_) \
    do { \
        cip = &ci[idx_ - 1]; \
        parse_info = S2P_Detail_Ucell ## idx_ ## _parse; \
        ess_extract_value(retval, &cip->canfrm, &parse_info[2], 0); \
        ess_extract_value(retval, &cip->canfrm, &parse_info[3], 0); \
        ess_extract_value(retval, &cip->canfrm, &parse_info[4], 0); \
    } while (0)

int ess_parse_ucell(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    cJSON * retval;
    struct ess_caninfo * cip;
    const struct ess_parser * parse_info;

    retval = cJSON_CreateObject();

    PARSE_UCELL_INFO(1);
    PARSE_UCELL_INFO(2);
    PARSE_UCELL_INFO(3);
    PARSE_UCELL_INFO(4);
    PARSE_UCELL_INFO(5);
    PARSE_UCELL_INFO(6);
    PARSE_UCELL_INFO(7);
    PARSE_UCELL_INFO(8);
    PARSE_UCELL_INFO(9);
    PARSE_UCELL_INFO(10);
    PARSE_UCELL_INFO(11);
    PARSE_UCELL_INFO(12);
    PARSE_UCELL_INFO(13);
    PARSE_UCELL_INFO(14);
    PARSE_UCELL_INFO(15);
    PARSE_UCELL_INFO(16);
    PARSE_UCELL_INFO(17);

    cip = &ci[18 - 1];
    parse_info = S2P_Detail_Ucell18_parse;
    ess_extract_value(retval, &cip->canfrm, &parse_info[2], 0);

    *pretval = retval;
    return 0;
}

int ess_parse_tcell(struct ess_caninfo * ci,
    struct ess_info_var * varp, void * * pretval, int flags)
{
    cJSON * retval;
    struct ess_caninfo * cip;
    const struct ess_parser * parse_info;

    retval = NULL;
    ess_parse_common(ci, varp, (void * *) &retval,
        flags, &S2P_Detail_Tcell1_parse[3]);

    cip = &ci[1];
    parse_info = S2P_Detail_Tcell2_parse;
    ess_extract_value(retval, &cip->canfrm, &parse_info[3], 0);
    ess_extract_value(retval, &cip->canfrm, &parse_info[4], 0);

    *pretval = retval;
    return 0;
}
