#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#include <sys/stat.h>

#include <linux/can.h>
#include <linux/can/raw.h>
#include <time.h>

#include "../Config.h"
#include "../Log/log.h"
#include "../Define/define.h"
#include "../ShareMemory/shmMem.h"
#include "Ev_Comm.h"
#include "Module_EvComm.h"

//------------------------------------------------------------------------------
static struct SysConfigData *pSysConfig = NULL;
static struct SysInfoData *pSysInfo = NULL;
static struct WARNING_CODE_INFO *pSysWarning = NULL;
static struct AlarmCodeData *pAlarmCode = NULL;
static struct CHAdeMOData *ShmCHAdeMOData = NULL;
static struct GBTData *ShmGBTData = NULL;
static struct CcsData *ShmCcsData = NULL;
static struct FanModuleData *ShmFanModuleData = NULL;
static DcCommonInfo *ShmDcCommonData = NULL;

#define TempArraySize 10
uint8_t TempArray_1[TempArraySize]={0};
uint8_t TempArray_2[TempArraySize]={0};
uint8_t ptemp_1 = 0;
uint8_t ptemp_2 = 0;
bool firstcircule = true;
float EvTargetVolt[2] = { 0 };
float EvTargetCur[2] = { 0 };
//------------------------------------------------------------------------------
extern bool AbnormalStopAnalysis(uint8_t gun_index, uint8_t *errCode);

//------------------------------------------------------------------------------
/*static uint8_t getMaxConnectTempAndChiller(uint8_t headTemp1, uint8_t headTemp2,
        uint8_t chillerTemp1, uint8_t chillerTemp2)
{
    uint8_t i = 0;
    uint8_t tempSource[4] = {headTemp1, headTemp2, chillerTemp1, chillerTemp2};
    uint8_t maxTemp = 0;

    if (headTemp1 == UNDEFINED_TEMP &&
            headTemp2 == UNDEFINED_TEMP &&
            chillerTemp1 == UNDEFINED_TEMP &&
            chillerTemp2 == UNDEFINED_TEMP) {
        return UNDEFINED_TEMP;
    }

    //先取得第一個非UNDEFINED_TEMP的值
    for (i = 0; i < (sizeof(tempSource) / sizeof(uint8_t)); i++) {
        if (tempSource[i] != UNDEFINED_TEMP) {
            maxTemp = tempSource[i];
            break;
        }
    }

    //找最大的溫度值
    for (i = 0; i < (sizeof(tempSource) / sizeof(uint8_t)); i++) {
        if (tempSource[i] != UNDEFINED_TEMP) {
            if (maxTemp < tempSource[i]) {
                maxTemp = tempSource[i];
            }
        }
    }

    return maxTemp;
}
*/
static uint8_t getAvageTemp(uint8_t value,uint8_t gun)
{
    uint16_t avagetemp = 0;
    int i;
    uint8_t ptr = 0;
    uint8_t *pArray; 
    if(gun == 0) { 
        pArray = &TempArray_1;
        ptr = ptemp_1;
    } else {
        pArray = &TempArray_2;
        ptr = ptemp_2;
    }

    pArray[ptr] = value;
    for(i=0;i<TempArraySize;i++) 
        avagetemp+=pArray[i];

    if (firstcircule) {
        avagetemp/=(ptr+1);
    } else {
        avagetemp/=TempArraySize;
    }
    ptr++;
    if(ptr >= TempArraySize){
        ptr = 0;
        firstcircule = false;
    }
    if(gun == 0 )
        ptemp_1 = ptr;
    else 
        ptemp_2 = ptr;

    if(avagetemp > TEMP_BOUNDARY)
        return UNDEFINED_TEMP;
    return avagetemp;
}

static uint8_t getMaxConnectTemp(uint8_t headTemp1, uint8_t headTemp2)
{
    uint8_t maxTemp = 0;

    if (headTemp1 > TEMP_BOUNDARY &&
            headTemp2 > TEMP_BOUNDARY) {
        return UNDEFINED_TEMP;
    }

    if (headTemp1 <= TEMP_BOUNDARY) {
        maxTemp = headTemp1;
    }

    if (headTemp2 <= TEMP_BOUNDARY) {
        if (headTemp2 > maxTemp) {
            maxTemp = headTemp2;
        }
    }
    return maxTemp;
}

static float ReadAdcVolt(uint8_t AdcChannel)
{
    //AIN0=CCS GUN Temp 1
    //AIN1=CCS GUN Temp 2
    //AIN2=CCS_Proximity/2
    //AIN3=pilot voltage
    int fd = -1;
    uint8_t str[64] = {0};
    uint8_t AdcValue[8] = {'\0'};

    if (AdcChannel > 7) {
        return -1;
    }

    sprintf((char *)str, "/sys/bus/iio/devices/iio\:device0/in_voltage%d_raw", AdcChannel);
    fd = open((char *)str, O_RDONLY);
    read(fd, AdcValue, 4);

    close(fd);

    return (1.8 * atoi((char *)&AdcValue[0])) / 4095;
    //return (1.8 * atoi((char *)&AdcValue)) / 4095;
}

