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

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/timeb.h>
#include <sys/time.h>

#include <net/if.h>

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

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

//------------------------------------------------------------------------------
static struct SysConfigData *pSysConfig = NULL;
static struct SysInfoData *pSysInfo = NULL;

static struct FaultCodeData *pFaultCode = NULL;
static struct AlarmCodeData *pAlarmCode = NULL;
static struct CHAdeMOData *ShmCHAdeMOData = NULL;
static struct GBTData *ShmGBTData = NULL;
static struct CcsData *ShmCcsData = NULL;
static DcCommonInfo *ShmDcCommonData = NULL;
static SelectGunInfo *ShmSelectGunInfo = NULL;

// 限制最大充電電壓,因應不同 type 槍線來限制
// Chademo : 500V, 125A,
// GB : 750, 120A
// CCS : 950V, 120A
//DS60-120 add
//static double chademoVol = 5000;
//static double ccsVol = 9500;
//static double gbVol = 7500;
static float maxChargingVol[2] = {0, 0};       // 限制最大充電電壓,如依照模塊則填上 0
// 限制最大充電電流與能量透過 Web
static float maxChargingCur[2] = {0, 0};         // 限制最大充電電流,如依照模塊則填上 0
static float maxChargingPow = 0;                   // 限制最大充電能量,如依照模塊則填上 0

static float LogInfo[2][10]; //DS60-120 add
static int CanFd = -1;

bool psuOutputReady[2] = {0, 0};
uint8_t deratingIndex[2];
//------------------------------------------------------------------------------
extern void CANReceiver(int fd);
extern void ClearAbnormalStatus_Chademo(uint8_t gun_index);
extern void ClearAbnormalStatus_GB(uint8_t gun_index);
extern void ClearAbnormalStatus_CCS(uint8_t gun_index);

//------------------------------------------------------------------------------
int GetCanFd(void)
{
    return CanFd;
}

uint32_t GetTimeoutValue(struct timeval _sour_time)
{
    struct timeval _end_time;
    gettimeofday(&_end_time, NULL);

    return 1000000 * (_end_time.tv_sec - _sour_time.tv_sec) + _end_time.tv_usec - _sour_time.tv_usec;
}

int InitCanBus(void)
{
    int s0, nbytes;
    struct timeval tv;
    struct ifreq ifr0;
    struct sockaddr_can addr0;
    struct can_filter rxfilter[3];

    system("/sbin/ip link set can0 down");
    system("/sbin/ip link set can0 type can bitrate 500000 restart-ms 100");
    system("/sbin/ip link set can0 up");

    s0 = socket(PF_CAN, SOCK_RAW, CAN_RAW);

    tv.tv_sec = 0;
    tv.tv_usec = 10000;

    if (setsockopt(s0, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct  timeval)) < 0) {
        log_error("Set SO_RCVTIMEO NG");
    }

    nbytes = 40960;
    if (setsockopt(s0, SOL_SOCKET,  SO_RCVBUF, &nbytes, sizeof(int)) < 0) {
        log_error("Set SO_RCVBUF NG");
    }

    nbytes = 40960;
    if (setsockopt(s0, SOL_SOCKET, SO_SNDBUF, &nbytes, sizeof(int)) < 0) {
        log_error("Set SO_SNDBUF NG");
    }

    rxfilter[0].can_id = 0x01;
    rxfilter[0].can_mask = 0x000000FF;
    rxfilter[1].can_id = 0x02;
    rxfilter[1].can_mask = 0x000000FF;
    rxfilter[2].can_id = 0x01FF;
    rxfilter[2].can_mask = 0x00000FFF;
    if (setsockopt(s0, SOL_CAN_RAW, CAN_RAW_FILTER,
                   &rxfilter, sizeof(struct can_filter) * 3) < 0) {
        log_error("RX setsockopt CAN_RAW_FILTER failed");
    }

    strcpy(ifr0.ifr_name, "can0");
    ioctl(s0, SIOCGIFINDEX, &ifr0); /* ifr.ifr_ifindex gets filled with that device's index */
    addr0.can_family = AF_CAN;
    addr0.can_ifindex = ifr0.ifr_ifindex;
    bind(s0, (struct sockaddr *)&addr0, sizeof(addr0));

    return s0;
}

float GetMaxChargingVol(uint8_t index)
{
    return maxChargingVol[index];
}

float GetMaxCharginigCur(uint8_t index)
{
    return maxChargingCur[index];
}

static void SendCommunicationOnly(uint8_t index)
{
    struct ChargingInfoData *pDcCharginigInfo = (struct ChargingInfoData *)GetDcChargingInfoData(index);
    uint8_t targetID = pDcCharginigInfo->Evboard_id;
/*
    if (pSysConfig->TotalConnectorCount == 1 &&
            pDcCharginigInfo->Type == _Type_CCS_2 &&
            ShmDcCommonData->CcsVersion == _CCS_VERSION_CHECK_TAG_V015S0) {
        targetID += 1;
    }
*/
    SetChargingPermission(index,
                          COMMUNICATION,
                          pDcCharginigInfo->AvailableChargingPower,
                          0,
                          0,
                          targetID);
}

static void SendStopOnly(uint8_t index)
{
    struct ChargingInfoData *pDcCharginigInfo = (struct ChargingInfoData *)GetDcChargingInfoData(index);

    uint8_t targetID = pDcCharginigInfo->Evboard_id;
/*
    if (pSysConfig->TotalConnectorCount == 1 &&
            pDcCharginigInfo->Type == _Type_CCS_2 &&
            ShmDcCommonData->CcsVersion == _CCS_VERSION_CHECK_TAG_V015S0) {
        targetID += 1;
    }
*/
    SetChargingPermission(index,
                          STOP,
                          pDcCharginigInfo->AvailableChargingPower,
                          0,
                          0,
                          targetID);
}

static uint8_t GetStopChargingReasonByEvse(uint8_t gunIndex, uint8_t *reason)
{
    uint8_t result = NO;
    struct ChargingInfoData *pDcChargingInfo = (struct ChargingInfoData *)GetDcChargingInfoData(gunIndex);

    if (pAlarmCode->AlarmEvents.bits.EmergencyStopTrip == 0x01) {
        // 012251
        *(reason + 5)  = 0;
        *(reason + 4)  = 1;
        *(reason + 3)  = 2;
        *(reason + 2)  = 2;
        *(reason + 1)  = 5;
        *(reason + 0)  = 1;
        result = YES;
    }

    if (pDcChargingInfo->Type == _Type_Chademo) {
        if (pFaultCode->FaultEvents.bits.ChademoOutputRelayDrivingFault == YES) {
            // 011012
            *(reason + 5) = 0;
            *(reason + 4) = 1;
            *(reason + 3) = 1;
            *(reason + 2) = 0;
            *(reason + 1) = 1;
            *(reason + 0) = 2;
            result = YES;
        } else if (pAlarmCode->AlarmEvents.bits.ChademoOutputUVPFail == YES) {
            // 012289
            *(reason + 5) = 0;
            *(reason + 4) = 1;
            *(reason + 3) = 2;
            *(reason + 2) = 2;
            *(reason + 1) = 8;
            *(reason + 0) = 9;
            result = YES;
        } else if (pAlarmCode->AlarmEvents.bits.ChademoGfdTrip == YES) {
            // 012234
            *(reason + 5) = 0;
            *(reason + 4) = 1;
            *(reason + 3) = 2;
            *(reason + 2) = 2;
            *(reason + 1) = 3;
            *(reason + 0) = 4;
            result = YES;
        }
    } else if (pDcChargingInfo->Type == _Type_GB) {
        if (pFaultCode->FaultEvents.bits.ChademoOutputRelayDrivingFault == YES) {
            // 012290
            *(reason + 5) = 0;
            *(reason + 4) = 1;
            *(reason + 3) = 2;
            *(reason + 2) = 2;
            *(reason + 1) = 9;
            *(reason + 0) = 0;
            result = YES;
        } else if (pAlarmCode->AlarmEvents.bits.GbGfdTrip == YES) {
            // 012236
            *(reason + 5) = 0;
            *(reason + 4) = 1;
            *(reason + 3) = 2;
            *(reason + 2) = 2;
            *(reason + 1) = 3;
            *(reason + 0) = 6;
            result = YES;
        }
    } else if (pDcChargingInfo->Type == _Type_CCS_2) {
        if (pFaultCode->FaultEvents.bits.CcsOutputRelayDrivingFault == YES) {
            // 011014
            *(reason + 5) = 0;
            *(reason + 4) = 1;
            *(reason + 3) = 1;
            *(reason + 2) = 0;
            *(reason + 1) = 1;
            *(reason + 0) = 4;
            result = YES;
        } else if (pAlarmCode->AlarmEvents.bits.CcsOutputUVPFail == YES) {
            // 012288
            *(reason + 5) = 0;
            *(reason + 4) = 1;
            *(reason + 3) = 2;
            *(reason + 2) = 2;
            *(reason + 1) = 8;
            *(reason + 0) = 8;
            result = YES;
        } else if (pAlarmCode->AlarmEvents.bits.CcsGfdTrip == YES) {
            // 012235
            *(reason + 5) = 0;
            *(reason + 4) = 1;
            *(reason + 3) = 2;
            *(reason + 2) = 2;
            *(reason + 1) = 3;
            *(reason + 0) = 5;
            result = YES;
        }
    }

    return result;
}

static void setCurrentOutput(void)
{
    struct ChargingInfoData *chargingData_1 = NULL;
    struct ChargingInfoData *chargingData_2 = NULL;

    if (pSysConfig->TotalConnectorCount == 1) {
        chargingData_1 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
        //chargingData_2 = (struct ChargingInfoData *)GetDcChargingInfoData(0);

        if (chargingData_1->FireChargingVoltage <= 500) { //DS60-120 add
            chargingData_1->PresentChargingCurrent = 0;
        }

    } else if (pSysConfig->TotalConnectorCount == 2) {
        chargingData_1 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
        chargingData_2 = (struct ChargingInfoData *)GetDcChargingInfoData(1);

        if (chargingData_1->FireChargingVoltage <= 500) { //DS60-120 add
            chargingData_1->PresentChargingCurrent = 0;
        }

        if (chargingData_2->FireChargingVoltage <= 500) {
            chargingData_2->PresentChargingCurrent = 0;
        }
    }
}
void GetOtpPwrOrCurMethod(struct ChargingInfoData* chargingData, float* pow, float* cur)
{
    if (chargingData->ConnectorTemp >= STAGE1_GUN_DERATING_TEMP &&
        chargingData->ConnectorTemp < STAGE2_GUN_DERATING_TEMP &&
        chargingData->deratingByConnOtp.deratingIndex < 1)
    {
        chargingData->deratingByConnOtp.deratingIndex = 1;
    } else if (chargingData->ConnectorTemp >= STAGE2_GUN_DERATING_TEMP &&
        chargingData->ConnectorTemp != UNDEFINED_TEMP &&
        chargingData->deratingByConnOtp.deratingIndex < 2)
    {
        chargingData->deratingByConnOtp.deratingIndex = 2;
    }

    if (chargingData->deratingByConnOtp.deratingTargetRate[chargingData->deratingByConnOtp.deratingIndex] != 0)
    {
        *pow *= chargingData->deratingByConnOtp.deratingTargetRate[chargingData->deratingByConnOtp.deratingIndex];
    } else if (chargingData->deratingByConnOtp.deratingTargetCurrent[chargingData->deratingByConnOtp.deratingIndex] != 0)
    {
        if (*cur > chargingData->deratingByConnOtp.deratingTargetCurrent[chargingData->deratingByConnOtp.deratingIndex])
            *cur = chargingData->deratingByConnOtp.deratingTargetCurrent[chargingData->deratingByConnOtp.deratingIndex];
    }
}
static void SetPresentChargingOutputCap(void)
{
    float pow1 = 0, cur1 = 0;
    float pow2 = 0, cur2 = 0;
    struct ChargingInfoData *chargingData_1 = NULL;
    struct ChargingInfoData *chargingData_2 = NULL;

    if (pSysConfig->TotalConnectorCount == 1) {
        chargingData_1 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
        chargingData_2 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
    } else if (pSysConfig->TotalConnectorCount == 2) {
        chargingData_1 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
        chargingData_2 = (struct ChargingInfoData *)GetDcChargingInfoData(1);
    }

#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
    float vol = 0;
#endif //!defined DD360 && !defined DD360Audi

    pow1 = chargingData_1->AvailableChargingPower;
    cur1 = chargingData_1->AvailableChargingCurrent;

#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
    vol = chargingData_1->MaximumChargingVoltage;
    GetMaxVolAndCurMethod(chargingData_1->Index, &vol, &cur1);
    GetMaxPowerMethod(chargingData_1->Index, &pow1);
#endif //!defined DD360 && !defined DD360Audi

    //DS60-120 add
    if (pow1 <= 0) {
        cur1 = 0;
    } else {
        if (chargingData_1->SystemStatus == S_CHARGING &&
                chargingData_1->FireChargingVoltage > 1500) {
            float maxCur = 0;
            maxCur = (pow1 * 1000) / chargingData_1->FireChargingVoltage;

            if (maxCur * 10 <= cur1) {
                //log_info("Gun1 -> MaxCharging Current = %f, Cap Current = %f ", (maxCur * 10), cur1);
                cur1 = maxCur * 10;
            }
        }
    }

    if (chargingData_1->deratingByConnOtp.isNeedDerating) {
        GetOtpPwrOrCurMethod(chargingData_1, &pow1, &cur1);
        if (deratingIndex[0] != chargingData_1->deratingByConnOtp.deratingIndex) {
            log_info("Gun0 Derating Index Set %d", chargingData_1->deratingByConnOtp.deratingIndex);
            deratingIndex[0] = chargingData_1->deratingByConnOtp.deratingIndex;
        }
    }
    pow2 = chargingData_2->AvailableChargingPower;
    cur2 = chargingData_2->AvailableChargingCurrent;

#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
    vol = chargingData_2->MaximumChargingVoltage;
    GetMaxVolAndCurMethod(chargingData_2->Index, &vol, &cur2);
    GetMaxPowerMethod(chargingData_2->Index, &pow2);
#endif //!defined DD360 && !defined DD360Audi

    //DS60-120 add
    if (pow2 <= 0) {
        cur2 = 0;
    } else {
        if (chargingData_2->SystemStatus == S_CHARGING &&
                chargingData_2->FireChargingVoltage > 1500) {
            float maxCur = 0;
            maxCur = (pow2 * 1000) / chargingData_2->FireChargingVoltage;

            if (maxCur * 10 <= cur2) {
                //log_info("Gun2 -> MaxCharging Current = %f, Cap Current = %f ", (maxCur * 10), cur2);
                cur2 = maxCur * 10;
            }
        }
    }

    if (chargingData_2->deratingByConnOtp.isNeedDerating) {
        GetOtpPwrOrCurMethod(chargingData_2, &pow2, &cur2);
        if (deratingIndex[1] != chargingData_2->deratingByConnOtp.deratingIndex) {
            log_info("Gun1 Derating Index Set %d", chargingData_2->deratingByConnOtp.deratingIndex);
            deratingIndex[1] = chargingData_2->deratingByConnOtp.deratingIndex;
        }
    }

    //DS60-120 add
    if ((LogInfo[0][EV_LOG_OUTPUT_CAP_POW] <= pow1 - 5 ||
            LogInfo[0][EV_LOG_OUTPUT_CAP_POW] >= pow1 + 5) ||
            (LogInfo[0][EV_LOG_OUTPUT_CAP_CUR] <= cur1 - 5 ||
             LogInfo[0][EV_LOG_OUTPUT_CAP_CUR] >= cur1 + 5) ||
            (LogInfo[1][EV_LOG_OUTPUT_CAP_POW] <= pow2 - 5 ||
             LogInfo[1][EV_LOG_OUTPUT_CAP_POW] >= pow2 + 5) ||
            (LogInfo[1][EV_LOG_OUTPUT_CAP_CUR] <= cur2 - 5 ||
             LogInfo[1][EV_LOG_OUTPUT_CAP_CUR] >= cur2 + 5)
       ) {
        //log_info("----------------------------------------------------- ");
        log_info("To EV (Real) Power_1 = %.1f, Cur_1 = %.1f, Power_2 = %.1f, Cur_2 = %.1f",
                 pow1 / 10, cur1 / 10, pow2 / 10, cur2 / 10);
        //log_info("----------------------------------------------------- ");

        LogInfo[0][EV_LOG_OUTPUT_CAP_POW] = pow1;
        LogInfo[0][EV_LOG_OUTPUT_CAP_CUR] = cur1;
        LogInfo[1][EV_LOG_OUTPUT_CAP_POW] = pow2;
        LogInfo[1][EV_LOG_OUTPUT_CAP_CUR] = cur2;
        chargingData_1->RealMaxCurrent = cur1;
        chargingData_1->RealMaxPower = pow1;

        if (pSysConfig->TotalConnectorCount == 2) {
            chargingData_2->RealMaxCurrent = cur2;
            chargingData_2->RealMaxPower = pow2;
        }
    }

    SetPresentOutputCapacity(pow1, cur1, pow2, cur2);
}