static void getChillerTemperature(ChillerTemp *chillerTemp)
{
    uint8_t i = 0;
    float adcVoltage = 0.0;
    ChillerTemp *pChillerTemp = (ChillerTemp *)chillerTemp;

    for (i = 0; i < 4; i++) {
        adcVoltage = 0.0;
        adcVoltage =  ReadAdcVolt(i);
        ShmDcCommonData->TempVolt[i] = adcVoltage;
        if ((adcVoltage <= 0.9) && (adcVoltage >= 0.8)) { //0 ~ -40
            pChillerTemp->Temp[i] = ((adcVoltage - 0.908) * 500) + 60;
            //log_info("1 adcVoltage = %f", (adcVoltage - 0.9) * 500);
        } else if ((adcVoltage <= 1.07) && (adcVoltage > 0.9)) {
            pChillerTemp->Temp[i] = ((adcVoltage - 0.91) * 705.88) + 60;
            //log_info("2 adcVoltage = %f", (adcVoltage - 0.9) * 500);
        } else {
            pChillerTemp->Temp[i] = UNDEFINED_TEMP;
        }

        /*CcsConnectorTemp1 = ReadAdcVolt(i);
        if ((CcsConnectorTemp1 <= 0.9) && (CcsConnectorTemp1 >= 0.8)) { //0 ~ -40
            CcsConnectorTemp1 = (CcsConnectorTemp1 - 0.9) * 500;
        } else if ((CcsConnectorTemp1 <= 1.07) && (CcsConnectorTemp1 > 0.9)) {
            CcsConnectorTemp1 = (CcsConnectorTemp1 - 0.9) * 705.88;
        } else {
            CcsConnectorTemp1 = 195;    //not available
        }
        CcsConnectorTemp |= ((unsigned int)(CcsConnectorTemp1 + 60) & 0xFF) << (i * 8); //0x00(-60)~0xFE(194)
        */
    }
}


static void AddrAssignment(uint8_t *data)
{
    uint8_t target_number[8];
    uint8_t index = 0x00;
    struct ChargingInfoData *pDcChargingInfo = NULL;

    memcpy(target_number, data, sizeof(target_number));
    index = *(data + 4);

    if (pSysConfig->TotalConnectorCount == 1) {
        index = 0x01;
    }

    //if (CheckUniqNumber(index)) {
    pDcChargingInfo = (struct ChargingInfoData *)GetDcChargingInfoData(index - 1);
    //DS60-120 add
    if (pDcChargingInfo->Type == _Type_Chademo) {
        log_info("Set EV board info : (Chademo) TargetAddr = %d ", index);
    } else if (pDcChargingInfo->Type == _Type_CCS_2) {
        log_info("Set EV board info : (CCS) TargetAddr = %d ", index);
    } else if (pDcChargingInfo->Type == _Type_GB) {
        log_info("Set EV board info : (GB) TargetAddr = %d ", index);
    }
    //log_info("EV board id = %x ", index); //DS60-120 remove

    //log_info("target_number[0] = %x ", target_number[0]);
    //log_info("target_number[1] = %x ", target_number[1]);
    //log_info("target_number[2] = %x ", target_number[2]);
    //log_info("target_number[3] = %x ", target_number[3]);
    //log_info("target_number[4] = %x ", target_number[4]);

    log_info("SetTargetAddr = %d, type = %d ", index, pDcChargingInfo->Type);

    SetTargetAddr(target_number, index);
    //}
}

void CheckEvConnect(int gunIndex)
{
    int isDisconnect = FALSE;
    int gunType = _Type_CCS_2;
    struct ChargingInfoData* pDcChargingInfo = (struct ChargingInfoData*)GetDcChargingInfoData(gunIndex);
    struct InfoCodeData* pInfoCode = (struct InfoCodeData*)GetShmInfoCodeData();
    struct ChargingInfoData* pDcChargingInfo_0 = (struct ChargingInfoData*)GetDcChargingInfoData(0);
    struct ChargingInfoData* pDcChargingInfo_1 = NULL;
    if (pSysConfig->TotalConnectorCount == 1)
        pDcChargingInfo_1 = (struct ChargingInfoData*)GetDcChargingInfoData(0);
    else
        pDcChargingInfo_1 = (struct ChargingInfoData*)GetDcChargingInfoData(1);

    if (pDcChargingInfo_0->Type == pDcChargingInfo_1->Type) {
        isDisconnect = ShmDcCommonData->pGunStatus[0].EVLoseFlag | ShmDcCommonData->pGunStatus[1].EVLoseFlag;
        gunType = pDcChargingInfo_0->Type;
    } else {
        isDisconnect = ShmDcCommonData->pGunStatus[gunIndex].EVLoseFlag;
        gunType = pDcChargingInfo->Type;
    }

    //log_info("ShmDcCommonData->EVDisconnectFlag[%d]:%d", gunIndex, ShmDcCommonData->EVDisconnectFlag[gunIndex]);
    if (isDisconnect) {
        switch (gunType) {
        case _Type_Chademo:
            pInfoCode->InfoEvents.bits.ChademoEvCommFail = YES;
            break;
        case _Type_CCS_2:
            pInfoCode->InfoEvents.bits.CcsEvCommFail = YES;
            break;
        case _Type_GB:
            pInfoCode->InfoEvents.bits.GbEvCommFail = YES;
            break;
        }

    } else {
        switch (gunType) {
        case _Type_Chademo:
            pInfoCode->InfoEvents.bits.ChademoEvCommFail = NO;
            break;
        case _Type_CCS_2:
            pInfoCode->InfoEvents.bits.CcsEvCommFail = NO;
            break;
        case _Type_GB:
            pInfoCode->InfoEvents.bits.GbEvCommFail = NO;
            break;
        }
    }
}