static void GetMaxVolAndCurMethod(uint8_t index, float *vol, float *cur)
{
    struct ChargingInfoData *pDcChargingInfo = (struct ChargingInfoData *)GetDcChargingInfoData(index);

    if (maxChargingVol[index] != 0 && maxChargingVol[index] <= *vol) {
        *vol = maxChargingVol[index];
    }

    if (maxChargingCur[index] != 0 && maxChargingCur[index] <= *cur) {
        *cur = maxChargingCur[index];
    }

    if (((pDcChargingInfo->SystemStatus >= S_PREPARING_FOR_EVSE &&
            pDcChargingInfo->SystemStatus <= S_CHARGING) ||
            (pDcChargingInfo->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
             pDcChargingInfo->SystemStatus <= S_CCS_PRECHARGE_ST1)) &&
            pDcChargingInfo->ChargingProfileCurrent >= 0 &&
            pDcChargingInfo->ChargingProfileCurrent <= *cur
       ) {
        *cur = pDcChargingInfo->ChargingProfileCurrent;
    }
}

static uint8_t waitPsuVolwithRealyVol(uint8_t gunIndex)
{
    PcPsuOutput *pPcPsuOutput = (PcPsuOutput *)&ShmDcCommonData->PcPsuOutput[gunIndex];
    struct ChargingInfoData *pDcChargingInfo = (struct ChargingInfoData *)GetDcChargingInfoData(gunIndex);
    int vol = 0;

    vol = abs(pPcPsuOutput->Voltage - pDcChargingInfo->FireChargingVoltage);
    if (vol <= 10) {
        return YES;
    }

    return NO;
}

/**
 * [SetPresentChargingOutputFromPcPsu 充電狀態讀取電源櫃PSU輸出電壓電流,縮小誤差值]
 * @Author   Jerry
 * @DateTime 2021-07-05
 */
static void SetPresentChargingOutputFromPcPsu(uint8_t gunCount)
{
    float vol1 = 0, cur1 = 0;
    float vol2 = 0, cur2 = 0;
    PcPsuOutput *pPcPsuOutput0 = NULL;
    PcPsuOutput *pPcPsuOutput1 = NULL;
    struct ChargingInfoData *chargingData0 = NULL;
    struct ChargingInfoData *chargingData1 = NULL;

    switch (gunCount) {
    case 1:
        pPcPsuOutput0 = (PcPsuOutput *)&ShmDcCommonData->PcPsuOutput[0];
        pPcPsuOutput1 = (PcPsuOutput *)&ShmDcCommonData->PcPsuOutput[0];
        chargingData0 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
        chargingData1 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
        break;

    case 2:
        pPcPsuOutput0 = (PcPsuOutput *)&ShmDcCommonData->PcPsuOutput[0];
        pPcPsuOutput1 = (PcPsuOutput *)&ShmDcCommonData->PcPsuOutput[1];
        chargingData0 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
        chargingData1 = (struct ChargingInfoData *)GetDcChargingInfoData(1);
        break;
    }

    vol1 = pPcPsuOutput0->Voltage == 0 ? chargingData0->FireChargingVoltage : (((float)pPcPsuOutput0->Voltage));
    cur1 = (chargingData0->PresentChargingCurrent * 10);//(((float)pPcPsuOutput0->Current) * 0.1);
    vol2 = pPcPsuOutput1->Voltage == 0 ? chargingData1->FireChargingVoltage : (((float)pPcPsuOutput1->Voltage));
    cur2 = (chargingData1->PresentChargingCurrent * 10);//(((float)pPcPsuOutput1->Current) * 0.1);

    if (
        (LogInfo[0][EV_LOG_NOW_OUTPUT_VOL] >= vol1 + CHK_VOL_RANGE) ||
        (LogInfo[0][EV_LOG_NOW_OUTPUT_VOL] <= vol1 - CHK_VOL_RANGE) ||
        (LogInfo[0][EV_LOG_NOW_OUTPUT_CUR] >= cur1 + CHK_CUR_RANGE) ||
        (LogInfo[0][EV_LOG_NOW_OUTPUT_CUR] <= cur1 - CHK_CUR_RANGE) ||
        (LogInfo[1][EV_LOG_NOW_OUTPUT_VOL] >= vol2 + CHK_VOL_RANGE) ||
        (LogInfo[1][EV_LOG_NOW_OUTPUT_VOL] <= vol2 - CHK_VOL_RANGE) ||
        (LogInfo[1][EV_LOG_NOW_OUTPUT_CUR] >= cur2 + CHK_CUR_RANGE) ||
        (LogInfo[1][EV_LOG_NOW_OUTPUT_CUR] <= cur2 - CHK_CUR_RANGE)
    ) {
        log_info("G1->Out Vol=%.1f,Out Cur=%.1f - G2->Out Vol=%.1f,Out Cur=%.1f",
                 vol1,
                 cur1 / 10,
                 vol2,
                 cur2 / 10);

        LogInfo[0][EV_LOG_NOW_OUTPUT_VOL] = vol1;
        LogInfo[0][EV_LOG_NOW_OUTPUT_CUR] = cur1;
        LogInfo[1][EV_LOG_NOW_OUTPUT_VOL] = vol2;
        LogInfo[1][EV_LOG_NOW_OUTPUT_CUR] = cur2;
    }

    SetPresentOutputPower(vol1, cur1, vol2, cur2);
}

static void SetPresentChargingOutputPower(void)
{
    float vol1 = 0, cur1 = 0;
    float vol2 = 0, cur2 = 0;
    PcPsuOutput *pPcPsuOutput1 = NULL;
    PcPsuOutput *pPcPsuOutput2 = NULL;
    struct ChargingInfoData *chargingData_1 = NULL;
    struct ChargingInfoData *chargingData_2 = NULL;
    bool isPsuVol1 = false, isPsuVol2 = false, isPsuCur1 = false, isPsuCur2 = false;

    if (pSysConfig->TotalConnectorCount == 1) {
        pPcPsuOutput1 = (PcPsuOutput *)&ShmDcCommonData->PcPsuOutput[0];
        pPcPsuOutput2 = (PcPsuOutput *)&ShmDcCommonData->PcPsuOutput[0];
        chargingData_1 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
        chargingData_2 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
    } else if (pSysConfig->TotalConnectorCount == 2) {
        pPcPsuOutput1 = (PcPsuOutput *)&ShmDcCommonData->PcPsuOutput[0];
        pPcPsuOutput2 = (PcPsuOutput *)&ShmDcCommonData->PcPsuOutput[1];
        chargingData_1 = (struct ChargingInfoData *)GetDcChargingInfoData(0);
        chargingData_2 = (struct ChargingInfoData *)GetDcChargingInfoData(1);
    }

    psuOutputReady[0] = chargingData_1->SystemStatus != S_CHARGING ? false : psuOutputReady[0];
    psuOutputReady[1] = chargingData_2->SystemStatus != S_CHARGING ? false : psuOutputReady[1];

    isPsuVol1 = chargingData_1->PantographFlag ? true : (pPcPsuOutput1->Voltage != 0 && psuOutputReady[0] == true);
    isPsuVol2 = chargingData_2->PantographFlag ? true : (pPcPsuOutput2->Voltage != 0 && psuOutputReady[1] == true);
    isPsuCur1 = chargingData_1->PantographFlag ? true : false;
    isPsuCur2 = chargingData_2->PantographFlag ? true : false;

    //vol1 = chargingData_1->FireChargingVoltage;
    vol1 = isPsuVol1 == false ? chargingData_1->FireChargingVoltage : (((float)pPcPsuOutput1->Voltage));
    cur1 = isPsuCur1 == false ? (chargingData_1->PresentChargingCurrent * 10) : pPcPsuOutput1->Current;
    //vol2 = chargingData_2->FireChargingVoltage;
    vol2 = isPsuVol2 == false ? chargingData_2->FireChargingVoltage : (((float)pPcPsuOutput2->Voltage));
    cur2 = isPsuCur2 == false ? (chargingData_2->PresentChargingCurrent * 10) : pPcPsuOutput2->Current;

    //DS60-120 add
    if ((LogInfo[0][EV_LOG_NOW_OUTPUT_VOL] >= vol1 + CHK_VOL_RANGE) ||
            (LogInfo[0][EV_LOG_NOW_OUTPUT_VOL] <= vol1 - CHK_VOL_RANGE) ||
            (LogInfo[0][EV_LOG_NOW_OUTPUT_CUR] >= cur1 + CHK_CUR_RANGE) ||
            (LogInfo[0][EV_LOG_NOW_OUTPUT_CUR] <= cur1 - CHK_CUR_RANGE) ||
            (LogInfo[1][EV_LOG_NOW_OUTPUT_VOL] >= vol2 + CHK_VOL_RANGE) ||
            (LogInfo[1][EV_LOG_NOW_OUTPUT_VOL] <= vol2 - CHK_VOL_RANGE) ||
            (LogInfo[1][EV_LOG_NOW_OUTPUT_CUR] >= cur2 + CHK_CUR_RANGE) ||
            (LogInfo[1][EV_LOG_NOW_OUTPUT_CUR] <= cur2 - CHK_CUR_RANGE)
       ) {
        log_info("G1-> Vol=%.1f, Cur=%.1f - G2-> Vol = %.1f, Cur = %.1f",
            vol1 / 10,
            cur1 / 10,
            vol2 / 10,
            cur2 / 10);

        LogInfo[0][EV_LOG_NOW_OUTPUT_VOL] = vol1;
        LogInfo[0][EV_LOG_NOW_OUTPUT_CUR] = cur1;
        LogInfo[1][EV_LOG_NOW_OUTPUT_VOL] = vol2;
        LogInfo[1][EV_LOG_NOW_OUTPUT_CUR] = cur2;
    }
    //if (_outVol_1 != vol1 ||
    //        _outCur_1 != cur1 ||
    //        _outVol_2 != vol2 ||
    //        _outCur_2 != cur2) {
    /*log_info("G1 -> Output Vol = %f, Output Cur = %f -- G2 -> Output Vol = %f, Output Cur = %f ",
        vol1, cur1, vol2, cur2);
    */
    //    _outVol_1 = vol1; _outCur_1 = cur1; _outVol_2 = vol2; _outCur_2 = cur2;
    //}

    SetPresentOutputPower(vol1, cur1, vol2, cur2);
}