void CANReceiver(int fd)
{
    pid_t canRecPid;

    canRecPid = fork();

    if (canRecPid < 0) {
        log_error("Create CAN Bus receive task failed");
        return;
    }

    if (canRecPid == 0) {
        int isContinue = 1;
        int nbytes;
        int intCmd;
        uint8_t _index = 0;
        uint8_t recvID = 0;
        uint8_t targetGun = 0x00;
        uint8_t gunTypeIndex = 0;
        uint8_t ver[16] = {0};
        uint8_t printChillerTemp = NO;
        uint8_t printConnTemp = NO;
        uint8_t chillerTemp[2] = {0, 0};
        uint8_t maxChillerTemp = 0;
        uint8_t lastChillerTemp[2] = {0};
        uint8_t maxConnTemp = 0;
        uint8_t lastConnTemp[2] = {0, 0};
        struct can_frame frame;
        ChillerTemp chiilerTemp = {0};
        struct ChargingInfoData *pDcChargingInfo = NULL;
        int len = 0;
        char _info[1024];
        int i;
        unsigned char GunStatus[2];

        pSysConfig = (struct SysConfigData *)GetShmSysConfigData();
        pSysInfo = (struct SysInfoData *)GetShmSysInfoData();
        pSysWarning = (struct WARNING_CODE_INFO *)GetShmSysWarningInfo();

        pAlarmCode = (struct AlarmCodeData *)GetShmAlarmCodeData();

        ShmCHAdeMOData = (struct CHAdeMOData *)GetShmCHAdeMOData();
        ShmGBTData = (struct GBTData *)GetShmGBTData();
        ShmCcsData = (struct CcsData *)GetShmCcsData();
        ShmDcCommonData = (DcCommonInfo *)GetShmDcCommonData();
        ShmFanModuleData = (struct FanModuleData *)GetShmFanModuleData();

        ShmDcCommonData->pGunStatus[0].EVLoseTimer = time((time_t*)NULL);
        ShmDcCommonData->pGunStatus[1].EVLoseTimer = time((time_t*)NULL);

        //log_info("Module_EvRXComm Child's PID is %d", getpid());
        while (isContinue) {
            memset(&frame, 0, sizeof(struct can_frame));

            for (_index = 0; _index < pSysConfig->TotalConnectorCount; _index++) {
                pDcChargingInfo = (struct ChargingInfoData*)GetDcChargingInfoData(_index);
                // 檢查是否有收到EV小板訊號
                if (pSysInfo->SelfTestSeq == _STEST_COMPLETE) {
                    if ((time((time_t*)NULL) - ShmDcCommonData->pGunStatus[_index].EVLoseTimer > 10) &&
                        !ShmDcCommonData->pGunStatus[_index].EVLoseFlag && pSysInfo->SelfTestSeq == _STEST_COMPLETE &&
                        pDcChargingInfo->SystemStatus != S_UPDATE && !ShmDcCommonData->debugflag) {

                        ShmDcCommonData->pGunStatus[_index].EVLoseTimer = time((time_t*)NULL);
                        ShmDcCommonData->pGunStatus[_index].EVLoseFlag = TRUE;

                        system("/sbin/ip link set can0 down");
                        sleep(1);
                        system("/sbin/ip link set can0 type can bitrate 500000 restart-ms 100");
                        system("/sbin/ip link set can0 up");
                    }
                } else {
                    ShmDcCommonData->pGunStatus[targetGun].EVLoseTimer = time((time_t*)NULL);
                    ShmDcCommonData->pGunStatus[targetGun].EVLoseFlag = FALSE;
                }
                CheckEvConnect(_index);
            }

            nbytes = read(fd, &frame, sizeof(struct can_frame));
            if (nbytes <= 0) {
                usleep(10000);
                continue;
            }

            recvID = 0;
            targetGun = 0x00;
            intCmd = (int) (frame.can_id & CAN_EFF_MASK);

            if (intCmd == ADDRESS_REQ) {
                //ShmDcCommonData->CcsVersion = _CCS_VERSION_CHECK_TAG_V013S0;
                AddrAssignment(frame.data);
                continue;
            }

            intCmd = (int) (frame.can_id & CAN_EFF_MASK & 0xFFFFFF00);

            recvID = ((uint8_t) (frame.can_id & 0x000000FF)); // 0x01 or 0x02
            
            if (ShmDcCommonData->showCanPackage) {
                len = 0;
                len += sprintf(&_info[len], "CAN Dispenser <= EV Rx:\t[0x%X] ", frame.can_id);
                for (i = 0; i < nbytes; i++) {
                    len += sprintf(&_info[len], "%X ", frame.data[i]);
                }
                len += sprintf(&_info[len], "\n");
                printf("%s", _info);
            }

            for (_index = 0; _index < pSysConfig->TotalConnectorCount; _index++) {                    // 假設有找到回應的 Index
                pDcChargingInfo = (struct ChargingInfoData *)GetDcChargingInfoData(_index);

                //if (gun_count == 1 &&
                //        _chargingData[_index]->Type == _Type_CCS_2 &&
                //        ShmDcCommonData->CcsVersion == _CCS_VERSION_CHECK_TAG_V015S0) {
                //    target -= 1;
                //}

                if (pDcChargingInfo->Evboard_id == recvID) {
                    targetGun = _index;
                    break;
                }
            }

            if ((targetGun < 0) || (targetGun >= CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY)) {
                log_info("EvComm (CANReceiver) : Target index = %x is < 0 or > QUANTITY ", targetGun);
                usleep(10000);
                continue;
            }
            //else if (gun_count == 1 && targetGun == 0 && findIndex == 1 &&
            //           ShmDcCommonData->CcsVersion == _CCS_VERSION_CHECK_TAG_V015S0) {
            //    // 這樣的條件下~ 也是單槍 CCS 舊版本的狀況 : 因為舊版 CCS 不會 timeout, then send request id
            //    ShmDcCommonData->CcsVersion = _CCS_VERSION_CHECK_TAG_V013S0;
            //}

            if (intCmd == 256) {
                log_info("EvComm command = 256");
                usleep(10000);
                continue;
            }
            // Reset Connect Timer
            ShmDcCommonData->pGunStatus[targetGun].EVLoseTimer = time((time_t*)NULL);
            ShmDcCommonData->pGunStatus[targetGun].EVLoseFlag = FALSE;

            pDcChargingInfo = (struct ChargingInfoData *)GetDcChargingInfoData(targetGun);
            gunTypeIndex = pDcChargingInfo->type_index;

    
            switch (intCmd) {
            case NOTIFICATION_EV_STATUS:
                if (pDcChargingInfo->ConnectorPlugIn != frame.data[0]) {
                    if (frame.data[0] == PLUG) {
                        log_info("Conn %d, Plugin. ", targetGun);
                        pDcChargingInfo->isEVCCIDVerify = false;
#ifdef DD360Audi                       
                        if (pSysConfig->isAuthrizeByEVCCID)
                            pSysInfo->CurGunSelected = targetGun;
#endif                        
                    } else if (frame.data[0] == UNPLUG) {
                        if (pDcChargingInfo->Type != _Type_CCS_2)
                            log_info("Conn %d, Unplug. ", targetGun);
                        strcpy( (char *) pDcChargingInfo->EVCCID, "");
                    } else {
                        log_info("Conn %d, None Check. (%d) ", targetGun, frame.data[0]);
                    }
                    if(pDcChargingInfo->RemoteStartFlag == YES) {
                        pSysInfo->CurGunSelected = targetGun;
                    }
                }
                pDcChargingInfo->PilotVoltage = frame.data[1];
                // CCS 小板確認Pilot Voltage != 0
                if (pDcChargingInfo->Type == _Type_CCS_2 && pDcChargingInfo->PilotVoltage != 0) {
                    if (frame.data[0] == UNPLUG && pDcChargingInfo->ConnectorPlugIn != frame.data[0])
                        log_info("Conn %d, Unplug. ", targetGun);
                    pDcChargingInfo->ConnectorPlugIn = frame.data[0];
                } else {
                    pDcChargingInfo->ConnectorPlugIn = frame.data[0];
                }

                //log_info("index = %d, ConnectorPlugIn = %x, data[0] = %x ",
                //         targetGun,
                //         pDcChargingInfo->ConnectorPlugIn,
                //         frame.data[0]);

                //log_info("ConnectorPlugIn = %x ", (-120 + frame.data[1]) / 10);
                break;

            case ACK_EV_FW_VERSION:
                memset(ver, 0, sizeof(ver));
                if (pDcChargingInfo->Type == _Type_Chademo) {
                    memcpy(ver, frame.data, frame.can_dlc);
                    memcpy(ShmCHAdeMOData->evse[gunTypeIndex].version, ver, ARRAY_SIZE(ver));
                    ShmCHAdeMOData->evse[gunTypeIndex].SelfTest_Comp = PASS;
                    log_info("chademo ver. : %s", ShmCHAdeMOData->evse[gunTypeIndex].version);
                } else if (pDcChargingInfo->Type == _Type_GB) {
                    memcpy(ver, frame.data, frame.can_dlc);
                    memcpy(ShmGBTData->evse[gunTypeIndex].version, ver, ARRAY_SIZE(ver));
                    ShmGBTData->evse[gunTypeIndex].SelfTest_Comp = PASS;
                    log_info("gbt ver. : %s", ShmGBTData->evse[gunTypeIndex].version);
                } else if (pDcChargingInfo->Type == _Type_CCS_2) {
                    if (ShmCcsData->CommProtocol == _CCS_COMM_V2GMessage_DIN70121) {
                        memcpy(ver, frame.data, frame.can_dlc); //DS60-120 add

                        //for (uint8_t _vCount = 0, _vPoint = 0; _vCount < frame.can_dlc; _vCount++) {//DS60-120 remove
                        /*if (_vCount % 2 == 0 && _vCount != 0)
                        {
                            ver[_vCount + _vPoint] = 0x2E;
                            _vPoint++;
                        }*/

                        //ver[_vCount + _vPoint] = frame.data[_vCount];
                        //}

                        memcpy(&ShmCcsData->V2GMessage_DIN70121[gunTypeIndex].version, ver, ARRAY_SIZE(ver));
                        ShmCcsData->V2GMessage_DIN70121[gunTypeIndex].SelfTest_Comp = PASS;
                        log_info("CCS FW = %s ", ShmCcsData->V2GMessage_DIN70121[gunTypeIndex].version);
                    }
                }

                if (targetGun == 0) {
                    memset(pSysInfo->Connector1FwRev,
                           0,
                           sizeof(pSysInfo->Connector1FwRev));

                    memcpy(pSysInfo->Connector1FwRev, ver, ARRAY_SIZE(ver));
                } else if (targetGun == 1) {
                    memset(pSysInfo->Connector2FwRev,
                           0,
                           sizeof(pSysInfo->Connector2FwRev));

                    memcpy(pSysInfo->Connector2FwRev, ver, ARRAY_SIZE(ver));
                }
                break;

            case ACK_EV_HW_VERSION:
                //log_info("Get EV HW = %s ", frame.data);
                break;

            case ACK_GET_OUTPUT_REQ:
                //DS60-120 add

                if ((pDcChargingInfo->SystemStatus >= S_PREPARING_FOR_EV &&
                        pDcChargingInfo->SystemStatus <= S_TERMINATING) ||
                        pDcChargingInfo->SystemStatus == S_ALARM ||
                        (pDcChargingInfo->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
                         pDcChargingInfo->SystemStatus <= S_CCS_PRECHARGE_ST1)
                   ) {
                    if (pDcChargingInfo->EvBatteryStartSoc <= 0 &&
                            pDcChargingInfo->SystemStatus >= S_PREPARING_FOR_EVSE) {
                        pDcChargingInfo->EvBatteryStartSoc = frame.data[1];
                    }

                    // 進入充電時重新更新電池電量
                    if (GunStatus[targetGun] != pDcChargingInfo->SystemStatus && pDcChargingInfo->SystemStatus == S_CHARGING) {

                        log_info("Reset Gun%d SoC:%d", targetGun, frame.data[1]);
                        pDcChargingInfo->EvBatterySoc = frame.data[1];
                        GunStatus[targetGun] = pDcChargingInfo->SystemStatus;
                    }

                    if (frame.data[1] > pDcChargingInfo->EvBatterySoc || pDcChargingInfo->SystemStatus == S_CHARGING) {
                        pDcChargingInfo->EvBatterySoc = frame.data[1];
                        if (frame.data[1] > pDcChargingInfo->EvBatterySoc)
                            log_info("Gun%d SOC:%d", targetGun,pDcChargingInfo->EvBatterySoc);
                    }
                }
                //pDcChargingInfo->EvBatterySoc = frame.data[1]; //DS60-120 remove

                //Jerry add set voltage limit
                pDcChargingInfo->EvBatterytargetVoltage = (float)((frame.data[3] << 8) + frame.data[2]) / 10;
                if (pDcChargingInfo->EvBatterytargetVoltage > (GetMaxChargingVol(targetGun) * 0.1)) {
                    pDcChargingInfo->EvBatterytargetVoltage = (GetMaxChargingVol(targetGun) * 0.1);
                }
                //printf("id = %d, EvBatterytargetVoltage = %.2f", targetGun, pDcChargingInfo->EvBatterytargetVoltage);

                //Jerry add set currency limit
                pDcChargingInfo->EvBatterytargetCurrent = (float)((frame.data[5] << 8) + frame.data[4]) / 10;
                if (pDcChargingInfo->EvBatterytargetCurrent > (GetMaxCharginigCur(targetGun) * 0.1)) {
                    pDcChargingInfo->EvBatterytargetCurrent = (GetMaxCharginigCur(targetGun) * 0.1);
                }
                //printf("id = %d, EvBatterytargetCurrent = %.2f", targetGun, pDcChargingInfo->EvBatterytargetCurrent);

                pDcChargingInfo->RemainChargingDuration = ((short) frame.data[7] << 8) + (short) frame.data[6];
                //printf("RemainChargingDuration = %d",  pDcChargingInfo->RemainChargingDuration);

                if (pDcChargingInfo->Type == _Type_Chademo) {
                    if (ShmCHAdeMOData->ev[gunTypeIndex].PresentMsgFlowStatus != frame.data[0]) {
                        log_info("Gun%d CHAdeMO board status = %d ", targetGun, ShmCHAdeMOData->ev[gunTypeIndex].PresentMsgFlowStatus);
                        ShmCHAdeMOData->ev[gunTypeIndex].PresentMsgFlowStatus = frame.data[0];
                        ShmDcCommonData->pGunInfo[targetGun].EVStatus = frame.data[0];
                    }

                    ShmCHAdeMOData->ev[gunTypeIndex].EvDetection = frame.data[0];
                    ShmCHAdeMOData->ev[gunTypeIndex].SOC = pDcChargingInfo->EvBatterySoc;
                    ShmCHAdeMOData->ev[gunTypeIndex].TargetBatteryVoltage = (pDcChargingInfo->EvBatterytargetVoltage * 10);
                    ShmCHAdeMOData->ev[gunTypeIndex].ChargingCurrentRequest = (pDcChargingInfo->EvBatterytargetCurrent * 10);
                } else if (pDcChargingInfo->Type == _Type_GB) {
                    if (ShmGBTData->ev[gunTypeIndex].PresentMsgFlowStatus != frame.data[0]) {
                        log_info("Gun%d GB Board status = %d ", targetGun, ShmGBTData->ev[pDcChargingInfo->type_index].PresentMsgFlowStatus);
                        ShmGBTData->ev[gunTypeIndex].PresentMsgFlowStatus = frame.data[0];
                        ShmDcCommonData->pGunInfo[targetGun].EVStatus = frame.data[0];
                    }

                    ShmGBTData->ev[gunTypeIndex].EvDetection = frame.data[0];
                    ShmGBTData->ev[gunTypeIndex].SOC = pDcChargingInfo->EvBatterySoc;
                    ShmGBTData->ev[gunTypeIndex].TargetBatteryVoltage = (pDcChargingInfo->EvBatterytargetVoltage * 10);
                    ShmGBTData->ev[gunTypeIndex].ChargingCurrentRequest = (pDcChargingInfo->EvBatterytargetCurrent * 10);
                } else if (pDcChargingInfo->Type == _Type_CCS_2) {
                    if (ShmCcsData->V2GMessage_DIN70121[gunTypeIndex].PresentMsgFlowStatus != frame.data[0] && frame.data[0] != 0xFF) {
                        log_info("Gun%d CCS board status = %d ", targetGun, ShmCcsData->V2GMessage_DIN70121[pDcChargingInfo->type_index].PresentMsgFlowStatus);
                        ShmCcsData->V2GMessage_DIN70121[gunTypeIndex].PresentMsgFlowStatus = frame.data[0];
                        ShmDcCommonData->pGunInfo[targetGun].EVStatus = frame.data[0];
                    }
                }
                if (pDcChargingInfo->EvBatterytargetVoltage > (EvTargetVolt[targetGun] + 5) ||
                    pDcChargingInfo->EvBatterytargetVoltage < (EvTargetVolt[targetGun] - 5) ||
                    pDcChargingInfo->EvBatterytargetCurrent >(EvTargetCur[targetGun] + 2) ||
                    pDcChargingInfo->EvBatterytargetCurrent < (EvTargetCur[targetGun] - 2)) {
                    log_info("Gun%d TargetVoltage = %f , TargetCurrent = %f",
                        targetGun, pDcChargingInfo->EvBatterytargetVoltage, pDcChargingInfo->EvBatterytargetCurrent);
                    EvTargetVolt[targetGun] = pDcChargingInfo->EvBatterytargetVoltage;
                    EvTargetCur[targetGun] = pDcChargingInfo->EvBatterytargetCurrent;
                }
                //log_info("EvBatterytargetVoltage = %f ", pDcChargingInfo->EvBatterytargetVoltage);
                //log_info("EvBatterytargetCurrent = %f ", pDcChargingInfo->EvBatterytargetCurrent);
                //log_info("BatteryVoltage = %d ",
                //         ShmCHAdeMOData->ev[_chargingData[target]->type_index].TargetBatteryVoltage);
                //log_info("CurrentRequest = %d ",
                //         ShmCHAdeMOData->ev[_chargingData[target]->type_index].ChargingCurrentRequest);
                break;

            case ACK_GET_EV_BATTERY_INFO:
                //_chargingData[target].EvACorDCcharging = frame.data[0];
                //_chargingData[target]->TotalBatteryCap = ((float) frame.data[4] << 8) + (short) frame.data[3];
                pDcChargingInfo->EvBatteryMaxVoltage = ((float)(((uint16_t)frame.data[4] << 8) + (uint16_t)frame.data[3])) / 10;
                //_chargingData[target]->EvBatteryMaxCurrent = ((float) frame.data[4] << 8) + (short) frame.data[3];
                //_chargingData[target].MaxiBatteryCurrent = ((short) frame.data[6] << 8) + (short) frame.data[5];
                if (pDcChargingInfo->Type == _Type_Chademo) {
                    ShmCHAdeMOData->ev[gunTypeIndex].TotalBatteryCapacity = ((uint16_t)frame.data[2] << 8) + (uint16_t)frame.data[1];
                    ShmCHAdeMOData->ev[gunTypeIndex].MaxiBatteryVoltage = pDcChargingInfo->EvBatteryMaxVoltage;

                    //log_info("EvBatteryMaxVoltage = %f ", _chargingData[target]->EvBatteryMaxVoltage);
                    //log_info("TotalBatteryCapacity = %d ", ShmCHAdeMOData->ev[_chargingData[target]->type_index].TotalBatteryCapacity);
                    //log_info("MaxiBatteryVoltage = %d ", ShmCHAdeMOData->ev[_chargingData[target]->type_index].MaxiBatteryVoltage);
                } else if (pDcChargingInfo->Type == _Type_GB) {
                    ShmGBTData->ev[gunTypeIndex].TotalBatteryCapacity = ((uint16_t)frame.data[2] << 8) + (uint16_t)frame.data[1];
                    ShmGBTData->ev[gunTypeIndex].MaxiBatteryVoltage = pDcChargingInfo->EvBatteryMaxVoltage;
                }
                //else if (pDcChargingInfo->Type == _Type_CCS_2) {
                //}
                break;

            case ACK_GET_MISCELLANEOUS_INFO:
                pDcChargingInfo->GunLocked = frame.data[0];
                pDcChargingInfo->PilotVoltage = ((float)(-120 + frame.data[3])) / 10;

                if (pDcChargingInfo->Type == _Type_Chademo) {
                    ShmCHAdeMOData->evse[gunTypeIndex].ConnectorTemperatureP = frame.data[1];
                    ShmCHAdeMOData->evse[gunTypeIndex].ConnectorTemperatureN = frame.data[2];
                    ShmCHAdeMOData->evse[gunTypeIndex].EvboardStatus = frame.data[7];
                } else if (pDcChargingInfo->Type == _Type_GB) {
                    ShmGBTData->evse[gunTypeIndex].ConnectorTemperatureP = frame.data[1];
                    ShmGBTData->evse[gunTypeIndex].ConnectorTemperatureN = frame.data[2];
                    ShmGBTData->evse[gunTypeIndex].EvboardStatus = frame.data[7];
                }
                /*else if (pDcChargingInfo->Type == _Type_CCS_2) {
                if (ShmCcsData->CommProtocol == _CCS_COMM_V2GMessage_DIN70121) {
                    //ShmCcsData->V2GMessage_DIN70121[gunTypeIndex]. .ConnectorTemperatureP = frame.data[1];
                    //ShmCcsData->V2GMessage_DIN70121[gunTypeIndex]. .ConnectorTemperatureN = frame.data[2];
                }
                }*/

                ShmDcCommonData->ConnectorTemp[gunTypeIndex][0] = frame.data[1];
                ShmDcCommonData->ConnectorTemp[gunTypeIndex][1] = frame.data[2];

                if (ShmDcCommonData->TestTemperature == YES) { //ReadCmdline test
                    break;
                }

                printChillerTemp = NO;
                printConnTemp = NO;

                if (((ShmDcCommonData->ChillerValve.MultiChillerGun & 0x80) >> 7) == YES) {
                    getChillerTemperature(&chiilerTemp);
                    memcpy((char *)ShmDcCommonData->SystemTemp, (char *)chiilerTemp.Temp, sizeof(ChillerTemp));
                    chillerTemp[0] = getMaxConnectTemp(chiilerTemp.Temp[0], chiilerTemp.Temp[1]);
                    chillerTemp[1] = getMaxConnectTemp(chiilerTemp.Temp[2], chiilerTemp.Temp[3]);
                    if ((ShmDcCommonData->ChillerValve.MultiChillerGun & 0x7F) == 1) {
                        // 單Chiller
                        maxChillerTemp = getMaxConnectTemp(chillerTemp[0], chillerTemp[1]);
                    } else {
                        // 雙Chiller
                        if (gunTypeIndex == 0) {
                            maxChillerTemp = chillerTemp[0];
                        } else {
                            maxChillerTemp = chillerTemp[1];
                        }
                    }
                    //if ((maxChillerTemp - 3) >= pDcChargingInfo->ChillerTemp) {
                    //    printChillerTemp = YES;
                    //}
                    if(maxChillerTemp > (lastChillerTemp[gunTypeIndex] + 2) || maxChillerTemp < (lastChillerTemp[gunTypeIndex] - 2))
                    {
                        lastChillerTemp[gunTypeIndex] = maxChillerTemp;
                        printChillerTemp = YES;
                    }

                    pDcChargingInfo->ChillerTemp = maxChillerTemp;
                }
                if (ShmDcCommonData->ConnectorTemp[gunTypeIndex][0] != 0 && ShmDcCommonData->ConnectorTemp[gunTypeIndex][1] != 0) {
                    maxConnTemp = getMaxConnectTemp(frame.data[1], frame.data[2]);
                    //if ((maxConnTemp - 3) >= pDcChargingInfo->ConnectorTemp) {
                    //    printConnTemp = YES;
                    //}
                    maxConnTemp = getAvageTemp(maxConnTemp, targetGun);
                    if (maxConnTemp > (lastConnTemp[targetGun] + 2) || maxConnTemp < (lastConnTemp[targetGun] - 2))
                    {
                        lastConnTemp[targetGun] = maxConnTemp;
                        printConnTemp = YES;
                    }

                    pDcChargingInfo->ConnectorTemp = maxConnTemp;
                } else {
                    //log_info("Connector%d Tmep is zero:[%d,%d]", gunTypeIndex, ShmDcCommonData->ConnectorTemp[gunTypeIndex][0],
                    //    ShmDcCommonData->ConnectorTemp[gunTypeIndex][1]);
                }
                //紀錄槍頭和水冷機溫度, 在系統狀態變化或溫度大於150
                if ((ShmDcCommonData->SystemModeChange[targetGun] == YES) ||
                        (printConnTemp == YES) ||
                        (printChillerTemp == YES) //&&
                        //(((pDcChargingInfo->ConnectorTemp >= GUN_OTP_VALUE) &&
                        //  (pDcChargingInfo->ConnectorTemp != UNDEFINED_TEMP)) ||
                        // ((pDcChargingInfo->ChillerTemp >= GUN_OTP_VALUE) &&
                        //  (pDcChargingInfo->ChillerTemp != UNDEFINED_TEMP)))
                   ) {
                    ShmDcCommonData->SystemModeChange[targetGun] = NO;
                    log_info("Conn %d max temp = %d, chiller = [%d %d], chiller2 = [%d %d]",
                             targetGun,
                             pDcChargingInfo->ConnectorTemp,
                             chiilerTemp.Temp[0],
                             chiilerTemp.Temp[1],
                             chiilerTemp.Temp[2],
                             chiilerTemp.Temp[3]);
                }

                if (((ShmDcCommonData->ChillerValve.MultiChillerGun & 0x80) >> 7) == YES) {
                    //沒有水冷槍
                    break;
                }

                if ((ShmDcCommonData->ChillerValve.MultiChillerGun & 0x7F) == 1) {
                    //單一水冷槍,不需要切換水冷機油閥
                    //ShmFanModuleData-> ? = YES; //尚未定義
                    break;
                }

                //紀錄槍頭溫度
                if (pDcChargingInfo->ConnectorTemp != UNDEFINED_TEMP) {
                    if (targetGun == 0) {
                        ShmDcCommonData->ChillerValve.LeftTemp = pDcChargingInfo->ConnectorTemp;
                    } else if (targetGun == 1) {
                        ShmDcCommonData->ChillerValve.RightTemp = pDcChargingInfo->ConnectorTemp;
                    }
                }

                //有兩把水冷槍,判斷兩把槍頭溫度,將水冷機節流閥導向溫度高的那一把槍
                if (ShmDcCommonData->ChillerValve.LeftTemp > ShmDcCommonData->ChillerValve.RightTemp) {
                    //ShmFanModuleData->? = YES; //尚未定義
                } else {
                    //ShmFanModuleData->? = NO; //尚未定義
                }

                //log_info("EvboardStatus = %x ",
                //         ShmCHAdeMOData->evse[gunTypeIndex].EvboardStatus);
                //log_info("ConnectorPlug locked = %x ",
                //         frame.data[0]);
                //log_info("PilotVoltage = %x ", (-120 + frame.data[3]) / 10);
                break;

            case ACK_EVSE_ISOLATION_STATUS:
                break;

            case ACK_EVSE_PRECHAGE_INFO:
                pDcChargingInfo->PrechargeStatus = frame.data[0];
                break;

            case NOTIFICATION_EV_STOP:
                // 車端要求停止
                if ((pDcChargingInfo->SystemStatus >= S_REASSIGN_CHECK && //DS60-120 add
                        pDcChargingInfo->SystemStatus <= S_TERMINATING) ||
                        (pDcChargingInfo->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
                         pDcChargingInfo->SystemStatus <= S_CCS_PRECHARGE_ST1)) {
                    // frame.data[0] : 0x01 => normal stop, 0x02 => ev emergency stop
                    /*
                    log_info("----------------------------- Gun%d NOTIFICATION_EV_STOP err level = %d -----------------------------",
                             targetGun,
                             frame.data[0]);
                             */
                    if (frame.data[0] == 0x02) {
                        if (AbnormalStopAnalysis(targetGun, frame.data + 1) == true) {
                            pDcChargingInfo->StopChargeFlag = YES;
                        } else {
                            pDcChargingInfo->NormalStopChargeFlag = YES;
                        }
                    } else {
                        AbnormalStopAnalysis(targetGun, frame.data + 1);
                        pDcChargingInfo->NormalStopChargeFlag = YES;
                    }
                }
                break;
            case ACK_EVCCID_REQ:

                if (frame.can_dlc > 0 && strcmp ( (char *)pDcChargingInfo->EVCCID, "" ) == EQUAL &&
                    pDcChargingInfo->Type == _Type_CCS_2)
                {
                    {
                        memset (
                                ShmCcsData->V2GMessage_DIN70121 [pDcChargingInfo->type_index].SessionSetupRequest.EVCCID,
                                0,
                                sizeof(ShmCcsData->V2GMessage_DIN70121 [pDcChargingInfo->type_index].SessionSetupRequest.EVCCID) );
                        memcpy (
                                ShmCcsData->V2GMessage_DIN70121 [pDcChargingInfo->type_index].SessionSetupRequest.EVCCID,
                                frame.data, frame.can_dlc );
                    }

                    sprintf ( (char *) pDcChargingInfo->EVCCID, "%.2x%.2x%.2x%.2x%.2x%.2x", frame.data [0],
                            frame.data [1], frame.data [2], frame.data [3], frame.data [4], frame.data [5] );

                    pDcChargingInfo->EVCCID [17] = '\0';
                    log_info( "Gun %d->EVCCID = %s ", targetGun, pDcChargingInfo->EVCCID );
                }
                break;
            case ACK_DOWNLOAD_FINISH:
                log_info("Gun %d image download finish",targetGun);
                break;
            default:
                log_info("EV board = %d, Ack none defined. intCmd = %d  ", targetGun, intCmd);
                break;
            }//switch

            usleep(10000);
        }//while
    }
}