static void checkConnectorOVPState(uint8_t gunIndex)
{
    struct ChargingInfoData *pDcChargingInfo = (struct ChargingInfoData *)GetDcChargingInfoData(gunIndex);
    // 避免槍溫偵測誤判
    static uint8_t gunTempAllowCount[2] = {0};

    bool isOTP = false;

    switch (pDcChargingInfo->Type) {
    case _Type_Chademo:
        if (ShmDcCommonData->ConnectErrList[gunIndex].GunBits.ChaConnectOTP) {
            isOTP = true;
        }
        break;

    case _Type_CCS_2:
        if (ShmDcCommonData->ConnectErrList[gunIndex].GunBits.CCSConnectOTP) {
            isOTP = true;
        }
        break;

    case _Type_GB:
        if (ShmDcCommonData->ConnectErrList[gunIndex].GunBits.GBTConnectOTP) {
            isOTP = true;
        }
        break;
    }

    if (ShmDcCommonData->ChillerTempErr[gunIndex].StatusBit.ChillerOTP == YES) {
        isOTP = true;
    }

    if (isOTP) {
        if (gunTempAllowCount[gunIndex] >= 2) {
            pDcChargingInfo->StopChargeFlag = YES;
        } else {
            gunTempAllowCount[gunIndex] += 1;
        }
    } else {
        gunTempAllowCount[gunIndex] = 0;
    }
}

static time_t GetRtcInfoForEpoch(void)
{
    struct timeb csuTime;
    struct tm *tmCSU;
    struct tm t;
    time_t result;

    ftime(&csuTime);
    tmCSU = localtime(&csuTime.time);

    t.tm_year = tmCSU->tm_year;
    t.tm_mon = tmCSU->tm_mon;
    t.tm_mday = tmCSU->tm_mday;
    t.tm_hour = tmCSU->tm_hour;
    t.tm_min = tmCSU->tm_min;
    t.tm_sec = tmCSU->tm_sec;
    t.tm_isdst = -1;
    result = mktime(&t);

    return result;
}

static void FormatVoltageAndCurrent(void)
{
    uint8_t gunIndex = 0;
    ParsingRatedCur parsingRatedCur = {0};
    RateCurInfo *pRatedCurInfo = NULL;

    if (RatedCurrentParsing((char *)pSysConfig->ModelName, &parsingRatedCur) != PASS) {
        log_error("Parsing rated current failed");
        return;
    }

    maxChargingPow = parsingRatedCur.Power;

    for (gunIndex = 0; gunIndex < pSysConfig->TotalConnectorCount; gunIndex++) {
        pRatedCurInfo = (RateCurInfo *)&parsingRatedCur.ParsingInfo[gunIndex];

        maxChargingVol[gunIndex] = pRatedCurInfo->Voltage;
        maxChargingCur[gunIndex] = pRatedCurInfo->Current;

        log_info("Conn %d GunType = %d, MaxVol = %f, MaxCur = %f ",
                 gunIndex,
                 pRatedCurInfo->GunType,
                 maxChargingVol[gunIndex],
                 maxChargingCur[gunIndex]);
    }
}

static int DiffTimeb(struct timeb ST, struct timeb ET)
{
    //return milli-second
    unsigned int StartTime, StopTime;

    StartTime = (unsigned int)ST.time;
    StopTime = (unsigned int)ET.time;

    return (StopTime - StartTime) * 1000 + ET.millitm - ST.millitm;
}

int main(int argc, char *argv[])
{
    bool chkChademoPermission[2] = {false};
    int isContinue = 1;
    uint8_t gunIndex = 0;
    uint8_t typeIndex = 0;
    uint8_t priorityLow = 1;
    uint8_t SendErrorCount[2] = {0, 0};
    uint8_t gfgResult = 0;
    uint32_t _timeBuf = 0;
    uint32_t chargingTime[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY] = {0};
    float maxVol, maxCur;
    struct timeval _chk_ratingPower_timeout[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY];
    struct timeval _chk_chademo_permission_timeout[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY];
    time_t rtc = {0};
    struct ChargingInfoData *pDcChargingInfo = NULL;
    struct timeb waitChargingTime;
    struct timeb nowTime;
    uint8_t Comcont = 0;
    uint8_t evstatus;

    if (CreateAllCsuShareMemory() == FAIL) {
        log_error("create share memory error");
        return FAIL;
    }

    MappingGunChargingInfo("EvComm Task");

    pSysConfig = (struct SysConfigData *)GetShmSysConfigData();
    pSysInfo = (struct SysInfoData *)GetShmSysInfoData();
    pAlarmCode = (struct AlarmCodeData *)GetShmAlarmCodeData();
    pFaultCode = (struct FaultCodeData *)GetShmFaultCodeData();
    ShmDcCommonData = (DcCommonInfo *)GetShmDcCommonData();
    ShmCHAdeMOData = (struct CHAdeMOData *)GetShmCHAdeMOData();
    ShmGBTData = (struct GBTData *)GetShmGBTData();
    ShmCcsData = (struct CcsData *)GetShmCcsData();
    ShmSelectGunInfo = (SelectGunInfo *)GetShmSelectGunInfo();

    CanFd = InitCanBus();

    FormatVoltageAndCurrent();

    signal(SIGCHLD,SIG_IGN);
    
    CANReceiver(CanFd);

    rtc = GetRtcInfoForEpoch();

    while (isContinue) {
        for (gunIndex = 0; gunIndex < pSysConfig->TotalConnectorCount; gunIndex++) {
            pDcChargingInfo = (struct ChargingInfoData *)GetDcChargingInfoData(gunIndex);
            typeIndex = pDcChargingInfo->type_index;

            if (priorityLow == 1) {
                // 優先權較低 - 只要有回應即不會再詢問
                if (pDcChargingInfo->Type == _Type_Chademo &&
                        ShmCHAdeMOData->evse[typeIndex].SelfTest_Comp != PASS) {
                    SyncRtcInfo(gunIndex, pDcChargingInfo->Evboard_id, (int)rtc);
                    GetFirmwareVersion(gunIndex, pDcChargingInfo->Evboard_id);
                } else if (pDcChargingInfo->Type == _Type_GB &&
                           ShmGBTData->evse[typeIndex].SelfTest_Comp != PASS) {
                    SyncRtcInfo(gunIndex, pDcChargingInfo->Evboard_id, (int)rtc);
                    GetFirmwareVersion(gunIndex, pDcChargingInfo->Evboard_id);
                } else if (pDcChargingInfo->Type == _Type_CCS_2) {
                    if (ShmCcsData->CommProtocol == _CCS_COMM_V2GMessage_DIN70121 &&
                            ShmCcsData->V2GMessage_DIN70121[typeIndex].SelfTest_Comp != PASS) {
                        SyncRtcInfo(gunIndex, pDcChargingInfo->Evboard_id, (int)rtc);
                        GetFirmwareVersion(gunIndex, pDcChargingInfo->Evboard_id);
                    }
                }
                /*
                if (pDcChargingInfo->Type == _Type_Chademo) {
                    pAlarmCode->AlarmEvents.bits.ChademoboardStestFail = 
                        (ShmCHAdeMOData->evse[typeIndex].SelfTest_Comp != PASS) ? true : false;
                    pAlarmCode->AlarmEvents.bits.ChademoModuleCommFail =
                        ( CanFd < 0 ) ? true : false;
                } else if (pDcChargingInfo->Type == _Type_GB) {
                    pAlarmCode->AlarmEvents.bits.GbtboardStestFail = 
                        (ShmGBTData->evse[typeIndex].SelfTest_Comp != PASS) ? true : false;
                    pAlarmCode->AlarmEvents.bits.mFail =
                        ( CanFd < 0 ) ? true : false;
                } else if (pDcChargingInfo->Type == _Type_CCS_2) {
                    pAlarmCode->AlarmEvents.bits.CCSboardStestFail = 
                        (ShmCcsData->V2GMessage_DIN70121[typeIndex].SelfTest_Comp != PASS) ? true : false;
                    pAlarmCode->AlarmEvents.bits.ChademoModuleCommFail =
                        ( CanFd < 0 ) ? true : false;
                        
                }*/
                //固定要取得的資訊 : 1.槍鎖狀態, 2."Connector 1" 溫度, 3."Connector 2" 溫度, 4.Pilot Voltage
                //log_info("GetMiscellaneousInfo. index = %d, Eid = %d ",
                //         gunIndex,
                //         pDcChargingInfo->Evboard_id);
                GetMiscellaneousInfo(gunIndex,
                                     pDcChargingInfo->RelayK1K2Status,
                                     pDcChargingInfo->PresentChargedEnergy,
                                     (pDcChargingInfo->PresentChargingVoltage * 10),
                                     pDcChargingInfo->Evboard_id);

                //checkConnectorOVPState(gunIndex);
            }

            switch (pDcChargingInfo->SystemStatus) {
            case S_IDLE:
            case S_RESERVATION:
                if (pDcChargingInfo->Type == _Type_Chademo) {

                    ClearAbnormalStatus_Chademo(gunIndex);
                    if (pSysInfo->PageIndex == _LCM_WAIT_FOR_PLUG) {
                        if (!chkChademoPermission[gunIndex]) {
                            chkChademoPermission[gunIndex] = true;
                            gettimeofday(&_chk_chademo_permission_timeout[gunIndex], NULL);
                            SendCommunicationOnly(gunIndex);
                        } else {
                            _timeBuf = GetTimeoutValue(_chk_chademo_permission_timeout[gunIndex]);

                            if (_timeBuf < 0) {
                                gettimeofday(&_chk_chademo_permission_timeout[gunIndex], NULL);
                            } else {
                                if (_timeBuf / 1000 > 10000) {
                                    SendCommunicationOnly(gunIndex);
                                    gettimeofday(&_chk_chademo_permission_timeout[gunIndex], NULL);
                                }
                            }
                        }
                    } else if (chkChademoPermission[gunIndex]) {
                        chkChademoPermission[gunIndex] = false;
                        SendStopOnly(gunIndex);
                    }

                    if (ShmDcCommonData->ConnectErrList[gunIndex].GunBits.ChaConnectOTP == NO) {
                        pDcChargingInfo->StopChargeFlag = NO;
                    }
                } else if (pDcChargingInfo->Type == _Type_GB) {
                    ClearAbnormalStatus_GB(gunIndex);

                    if (ShmDcCommonData->ConnectErrList[gunIndex].GunBits.GBTConnectOTP == NO) {
                        pDcChargingInfo->StopChargeFlag = NO;
                    }
                } else if (pDcChargingInfo->Type == _Type_CCS_2) {
                    ClearAbnormalStatus_CCS(gunIndex);
                    if (ShmDcCommonData->ConnectErrList[gunIndex].GunBits.CCSConnectOTP == NO) {
                        pDcChargingInfo->StopChargeFlag = NO;
                    }
                }
                // Set Ev board in communication mode
                // Get EVCCID for authorize when gun plug-in only for CCS
                if ( pDcChargingInfo->ConnectorPlugIn && pSysConfig->isAuthrizeByEVCCID &&
                        ShmCcsData->CommProtocol == _CCS_COMM_V2GMessage_DIN70121) {
                    if (Comcont == 1 && evstatus < 15) {
                        GetEVCCIDReq(gunIndex,pDcChargingInfo->Evboard_id);
                        SendCommunicationOnly(gunIndex);
                    } 
                }
                if (ShmCcsData->CommProtocol == _CCS_COMM_V2GMessage_DIN70121 && priorityLow == 1) {
                    GetOutputReq(gunIndex,pDcChargingInfo->Evboard_id);
                    if (evstatus != ShmCcsData->V2GMessage_DIN70121[pDcChargingInfo->type_index].PresentMsgFlowStatus) {
                        evstatus = ShmCcsData->V2GMessage_DIN70121[pDcChargingInfo->type_index].PresentMsgFlowStatus;
                    }
                    if ( evstatus > 19 && evstatus < 255 )
                        GetEVCCIDReq(gunIndex,pDcChargingInfo->Evboard_id);
                }
                if (priorityLow == 1) {
                    pDcChargingInfo->PresentChargedEnergy   = 0;
                    pDcChargingInfo->PresentChargingPower   = 0;
                    pDcChargingInfo->GroundFaultStatus      = GFD_WAIT;
                    pDcChargingInfo->RealRatingPower        = 0;
                    pDcChargingInfo->StopChargeFlag         = NO;
                    pDcChargingInfo->NormalStopChargeFlag   = NO;//DS60-120 add
                    pDcChargingInfo->ChargingFee            = 0.0;
                    pDcChargingInfo->EvBatterySoc           = 0;
                    pDcChargingInfo->EvBatteryStartSoc      = 0; //DS60-120 add
                    pDcChargingInfo->EvBatteryMaxVoltage    = 0; //DS60-120 add
                    pDcChargingInfo->ChargingProfilePower   = -1; //DS60-120 add
                    pDcChargingInfo->ChargingProfileCurrent = -1; //DS60-120 add

                    if (pSysInfo->MainChargingMode == _MAIN_CHARGING_MODE_MAX) { //DS60-120 add
                        pDcChargingInfo->PresentChargingVoltage = 0;
                        pDcChargingInfo->PresentChargingCurrent = 0;
                        pDcChargingInfo->EvBatteryMaxVoltage = 0;
                    }

                    chargingTime[gunIndex] = 0;
                    //maxChargingCur[gunIndex] = pSysConfig->MaxChargingCurrent * 10;
                    //maxChargingPow = (pSysConfig->MaxChargingPower * 10);
                    //DS60-120 add
                    SendErrorCount[gunIndex] = 0;

                    //maxChargingPow = pSysConfig->MaxChargingPower * 10;
                    // ShmPsuData->SystemAvailablePower 已是 * 10
                    //maxChargingPow = ShmPsuData->SystemAvailablePower;
                    if (pSysConfig->MaxChargingPower * 10 != 0 &&
                            pSysConfig->MaxChargingPower * 10 < maxChargingPow) {
                        maxChargingPow = pSysConfig->MaxChargingPower * 10;
                    }

                    LogInfo[gunIndex][EV_LOG_EVSE_MAX_VOL] = 0;
                    LogInfo[gunIndex][EV_LOG_EVSE_MAX_CUR] = 0;
                    LogInfo[gunIndex][EV_LOG_MAX_BATT_VOL] = 0;
                    LogInfo[gunIndex][EV_LOG_SOC] = 0;

                    SetPresentChargingOutputPower();
                }
                break;

            case S_PREPARNING:
                chkChademoPermission[gunIndex] = false; //DS60-120 add
                // 設定當前輸出
                SetPresentChargingOutputPower();

                pDcChargingInfo->PowerConsumption = 0;
                break;

            case S_PREPARING_FOR_EV:
                // 開始確認車端是否同意開始充電 : 1.SOC, 2.Target Vol, 3.Target Cur, 4.Charging remaining time
                GetOutputReq(gunIndex, pDcChargingInfo->Evboard_id);

                //log_info("PresentChargingVoltage = %f ", pDcChargingInfo->PresentChargingVoltage);
                //log_info("PresentChargingCurrent = %f ", pDcChargingInfo->PresentChargingCurrent);
                //log_info("AvailableChargingPower = %f ", pDcChargingInfo->AvailableChargingPower);
                //log_info("AvailableChargingCurrent = %f ", pDcChargingInfo->AvailableChargingCurrent);
                //log_info("MaximumChargingVoltage = %f ", pDcChargingInfo->MaximumChargingVoltage);

                // 設定當前輸出
                SetPresentChargingOutputPower();

                if (ShmSelectGunInfo->WaitDoCommPermission[gunIndex] == YES) {
                    ShmSelectGunInfo->WaitDoCommPermission[gunIndex] = NO;
                    //if (priorityLow == 1) {
                    // 樁端輸出能力
                    maxVol = pDcChargingInfo->MaximumChargingVoltage;
                    maxCur = pDcChargingInfo->AvailableChargingCurrent;

                    GetMaxVolAndCurMethod(gunIndex, &maxVol, &maxCur);

                    //DS60-120 add
                    if (LogInfo[gunIndex][EV_LOG_EVSE_MAX_VOL] != maxVol ||
                            LogInfo[gunIndex][EV_LOG_EVSE_MAX_CUR] != maxCur) {
                        LogInfo[gunIndex][EV_LOG_EVSE_MAX_VOL] = maxVol;
                        LogInfo[gunIndex][EV_LOG_EVSE_MAX_CUR] = maxCur;

                        log_info("To EV_%d Max_Vol = %.1f, Cap_Cur = %.1f, Cap_Pow = %.1f",
                                 gunIndex,
                                 maxVol / 10,
                                 maxCur / 10,
                                 pDcChargingInfo->AvailableChargingPower / 10);
                    }

                    pDcChargingInfo->RealMaxVoltage = maxVol;

                    SetChargingPermission(gunIndex,
                                          START,
                                          pDcChargingInfo->AvailableChargingPower,
                                          maxCur,
                                          maxVol,
                                          pDcChargingInfo->Evboard_id);

                    // 取得車端電池資訊 : 1.AC or DC ? 2.Total battery cap, 3.Max battery vol, 4.Max battery cur
                    GetEvBatteryInfo(gunIndex, pDcChargingInfo->Evboard_id);
                }

                gettimeofday(&_chk_ratingPower_timeout[gunIndex], NULL);
                break;

            case S_PREPARING_FOR_EVSE:
            case S_CCS_PRECHARGE_ST0:
            case S_CCS_PRECHARGE_ST1:
                // 開始確認車端是否同意開始充電
                GetOutputReq(gunIndex, pDcChargingInfo->Evboard_id);

                // 設定當前輸出
                SetPresentChargingOutputPower();

                if (priorityLow % 5 == 1) {
                    // 取得車端電池資訊 : 1.AC or DC ? 2.Total battery cap, 3.Max battery vol, 4.Max battery cur
                    GetEvBatteryInfo(gunIndex, pDcChargingInfo->Evboard_id); //DS60-120 add

                    // 樁端輸出能力改變
                    SetPresentChargingOutputCap();
                }

                //DS60-120 add
                if (LogInfo[gunIndex][EV_LOG_MAX_BATT_VOL] != pDcChargingInfo->EvBatteryMaxVoltage) {
                    LogInfo[gunIndex][EV_LOG_MAX_BATT_VOL] = pDcChargingInfo->EvBatteryMaxVoltage;
                    log_info("index = %d, Ev Maximum Battery Voltage = %f ",
                             gunIndex,
                             pDcChargingInfo->EvBatteryMaxVoltage);
                }

                if (LogInfo[gunIndex][EV_LOG_SOC] != pDcChargingInfo->EvBatterySoc) {
                    LogInfo[gunIndex][EV_LOG_SOC] = pDcChargingInfo->EvBatterySoc;
                    log_info("index = %d, SOC = %d ",
                             gunIndex,
                             pDcChargingInfo->EvBatterySoc);
                }

                // 持續通知 Isolation 測試狀態
                if (priorityLow == 1) {
                    // 拉 500 V 如果在一秒鐘內 GFD 都符合則 PASS
//                      if (_chargingData[_index]->FireChargingVoltage >= 3500)
//                         pDcChargingInfo->GroundFaultStatus = GFD_PASS;

                    //log_info("To EV_%d GFD = %d ",   _index,pDcChargingInfo->GroundFaultStatus);
                    //if(_chargingData[_index]->GroundFaultStatus != GFD_WAIT)
                    {
                        //if ((GetTimeoutValue(_derating_time) / 1000) > 1000)
                        gfgResult = pDcChargingInfo->GroundFaultStatus;

                        // GB & Chademo ~ Warning 也先算 Pass,因為 CCS 認證會驗 Warning 故不可更動
                        if (pDcChargingInfo->Type == _Type_Chademo ||
                                pDcChargingInfo->Type == _Type_GB) {
                            if (gfgResult == GFD_WARNING) {
                                gfgResult = GFD_PASS;
                            }
                        }

                        if (gfgResult == GFD_WARNING || gfgResult == GFD_PASS) {
                            if (((GetTimeoutValue(_chk_ratingPower_timeout[gunIndex]) / 1000) > 12000 &&
                                    pDcChargingInfo->RealRatingPower > 0) ||
                                    (GetTimeoutValue(_chk_ratingPower_timeout[gunIndex]) / 1000) > 14000) {
                                //log_info("**********EvComm : gunIndex= %d, RealRatingPower = %d ",
                                //            gunIndex,pDcChargingInfo->RealRatingPower);
                                //gfgResult = GFD_PASS;

                                //DS60-120 add
                                if (LogInfo[gunIndex][EV_LOG_REAL_CAP_POW] != pDcChargingInfo->RealRatingPower) {
                                    LogInfo[gunIndex][EV_LOG_REAL_CAP_POW] = pDcChargingInfo->RealRatingPower;
                                    log_info("Conn %d, RealRatingPower = %d ",
                                             gunIndex,
                                             pDcChargingInfo->RealRatingPower);

                                }
                            } else {
                                gfgResult = GFD_WAIT;
                            }
                        }

                        SetIsolationStatus(gunIndex, gfgResult, pDcChargingInfo->Evboard_id);
                    }

                    if (pDcChargingInfo->SystemStatus == S_CCS_PRECHARGE_ST0 &&
                            pDcChargingInfo->PrechargeStatus == PRECHARGE_READY
                       ) {
                        SetEvsePrechargeInfo(gunIndex, PRECHARGE_PRERELAY_PASS, pDcChargingInfo->Evboard_id);
                    }
                }
                ftime(&waitChargingTime);
                break;

            case S_CHARGING:
                //if (waitPsuVolwithRealyVol(gunIndex) == NO) {
                //    continue;
                //}
                GetEvBatteryInfo(gunIndex, pDcChargingInfo->Evboard_id); //DS60-120 add
                // 計算 Power
                pDcChargingInfo->PresentChargingPower =
                    ((float)((pDcChargingInfo->PresentChargingVoltage) *
                             (pDcChargingInfo->PresentChargingCurrent)) / 1000);

                //DS60-120 remove
                if (chargingTime[gunIndex] == 0 ||
                        chargingTime[gunIndex] > pDcChargingInfo->PresentChargedDuration) {
                    chargingTime[gunIndex] = pDcChargingInfo->PresentChargedDuration;
                } else {
                    int passTime = pDcChargingInfo->PresentChargedDuration - chargingTime[gunIndex];
                    
                    if (passTime > 0) {
                        float changingPow = (pDcChargingInfo->PresentChargingPower) * passTime / 3600;
                        if (pSysConfig->BillingData.isBilling) {
                            pDcChargingInfo->ChargingFee += changingPow * pSysConfig->BillingData.Cur_fee;
                        }

                        pDcChargingInfo->PresentChargedEnergy += changingPow;

                        ShmDcCommonData->pGunInfo[gunIndex].PowerConsumption += changingPow;
                        pDcChargingInfo->PowerConsumption += changingPow;
                        chargingTime[gunIndex] = pDcChargingInfo->PresentChargedDuration;
                    }
                    
                }

                // 開始確認車端是否同意開始充電
                GetOutputReq(gunIndex, pDcChargingInfo->Evboard_id);

                // 設定當前輸出
                ftime(&nowTime);
                if (!(DiffTimeb(waitChargingTime, nowTime) < 5000 ||
                        DiffTimeb(waitChargingTime, nowTime) < 0)) {
                    psuOutputReady[gunIndex] = true;
                }
                SetPresentChargingOutputPower();

                // for test end
                if (priorityLow % 5 == 0) {
                    // 樁端輸出能力改變
                    SetPresentChargingOutputCap();
                }

                if ((pDcChargingInfo->GroundFaultStatus == GFD_FAIL) ||
                        (pDcChargingInfo->Type == _Type_CCS_2)) {
                    SetIsolationStatus(gunIndex,
                                       pDcChargingInfo->GroundFaultStatus,
                                       pDcChargingInfo->Evboard_id);
                }
                /*
                else if (pDcChargingInfo->Type == _Type_CCS_2) {
                SetIsolationStatus(gunIndex, pDcChargingInfo->GroundFaultStatus, pDcChargingInfo->Evboard_id);
                }*/

                // GFD 失敗再通知
                if (priorityLow == 1) {
                    if (pDcChargingInfo->Type == _Type_CCS_2 &&
                            pDcChargingInfo->PrechargeStatus == PRECHARGE_READY) {
                        SetEvsePrechargeInfo(gunIndex,
                                             PRECHARGE_CHARELAY_PASS,
                                             pDcChargingInfo->Evboard_id);
                    }
                }
                break;

            case S_ALARM:
            case S_TERMINATING:
                // 設定當前輸出
                setCurrentOutput();
                SetPresentChargingOutputPower();

                // 槍鎖還在,則代表是樁端要求的停止
                if (pDcChargingInfo->GunLocked == START ||
                        pDcChargingInfo->Type == _Type_CCS_2) {
                    uint8_t normalStop = 0x01;
                    uint8_t stopReason[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

                    if (GetStopChargingReasonByEvse(gunIndex, stopReason)) {
                        normalStop = 0x02;
                    }

                    EvseStopChargingEvent(normalStop,
                                          stopReason,
                                          pDcChargingInfo->Evboard_id);

                    SendErrorCount[gunIndex] += 1; //DS60-120 add
                }

                if (pDcChargingInfo->Type == _Type_CCS_2) {
                    SetIsolationStatus(gunIndex,
                                       pDcChargingInfo->GroundFaultStatus,
                                       pDcChargingInfo->Evboard_id);
                }

                GetOutputReq(gunIndex, pDcChargingInfo->Evboard_id);

                //DS60-120 add
                if (pDcChargingInfo->SystemStatus == S_ALARM) {
                    if (priorityLow == 1) {
                        // 樁端輸出能力
                        maxVol = pDcChargingInfo->MaximumChargingVoltage;
                        maxCur = pDcChargingInfo->AvailableChargingCurrent;

                        GetMaxVolAndCurMethod(gunIndex, &maxVol, &maxCur);

                        SetChargingPermission(gunIndex,
                                              STOP,
                                              pDcChargingInfo->AvailableChargingPower,
                                              maxCur,
                                              maxVol,
                                              pDcChargingInfo->Evboard_id);
                    }
                }
                break;

            case S_COMPLETE:
                // 設定當前輸出
                SetPresentChargingOutputPower();

                if (priorityLow == 1) {
                    // 樁端輸出能力
                    maxVol = pDcChargingInfo->MaximumChargingVoltage;
                    maxCur = pDcChargingInfo->AvailableChargingCurrent;

                    GetMaxVolAndCurMethod(gunIndex, &maxVol, &maxCur);
                    SetChargingPermission(gunIndex,
                                          STOP,
                                          pDcChargingInfo->AvailableChargingPower,
                                          maxCur,
                                          maxVol,
                                          pDcChargingInfo->Evboard_id);

                    //DS60-120 add
                    //if (pDcChargingInfo->EvBatterySoc >= 100) {
                    //    //滿電,則直接清掉錯誤
                    //    if (pDcChargingInfo->Type == _Type_Chademo) {
                    //        ClearAbnormalStatus_Chademo(gunIndex);
                    //    } else if (pDcChargingInfo->Type == _Type_GB) {
                    //        ClearAbnormalStatus_GB(gunIndex);
                    //    } else if (pDcChargingInfo->Type == _Type_CCS_2) {
                    //        ClearAbnormalStatus_CCS(gunIndex);
                    //    }
                    //}
                }
                break;
            }//switch
        }//for
        Comcont >= 200 ? Comcont = 1: Comcont++;
        priorityLow >= 20 ? priorityLow = 1 : priorityLow++;
        usleep(50000);
    }//while

    return 0;
}