#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <linux/wireless.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <unistd.h>
#include <stdarg.h>
#include <stdio.h>      /*標準輸入輸出定義*/
#include <stdlib.h>     /*標準函數庫定義*/
#include <unistd.h>     /*Unix 標準函數定義*/
#include <fcntl.h>      /*檔控制定義*/
#include <termios.h>    /*PPSIX 終端控制定義*/
#include <errno.h>      /*錯誤號定義*/
#include <errno.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <ifaddrs.h>
#include <math.h>
#include <stdbool.h>

#include "../Define/define.h"
#include "Module_InternalComm.h"
#include "internalComm.h"
#include "../Config.h"

//------------------------------------------------------------------------------
struct SysConfigAndInfo         *ShmSysConfigAndInfo;
struct StatusCodeData           *ShmStatusCodeData;
struct FanModuleData            *ShmFanModuleData;
struct RelayModuleData          *ShmRelayModuleData;
struct LedModuleData            *ShmLedModuleData;
struct PsuData                  *ShmPsuData;
struct OCPP16Data               *ShmOCPP16Data;
DcCommonInfo                    *ShmDcCommonData;

uint8_t gunCount;
uint8_t acgunCount;
// 槍資訊
struct ChargingInfoData *_chargingData[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY];
struct ChargingInfoData *ac_chargingInfo[AC_QUANTITY];

bool _isOutputNoneMatch[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY];
struct timeval _checkOutputNoneMatchTimer[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY];

bool _isRelayWelding[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY];
struct timeval _checkRelayWeldingTimer[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY];

bool _isOvpChkTimeFlag[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY]; //DS60-120 add
struct timeval _checkOutputVolProtectTimer[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY]; //DS60-120 add

// SMR1 *2 + SMR2 * 2 + Parallel * 2
struct timeval _relayStateChkTimer[6]; //DS60-120 add

uint8_t _threePhaseOvp[3] = {0, 0, 0}; //DS60-120 add
uint8_t _threePhaseUvp[3] = {0, 0, 0}; //DS60-120 add

bool FindChargingInfoData(uint8_t target, struct ChargingInfoData **chargingData);

int Uart5Fd;
char *relayRs485PortName = "/dev/ttyS5";
unsigned short fanSpeedSmoothValue = 500;

bool isStopChargingCount = false;
struct timeval _close_ac_contactor;

struct timeval _priority_time;
struct timeval _led_priority_time;

struct timeval  _ac_charging_comp;
struct timeval  _ac_preparing;
struct timeb    _ac_startChargingTime;
struct timeb    _ac_endChargingTime;

unsigned short _setFanSpeed = 0;
float _beforeChargingTotalEnergy = 0.0;
uint8_t _checkLedChanged = 3;

Ver ver;
PresentInputVoltage inputVoltage;
PresentOutputVoltage outputVoltage;
FanSpeed fanSpeed;
Temperature temperature;
AuxPower auxPower;
Gfd gfd_adc;
Gfd_config gfd_config;
Gpio_in gpio_in;
Gpio_out gpio_out;
Relay outputRelay;
Relay regRelay;
Rtc rtc;
Led_Color cur_led_color;
Led_Color led_color;

Ac_Status acStatus;
Ac_Led_Status ledStatus;
Ac_Alarm_code acAlarmCode;
Ac_Charging_energy acChargingEnergy;
Ac_Charging_current acChargingCurrent;

int _alarm_code[] = {
    AC_OVP,
    AC_UVP,
    AC_OCP,
    AC_OTP,
    AC_GMI_FAULT,
    AC_CP_ERROR,
    AC_AC_LEAKAGE,
    AC_DC_LEAKAGE,
    AC_SYSTEM_SELFTEST_FAULT,
    AC_HANDSHAKE_TIMEOUT,
    AC_EMC_STOP,
    AC_RELAY_WELDING,
    AC_GF_MODULE_FAULT,
    AC_SHUTTER_FAULT,
    AC_LOCKER_FAULT,
    AC_POWER_DROP,
    AC_CIRCUIT_SHORT,
    AC_ROTARY_SWITCH_FAULT,
    AC_RELAY_DRIVE_FAULT
};

uint8_t getCommTargetID(uint8_t index)
{
    uint8_t targetID = 0;

    if (gunCount == 1) {
        if (strncmp((char *)&ShmSysConfigAndInfo->SysConfig.ModelName[7], "0", 1) != 0) {
            targetID = 0x01;
        } else if (strncmp((char *)&ShmSysConfigAndInfo->SysConfig.ModelName[9], "0", 1) != 0) {
            targetID = 0x02;
        }
    } else {
        targetID = _chargingData[index]->Evboard_id;
    }

    return targetID;
}

unsigned long 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 StoreLogMsg(const char *fmt, ...)
{
    char Buf[4096 + 256];
    char buffer[4096];
    va_list args;
    struct timeb  SeqEndTime;
    struct tm *tm;

    va_start(args, fmt);
    int rc = vsnprintf(buffer, sizeof(buffer), fmt, args);
    va_end(args);

    memset(Buf, 0, sizeof(Buf));
    ftime(&SeqEndTime);
    SeqEndTime.time = time(NULL);
    tm = localtime(&SeqEndTime.time);

    if (ShmSysConfigAndInfo->SysConfig.SwitchDebugFlag == YES) {
        sprintf(Buf, "%02d:%02d:%02d:%03d - %s",
                tm->tm_hour, tm->tm_min, tm->tm_sec, SeqEndTime.millitm, buffer);
        printf("%s \n", Buf);
    } else {
        sprintf(Buf, "echo \"%04d-%02d-%02d %02d:%02d:%02d:%03d - %s\" >> /Storage/SystemLog/[%04d.%02d]SystemLog",
                tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, SeqEndTime.millitm,
                buffer,
                tm->tm_year + 1900, tm->tm_mon + 1);
        system(Buf);
    }

    return rc;
}

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;
    return (StopTime - StartTime);
}

unsigned short MaxValue(unsigned short value1, unsigned short value2)
{
    return value1 >= value2 ? value1 : value2;
}

//==========================================
// Communication Function
//==========================================
void GetFwAndHwVersion_Fan()
{
    if (Query_FW_Ver(Uart5Fd, ADDR_FAN, &ver) == PASS) {
        // FanModuleData
        strcpy((char *) ShmFanModuleData->version, ver.Version_FW);
        // SystemInfo
        strcpy((char *) ShmSysConfigAndInfo->SysInfo.FanModuleFwRev, ver.Version_FW);
        //log_info("GetFwAndHwVersion_Fan s1 = %s \n", ver.Version_FW);
    }

    if (Query_HW_Ver(Uart5Fd, ADDR_FAN, &ver) == PASS) {
        // SystemInfo
        strcpy((char *) ShmSysConfigAndInfo->SysInfo.FanModuleHwRev, ver.Version_FW);
        //log_info("GetFwAndHwVersion_Fan s2 = %s \n", ver.Version_HW);
    }
}

void GetFwAndHwVersion_Relay()
{
    if (Query_FW_Ver(Uart5Fd, ADDR_RELAY, &ver) == PASS) {
        // RelayModuleData
        strcpy((char *) ShmRelayModuleData->version, ver.Version_FW);
        // SystemInfo
        strcpy((char *) ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev, ver.Version_FW);
        //log_info("GetFwAndHwVersion_Relay s1 = %s \n", ver.Version_FW);
    }

    if (Query_HW_Ver(Uart5Fd, ADDR_RELAY, &ver) == PASS) {
        // SystemInfo
        strcpy((char *) ShmSysConfigAndInfo->SysInfo.RelayModuleHwRev, ver.Version_FW);
        //log_info("GetFwAndHwVersion_Relay s2 = %s \n", ver.Version_HW);
    }
}

void GetFwAndHwVersion_Led()
{
    if (Query_FW_Ver(Uart5Fd, ADDR_LED, &ver) == PASS) {
        // LedModuleData
        strcpy((char *) ShmLedModuleData->version, ver.Version_FW);
        // SystemInfo
        strcpy((char *) ShmSysConfigAndInfo->SysInfo.LedModuleFwRev, ver.Version_FW);
        //log_info("GetFwAndHwVersion_Led s1 = %s \n", ver.Version_FW);
        ShmLedModuleData->SelfTest_Comp = YES;
    } else {
        //log_info("GetFwAndHwVersion_Led fail \n");
    }

//  if (Query_HW_Ver(Uart5Fd, ADDR_LED, &ver) == PASS)
//  {
//      // SystemInfo
//      strcpy((char *) ShmSysConfigAndInfo->SysInfo.RelayModuleHwRev, ver.Version_FW);
//      //log_info("GetFwAndHwVersion_Relay s2 = %s \n", ver.Version_HW);
//  }
}

void GetFwVersion_AC()
{
    if (Query_FW_Ver(Uart5Fd, ADDR_AC_PLUG, &ver) == PASS) {
        ac_chargingInfo[0]->SelfTest_Comp = YES;
        strcpy((char *) ac_chargingInfo[0]->version, ver.Version_FW);
    }
}

void GetAcModelName()
{
    memset(ShmSysConfigAndInfo->SysConfig.AcModelName, 0x00, sizeof(ShmSysConfigAndInfo->SysConfig.AcModelName));
    if (Query_Model_Name(Uart5Fd, ADDR_AC_PLUG, ShmSysConfigAndInfo->SysConfig.AcModelName) == PASS) {
        log_info("ac model name = %s \n", ShmSysConfigAndInfo->SysConfig.AcModelName);
    }
}

void SetRtcData_Relay()
{
    struct timeb csuTime;
    struct tm *tmCSU;

    ftime(&csuTime);
    tmCSU = localtime(&csuTime.time);
    //  log_info("Time : %04d-%02d-%02d %02d:%02d:%02d \n", tmCSU->tm_year + 1900,
    //          tmCSU->tm_mon + 1, tmCSU->tm_mday, tmCSU->tm_hour, tmCSU->tm_min,
    //          tmCSU->tm_sec);

    rtc.RtcData[0] = '0' + (tmCSU->tm_year + 1900) / 1000 % 10;
    rtc.RtcData[1] = '0' + (tmCSU->tm_year + 1900) / 100 % 10;
    rtc.RtcData[2] = '0' + (tmCSU->tm_year + 1900) / 10 % 10;
    rtc.RtcData[3] = '0' + (tmCSU->tm_year + 1900) / 1 % 10;

    rtc.RtcData[4] = '0' + (tmCSU->tm_mon + 1) / 10 % 10;
    rtc.RtcData[5] = '0' + (tmCSU->tm_mon + 1) / 1 % 10;

    rtc.RtcData[6] = '0' + (tmCSU->tm_mday) / 10 % 10;
    rtc.RtcData[7] = '0' + (tmCSU->tm_mday) / 1 % 10;

    rtc.RtcData[8] = '0' + (tmCSU->tm_hour) / 10 % 10;
    rtc.RtcData[9] = '0' + (tmCSU->tm_hour) / 1 % 10;

    rtc.RtcData[10] = '0' + (tmCSU->tm_min) / 10 % 10;
    rtc.RtcData[11] = '0' + (tmCSU->tm_min) / 1 % 10;

    rtc.RtcData[12] = '0' + (tmCSU->tm_sec) / 10 % 10;
    rtc.RtcData[13] = '0' + (tmCSU->tm_sec) / 1 % 10;

    if (Config_Rtc_Data(Uart5Fd, ADDR_RELAY, &rtc) == PASS) {
        //log_info("SetRtc (RB) sucessfully. \n");
    }
}

void SetModelName_Relay()
{
    if (Config_Model_Name(Uart5Fd, ADDR_RELAY, ShmSysConfigAndInfo->SysConfig.ModelName) == PASS) {
        //log_info("Set Model name (RB) PASS = %s \n", ShmSysConfigAndInfo->SysConfig.ModelName);
    }
}

void SetRtcData_Fan()
{
    struct timeb csuTime;
    struct tm *tmCSU;

    ftime(&csuTime);
    tmCSU = localtime(&csuTime.time);
    //  log_info("Time : %04d-%02d-%02d %02d:%02d:%02d \n", tmCSU->tm_year + 1900,
    //          tmCSU->tm_mon + 1, tmCSU->tm_mday, tmCSU->tm_hour, tmCSU->tm_min,
    //          tmCSU->tm_sec);

    rtc.RtcData[0] = '0' + (tmCSU->tm_year + 1900) / 1000 % 10;
    rtc.RtcData[1] = '0' + (tmCSU->tm_year + 1900) / 100 % 10;
    rtc.RtcData[2] = '0' + (tmCSU->tm_year + 1900) / 10 % 10;
    rtc.RtcData[3] = '0' + (tmCSU->tm_year + 1900) / 1 % 10;

    rtc.RtcData[4] = '0' + (tmCSU->tm_mon + 1) / 10 % 10;
    rtc.RtcData[5] = '0' + (tmCSU->tm_mon + 1) / 1 % 10;

    rtc.RtcData[6] = '0' + (tmCSU->tm_mday) / 10 % 10;
    rtc.RtcData[7] = '0' + (tmCSU->tm_mday) / 1 % 10;

    rtc.RtcData[8] = '0' + (tmCSU->tm_hour) / 10 % 10;
    rtc.RtcData[9] = '0' + (tmCSU->tm_hour) / 1 % 10;

    rtc.RtcData[10] = '0' + (tmCSU->tm_min) / 10 % 10;
    rtc.RtcData[11] = '0' + (tmCSU->tm_min) / 1 % 10;

    rtc.RtcData[12] = '0' + (tmCSU->tm_sec) / 10 % 10;
    rtc.RtcData[13] = '0' + (tmCSU->tm_sec) / 1 % 10;

    if (Config_Rtc_Data(Uart5Fd, ADDR_FAN, &rtc) == PASS) {
        //log_info("SetRtc (FB) sucessfully. \n");
    }
}

void SetModelName_Fan()
{
    if (Config_Model_Name(Uart5Fd, ADDR_FAN, ShmSysConfigAndInfo->SysConfig.ModelName) == PASS) {
        log_info("Set Model name PASS = %s \n", ShmSysConfigAndInfo->SysConfig.ModelName);
    }
}

// AC 三相輸入電壓
void GetPresentInputVol(void)
{
    if (Query_Present_InputVoltage(Uart5Fd, ADDR_RELAY, &inputVoltage) == PASS) {
        // resolution : 0.1
        ShmSysConfigAndInfo->SysInfo.InputVoltageR = ShmRelayModuleData->InputL1Volt = inputVoltage.L1N_L12;
        ShmSysConfigAndInfo->SysInfo.InputVoltageS = ShmRelayModuleData->InputL2Volt = inputVoltage.L2N_L23;
        ShmSysConfigAndInfo->SysInfo.InputVoltageT = ShmRelayModuleData->InputL3Volt = inputVoltage.L3N_L31;

        //********************************************************************************************************//
        // Vin (UVP)
        if (ShmSysConfigAndInfo->SysInfo.ChargerType == _CHARGER_TYPE_IEC) {
            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputUVP == NO) {
                if (inputVoltage.L1N_L12 < VIN_MIN_VOLTAGE_IEC) {
                    log_info("In Uvp L1N_L12 = %f \n", inputVoltage.L1N_L12);
                    if (_threePhaseUvp[0] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputUVP = YES;
                    } else {
                        _threePhaseUvp[0] += 1;
                    }
                }
            } else {
                if (inputVoltage.L1N_L12 > VIN_MIN_REV_VOLTAGE_IEC) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputUVP = NO;
                    _threePhaseUvp[0] = 0;
                }
            }

            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputUVP == NO) {
                if (inputVoltage.L2N_L23 < VIN_MIN_VOLTAGE_IEC) {
                    log_info("In Uvp L2N_L23 = %f \n", inputVoltage.L2N_L23);
                    if (_threePhaseUvp[1] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputUVP = YES;
                    } else {
                        _threePhaseUvp[1] += 1;
                    }
                }
            } else {
                if (inputVoltage.L2N_L23 > VIN_MIN_REV_VOLTAGE_IEC) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputUVP = NO;
                    _threePhaseUvp[1] = 0;
                }
            }

            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputUVP == NO) {
                if (inputVoltage.L3N_L31 < VIN_MIN_VOLTAGE_IEC) {
                    log_info("In Uvp L3N_L31 = %f \n", inputVoltage.L3N_L31);
                    if (_threePhaseUvp[2] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputUVP = YES;
                    } else {
                        _threePhaseUvp[2] += 1;
                    }
                }
            } else {
                if (inputVoltage.L3N_L31 > VIN_MIN_REV_VOLTAGE_IEC) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputUVP = NO;
                    _threePhaseUvp[2] = 0;
                }
            }
        } else if (ShmSysConfigAndInfo->SysInfo.ChargerType == _CHARGER_TYPE_UL) {
            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputUVP == NO) {
                if (inputVoltage.L1N_L12 < VIN_MIN_VOLTAGE_UL) {
                    log_info("In Uvp L1N_L12 = %f \n", inputVoltage.L1N_L12);
                    if (_threePhaseUvp[0] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputUVP = YES;
                    } else {
                        _threePhaseUvp[0] += 1;
                    }
                }
            } else {
                if (inputVoltage.L1N_L12 > VIN_MIN_REV_VOLTAGE_UL) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputUVP = NO;
                    _threePhaseUvp[0] = 0;
                }
            }

            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputUVP == NO) {
                if (inputVoltage.L2N_L23 < VIN_MIN_VOLTAGE_UL) {
                    log_info("In Uvp L2N_L23 = %f \n", inputVoltage.L2N_L23);
                    if (_threePhaseUvp[1] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputUVP = YES;
                    } else {
                        _threePhaseUvp[1] += 1;
                    }
                }
            } else {
                if (inputVoltage.L2N_L23 > VIN_MIN_REV_VOLTAGE_UL) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputUVP = NO;
                    _threePhaseUvp[1] = 0;
                }
            }

            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputUVP == NO) {
                if (inputVoltage.L3N_L31 < VIN_MIN_VOLTAGE_UL) {
                    log_info("In Uvp L3N_L31 = %f \n", inputVoltage.L3N_L31);
                    if (_threePhaseUvp[2] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputUVP = YES;
                    } else {
                        _threePhaseUvp[2] += 1;
                    }
                }
            } else {
                if (inputVoltage.L3N_L31 > VIN_MIN_REV_VOLTAGE_UL) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputUVP = NO;
                    _threePhaseUvp[2] = 0;
                }
            }
        }

        //********************************************************************************************************//
        // Vin (OVP)
        if (ShmSysConfigAndInfo->SysInfo.ChargerType == _CHARGER_TYPE_IEC) {
            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputOVP == NO) {
                if (inputVoltage.L1N_L12 > VIN_MAX_VOLTAGE_IEC) {
                    log_info("In Ovp L1N_L12 = %f \n", inputVoltage.L1N_L12);
                    if (_threePhaseOvp[0] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputOVP = YES;
                    } else {
                        _threePhaseOvp[0] += 1;
                    }
                }
            } else {
                if (inputVoltage.L1N_L12 < VIN_MAX_REV_VOLTAGE_IEC) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputOVP = NO;
                    _threePhaseOvp[0] = 0;
                }
            }

            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputOVP == NO) {
                if (inputVoltage.L2N_L23 > VIN_MAX_VOLTAGE_IEC) {
                    log_info("In Ovp L2N_L23 = %f \n", inputVoltage.L2N_L23);
                    if (_threePhaseOvp[1] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputOVP = YES;
                    } else {
                        _threePhaseOvp[1] += 1;
                    }
                }
            } else {
                if (inputVoltage.L2N_L23 < VIN_MAX_REV_VOLTAGE_IEC) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputOVP = NO;
                    _threePhaseOvp[1] = 0;
                }
            }

            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputOVP == NO) {
                if (inputVoltage.L3N_L31 > VIN_MAX_VOLTAGE_IEC) {
                    log_info("In Ovp L3N_L31 = %f \n", inputVoltage.L3N_L31);
                    if (_threePhaseOvp[2] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputOVP = YES;
                    } else {
                        _threePhaseOvp[2] += 1;
                    }
                }
            } else {
                if (inputVoltage.L3N_L31 < VIN_MAX_REV_VOLTAGE_IEC) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputOVP = NO;
                    _threePhaseOvp[2] = 0;
                }
            }
        } else if (ShmSysConfigAndInfo->SysInfo.ChargerType == _CHARGER_TYPE_UL) {
            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputOVP == NO) {
                if (inputVoltage.L1N_L12 > VIN_MAX_VOLTAGE_UL) {
                    log_info("In Ovp L1N_L12 = %f \n", inputVoltage.L1N_L12);
                    if (_threePhaseOvp[0] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputOVP = YES;
                    } else {
                        _threePhaseOvp[0] += 0;
                    }
                }
            } else {
                if (inputVoltage.L1N_L12 < VIN_MAX_REV_VOLTAGE_UL) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputOVP = NO;
                    _threePhaseOvp[0] = 0;
                }
            }

            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputOVP == NO) {
                if (inputVoltage.L2N_L23 > VIN_MAX_VOLTAGE_UL) {
                    log_info("In Ovp L2N_L23 = %f \n", inputVoltage.L2N_L23);
                    if (_threePhaseOvp[1] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputOVP = YES;
                    } else {
                        _threePhaseOvp[1] += 0;
                    }
                }
            } else {
                if (inputVoltage.L2N_L23 < VIN_MAX_REV_VOLTAGE_UL) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputOVP = NO;
                    _threePhaseOvp[1] = 0;
                }
            }

            if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputOVP == NO) {
                if (inputVoltage.L3N_L31 > VIN_MAX_VOLTAGE_UL) {
                    log_info("In Ovp L3N_L31 = %f \n", inputVoltage.L3N_L31);
                    if (_threePhaseOvp[2] >= OVP_UVP_CHK_COUNT) {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputOVP = YES;
                    } else {
                        _threePhaseOvp[2] += 1;
                    }
                }
            } else {
                if (inputVoltage.L3N_L31 < VIN_MAX_REV_VOLTAGE_UL) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputOVP = NO;
                    _threePhaseOvp[2] = 0;
                }
            }
        }
    }
}

// 左右槍的 Relay 前後的輸出電壓
void GetPersentOutputVol(void)
{
    uint8_t index = 0;
    uint8_t targetID = 0;

    if (Query_Present_OutputVoltage(Uart5Fd, ADDR_RELAY, &outputVoltage) != PASS) {
        return;
    }

    //log_info("Conn1 fuse 1 = %f \n", outputVoltage.behindFuse_Voltage_C1);
    //log_info("Conn1 relay 1 = %f \n", outputVoltage.behindRelay_Voltage_C1);
    //log_info("Conn2 fuse 2 = %f \n", outputVoltage.behindFuse_Voltage_C2);
    //log_info("Conn2 relay 2 = %f \n", outputVoltage.behindRelay_Voltage_C2);

    //log_info("outputVoltage.behindFuse_Voltage_C1 = %f \n", outputVoltage.behindFuse_Voltage_C1);
    //log_info("outputVoltage.behindFuse_Voltage_C2 = %f \n", outputVoltage.behindFuse_Voltage_C2);

    ShmRelayModuleData->Gun1FuseOutputVolt = outputVoltage.behindFuse_Voltage_C1;
    ShmRelayModuleData->Gun1RelayOutputVolt = outputVoltage.behindRelay_Voltage_C1;
    ShmRelayModuleData->Gun2FuseOutputVolt = outputVoltage.behindFuse_Voltage_C2;
    ShmRelayModuleData->Gun2RelayOutputVolt = outputVoltage.behindRelay_Voltage_C2;

    for (index = 0; index < gunCount; index++) {
        targetID = getCommTargetID(index);

        switch (targetID) {
        case 0x01:
#if defined DD360 || defined DD360Audi || defined DD360ComBox
            _chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun1RelayOutputVolt;
            _chargingData[index]->PresentChargingCurrent = ShmRelayModuleData->Gun1FuseOutputVolt / 10;
            _chargingData[index]->PresentChargingVoltage = _chargingData[index]->FireChargingVoltage / 10;
            _chargingData[index]->FuseChargingVoltage = _chargingData[index]->FireChargingVoltage;
            break;
#endif //defined DD360 || defined DD360Audi || defined DD360ComBox

            _chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun1RelayOutputVolt;
            _chargingData[index]->FuseChargingVoltage = ShmRelayModuleData->Gun1FuseOutputVolt;
            break;

        case 0x02:
#if defined DD360 || defined DD360Audi || defined DD360ComBox
            _chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun2RelayOutputVolt;
            _chargingData[index]->PresentChargingCurrent = ShmRelayModuleData->Gun2FuseOutputVolt / 10;
            _chargingData[index]->PresentChargingVoltage = _chargingData[index]->FireChargingVoltage / 10;
            _chargingData[index]->FuseChargingVoltage = _chargingData[index]->FireChargingVoltage;
            break;
#endif //defined DD360 || defined DD360Audi || defined DD360ComBox

            _chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun2RelayOutputVolt;
            _chargingData[index]->FuseChargingVoltage = ShmRelayModuleData->Gun2FuseOutputVolt;
            break;
        }

        //unsigned short Ovp = 0;
        //unsigned short Ocp = 0;
        //Ovp = MIN [VOUT_MAX_VOLTAGE, EV_BATTERY_VOLTAGE]  // 最大輸出電壓與電池電壓最大值
        //Ocp = MIN [IOUT_MAX_CURRENT, EV_CURRENT_REQ]      // 最大輸出電流與需求電流最小值
        //if (_chargingData[index]->Type == _Type_Chademo) {
        //    //Ovp = MaxValue(_chargingData[index]->MaximumChargingVoltage, _chargingData[index]->EvBatteryMaxVoltage);
        //    //Ocp = MaxValue(_chargingData[index]->PresentChargingCurrent, ShmCHAdeMOData->ev[_chargingData[index]->type_index].ChargingCurrentRequest);
        //} else if (_chargingData[index]->Type == _Type_CCS_2) {
        //}
    }
}

// 風扇速度
void GetFanSpeed()
{
    //log_info("Get fan board speed \n");
    if (Query_Fan_Speed(Uart5Fd, ADDR_FAN, &fanSpeed) == PASS) {
        ShmFanModuleData->PresentFan1Speed = fanSpeed.speed[0];
        ShmFanModuleData->PresentFan2Speed = fanSpeed.speed[1];
        ShmFanModuleData->PresentFan3Speed = fanSpeed.speed[2];
        ShmFanModuleData->PresentFan4Speed = fanSpeed.speed[3];
//      log_info("SystemFanRotaSpeed_1 = %d \n", fanSpeed.speed[0]);
//      log_info("SystemFanRotaSpeed_2 = %d \n", fanSpeed.speed[1]);
//      log_info("SystemFanRotaSpeed_3 = %d \n", fanSpeed.speed[2]);
//      log_info("SystemFanRotaSpeed_4 = %d \n", fanSpeed.speed[3]);
        // Config_Fan_Speed(Uart5Fd, ADDR_FAN, &fanSpeed[0]);
        //SysInfoData (SystemFanRotaSpeed)
    }
}

// 讀取 Relay 狀態
void GetRelayOutputStatus(void)
{
    if (Query_Relay_Output(Uart5Fd, ADDR_RELAY, &regRelay) == PASS) {
#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
        regRelay.relay_event.bits.AC_Contactor = ShmSysConfigAndInfo->SysInfo.AcContactorStatus;
#endif //!defined DD360 && !defined DD360Audi
    }
}

// 確認 K1 K2 relay 的狀態
void CheckK1K2RelayOutput(uint8_t index)
{
    uint8_t targetID = 0;

    targetID = getCommTargetID(index);

    switch (targetID) {
    case 0x01:
        if (regRelay.relay_event.bits.Gun1_N == YES && regRelay.relay_event.bits.Gun1_P == YES) {
            _chargingData[index]->RelayK1K2Status = YES;
        } else {
            _chargingData[index]->RelayK1K2Status = NO;
        }

        if (_chargingData[index]->Type == _Type_CCS_2) {
#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
            if (regRelay.relay_event.bits.Gun1_N == YES && regRelay.relay_event.bits.CCS_Precharge == YES) {
                _chargingData[index]->RelayKPK2Status = YES;
            } else {
                _chargingData[index]->RelayKPK2Status = NO;
            }
#else
            if (_chargingData[index]->SystemStatus == S_CCS_PRECHARGE_ST0) {
                _chargingData[index]->RelayKPK2Status = YES;
            } else {
                _chargingData[index]->RelayKPK2Status = NO;
            }
#endif //!defined DD360 && !defined DD360Audi
        }
        break;

    case 0x02:
        if (regRelay.relay_event.bits.Gun2_N == YES &&
                regRelay.relay_event.bits.Gun2_P == YES) {
            _chargingData[index]->RelayK1K2Status = YES;
        } else {
            _chargingData[index]->RelayK1K2Status = NO;
        }

        if (_chargingData[index]->Type == _Type_CCS_2) {
#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
            if (regRelay.relay_event.bits.Gun2_N == YES &&
                    regRelay.relay_event.bits.CCS_Precharge == YES) {
                _chargingData[index]->RelayKPK2Status = YES;
            } else {
                _chargingData[index]->RelayKPK2Status = NO;
            }
#else
            if (_chargingData[index]->SystemStatus == S_CCS_PRECHARGE_ST0) {
                _chargingData[index]->RelayKPK2Status = YES;
            } else {
                _chargingData[index]->RelayKPK2Status = NO;
            }
#endif //!defined DD360 && !defined DD360Audi
        }
        break;
    }

#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
    //DS60-120 add
    if (ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus == YES) {
        if (regRelay.relay_event.bits.Gun1_Parallel_N == NO &&
                regRelay.relay_event.bits.Gun1_Parallel_P == NO) {
            ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus = NO;
        }
    } else if (ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus == NO) {
        if (regRelay.relay_event.bits.Gun1_Parallel_N == YES &&
                regRelay.relay_event.bits.Gun1_Parallel_P == YES) {
            ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus = YES;
        }
    }
#else
    ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus = YES;
#endif //!defined DD360 && !defined DD360Audi
}

void GetGfdAdc()
{
    int gunIndex = 0;
    uint8_t targetID = 0;

    // define : 每 0.2 ~ 1 秒一次
    // occur : <= 75k 歐姆 @ 150 - 750 Vdc
    // warning : >= 100 歐姆 && <= 500 歐姆 @ 150-750 Vdc
    if (Query_Gfd_Adc(Uart5Fd, ADDR_RELAY, &gfd_adc) == PASS) {
        for (gunIndex = 0; gunIndex < gunCount; gunIndex++) {
            if (_chargingData[gunIndex]->Type == 0x09 &&
                    !ShmSysConfigAndInfo->SysConfig.AlwaysGfdFlag
               ) {
                if ((_chargingData[gunIndex]->PresentChargingVoltage * 10) >= VOUT_MIN_VOLTAGE) {
                    _chargingData[gunIndex]->GroundFaultStatus = GFD_PASS;
                }
                continue;
            }

            targetID = getCommTargetID(gunIndex);

            if (targetID == 0x01) {
                if (gfd_adc.result_conn1 == GFD_WARNING) {
                    gfd_adc.result_conn1 = GFD_PASS;
                }

                _chargingData[gunIndex]->GroundFaultStatus = gfd_adc.result_conn1;
                //log_info("GFD ******** Result = %d, Step = %d, R = %d, Vol = %d \n",
                //            _chargingData[gunIndex]->GroundFaultStatus,
                //            gfd_adc.rb_step_1,
                //            gfd_adc.Resister_conn1,
                //            gfd_adc.voltage_conn1);
                if (_chargingData[gunIndex]->GroundFaultStatus == GFD_FAIL) {
                    log_info("GFD Fail. index = %d, Step = %d, R = %d, Vol = %d \n",
                             gunIndex,
                             gfd_adc.rb_step_1,
                             gfd_adc.Resister_conn1,
                             gfd_adc.voltage_conn1);

                } else if (_chargingData[gunIndex]->GroundFaultStatus == GFD_PASS ||
                           _chargingData[gunIndex]->GroundFaultStatus == GFD_WARNING
                          ) {
                    if (_chargingData[gunIndex]->GroundFaultStatus == GFD_WARNING) {
                        log_info("GFD Warning. index = %d, Result = %d, R = %d, Vol = %d \n",
                                 gunIndex,
                                 _chargingData[gunIndex]->GroundFaultStatus,
                                 gfd_adc.Resister_conn1,
                                 gfd_adc.voltage_conn1);
                    }
                }
            } else if (targetID == 0x02) {
                if (gfd_adc.result_conn2 == GFD_WARNING) {
                    gfd_adc.result_conn2 = GFD_PASS;
                }
                _chargingData[gunIndex]->GroundFaultStatus = gfd_adc.result_conn2;
                if (_chargingData[gunIndex]->GroundFaultStatus == GFD_FAIL) {
                    log_info("GFD Fail. index = %d, Step = %d, R = %d, Vol = %d \n",
                             gunIndex,
                             gfd_adc.rb_step_2,
                             gfd_adc.Resister_conn2,
                             gfd_adc.voltage_conn2);
                } else if (_chargingData[gunIndex]->GroundFaultStatus == GFD_PASS ||
                           _chargingData[gunIndex]->GroundFaultStatus == GFD_WARNING
                          ) {
                    if (_chargingData[gunIndex]->GroundFaultStatus == GFD_WARNING) {
                        log_info("GFD Warning. index = %d, Result = %d, R = %d, Vol = %d \n",
                                 gunIndex,
                                 _chargingData[gunIndex]->GroundFaultStatus,
                                 gfd_adc.Resister_conn1,
                                 gfd_adc.voltage_conn1);
                    }
                }
            }
        }
    }
}

void GetGpioInput()
{
    if (Query_Gpio_Input(Uart5Fd, ADDR_AUX, &gpio_in) == PASS) {
        // AC Contactor Status
        if (gpio_in.AC_MainBreaker == 1) {
            // AC Main Breaker ON
            log_info("RB AC Main Breaker. \n");
        }

        if (gpio_in.SPD == 1) {
            // SPD (雷擊保護) ON
            log_info("RB SPD. \n");
        }

        if (gpio_in.Door_Open == 1) {
            // Door Open
            log_info("RB Door Open. \n");
        }

        if (gpio_in.GFD[0] == 1) {
            // GFD_1 Trigger
        }

        if (gpio_in.GFD[1] == 1) {
            // GFD_2 Trigger
        }

        if (gpio_in.AC_Drop == 1) {
            // AC Drop
            log_info("RB AC Drop. \n");
        }

        if (gpio_in.Emergency_IO == 1) {
            // Emergency IO ON
            log_info("RB Emergency IO ON. \n");
        }

        if (gpio_in.Button_Emergency_Press == 1) {
            // Emergency button Press
        }

        if (gpio_in.Button_On_Press == 1) {
            // On button Press
        }

        if (gpio_in.Button_Off_Press == 1) {
            // Off button Press
        }

        if (gpio_in.Key_1_Press == 1) {
            // key 1 press
        }

        if (gpio_in.Key_2_Press == 1) {
            // key 2 press
        }

        if (gpio_in.Key_3_Press == 1) {
            // key 3 press
        }

        if (gpio_in.Key_4_Press == 1) {
            // key 4 press
        }
    }
}

// 5V 12V 24V 48V
void GetAuxPower()
{
    if (Query_Aux_PowerVoltage(Uart5Fd, ADDR_FAN, &auxPower) == PASS) {
        ShmSysConfigAndInfo->SysInfo.AuxPower48V = auxPower.voltage[0];
        ShmSysConfigAndInfo->SysInfo.AuxPower24V = auxPower.voltage[1];
        //ShmSysConfigAndInfo->SysInfo.AuxPower12V = auxPower.voltage[4];
        //ShmSysConfigAndInfo->SysInfo.AuxPower5V = auxPower.voltage[6];
        // aux power voltage
        //log_info("aux1 = %x, \n", auxPower.voltage[0]);
        //log_info("aux2 = %x, \n", auxPower.voltage[1]);
    }
}

void SetFanModuleSpeed()
{
    {
        FanSpeed _fanSpeed;

        _setFanSpeed += fanSpeedSmoothValue;

        if (_setFanSpeed >= ShmFanModuleData->SetFan1Speed) {
            _setFanSpeed = ShmFanModuleData->SetFan1Speed;
        }

        //printf("_setFanSpeed = %d \n", _setFanSpeed);
        _fanSpeed.speed[0] = _setFanSpeed;

        _fanSpeed.speed[1] = _setFanSpeed;

        _fanSpeed.speed[2] = _setFanSpeed;

        _fanSpeed.speed[3] = _setFanSpeed;

        if (Config_Fan_Speed(Uart5Fd, ADDR_FAN, &_fanSpeed) == PASS) {
            //log_info("successfully Fan\n");
        }
    }
}

//==========================================
// Common Function
//==========================================
void SetK1K2RelayStatus(uint8_t index)
{
    uint8_t targetID = 0;
    PreChargingState *pRegPreChargingState = NULL;
    PreChargingState *pOutputPreChargingState = NULL;
    GunPNState *pRegGunPNState = NULL;
    GunPNState *pOutputGunPNState = NULL;
    struct ChargingInfoData *pDcChargingInfo = (struct ChargingInfoData *)_chargingData[index];

    if (ShmPsuData->Work_Step >= _TEST_MODE && ShmPsuData->Work_Step <= _TEST_MODE) {
        if (regRelay.relay_event.bits.Gun1_N == NO) {
            outputRelay.relay_event.bits.Gun1_N = YES;
        } else if (regRelay.relay_event.bits.Gun1_P == NO) {
            outputRelay.relay_event.bits.Gun1_P = YES;
        }
        return;
    }

    targetID = getCommTargetID(index);

    pRegPreChargingState = (PreChargingState *)&regRelay.relay_event.relay_status[0];
    pOutputPreChargingState = (PreChargingState *)&outputRelay.relay_event.relay_status[0];
    if (targetID == 0x01) {
        pRegGunPNState = (GunPNState *)&regRelay.relay_event.relay_status[1];
        pOutputGunPNState = (GunPNState *)&outputRelay.relay_event.relay_status[1];
    } else if (targetID == 0x02) {
        pRegGunPNState = (GunPNState *)&regRelay.relay_event.relay_status[2];
        pOutputGunPNState = (GunPNState *)&outputRelay.relay_event.relay_status[2];
    }

    switch (pDcChargingInfo->SystemStatus) {
    case S_BOOTING:
    case S_IDLE:
    case S_AUTHORIZING:
    case S_REASSIGN_CHECK:
    case S_REASSIGN:
    case S_PREPARNING:
    case S_PREPARING_FOR_EV:
        if (pRegGunPNState->GunP == YES) {
            pOutputGunPNState->GunP = NO;
        } else if (pRegGunPNState->GunN == YES) {
            pOutputGunPNState->GunN = NO;
        }

        if (targetID == 0x02 && pDcChargingInfo->Type == _Type_CCS_2) {
            if (pRegPreChargingState->CcsPrecharge == YES) {
                pOutputPreChargingState->CcsPrecharge = NO;
            }
        }
        break;

    case S_PREPARING_FOR_EVSE:
    case S_CHARGING:
        //if (pDcChargingInfo->RelayWeldingCheck != YES) {
        //    break;
        //}

        if (pRegGunPNState->GunN == NO) {
            pOutputGunPNState->GunN = YES;
        } else if (pRegGunPNState->GunP == NO) {
            pOutputGunPNState->GunP = YES;
        }
        break;

    case S_TERMINATING:
    case S_COMPLETE:
    case S_ALARM:
        if ((pDcChargingInfo->PresentChargingCurrent * 10) <= SEFETY_SWITCH_RELAY_CUR) {
            if (pRegGunPNState->GunP == YES) {
                pOutputGunPNState->GunP = NO;
            } else if (pRegGunPNState->GunN == YES) {
                pOutputGunPNState->GunN = NO;
            }
        }
        break;

    case S_CCS_PRECHARGE_ST0:
#if defined DD360 || defined DD360Audi || defined DD360ComBox
        break;
#endif //defined DD360 || defined DD360Audi || defined DD360ComBox

        if (pDcChargingInfo->Type == _Type_CCS_2 && targetID == 0x02) {
            if (pRegPreChargingState->CcsPrecharge == NO) {
                pOutputPreChargingState->CcsPrecharge = YES;
            } else if (pRegPreChargingState->CcsPrecharge == YES) {
                pRegGunPNState->GunP = NO;
            }
        }
        break;

    case S_CCS_PRECHARGE_ST1:
#if defined DD360 || defined DD360Audi || defined DD360ComBox
        break;
#endif //defined DD360 || defined DD360Audi || defined DD360ComBox

        if (pDcChargingInfo->Type == _Type_CCS_2 && targetID == 0x02) {
            if (pRegGunPNState->GunP == NO) {
                pOutputGunPNState->GunP = YES;
            } else if (pRegGunPNState->GunP == YES) {
                pOutputPreChargingState->CcsPrecharge = NO;
            }
        }
        break;
    }
}

void CheckAcInputOvpStatus(uint8_t index)
{
    if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputOVP == YES ||
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputOVP == YES ||
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputOVP == YES) {
//      if ((_chargingData[index]->SystemStatus >= S_PREPARNING && _chargingData[index]->SystemStatus <= S_CHARGING) ||
//              (_chargingData[index]->SystemStatus >= S_CCS_PRECHARGE_ST0 && _chargingData[index]->SystemStatus <= S_CCS_PRECHARGE_ST1))
//      {
//          if (ShmSysConfigAndInfo->SysInfo.ChargerType == _CHARGER_TYPE_IEC)
//          {
//              if (_psuInputVolR > VIN_MAX_VOLTAGE_IEC ||
//                      _psuInputVolS > VIN_MAX_VOLTAGE_IEC ||
//                      _psuInputVolT > VIN_MAX_VOLTAGE_IEC)
//              {
//                  log_info("IEC _psuInputVolR = %f, _psuInputVolS = %f, _psuInputVolT = %f \n",
//                          _psuInputVolR, _psuInputVolS, _psuInputVolT);
//                  _chargingData[index]->StopChargeFlag = YES;
//              }
//
//          }
//          else if (ShmSysConfigAndInfo->SysInfo.ChargerType == _CHARGER_TYPE_UL)
//          {
//              if (_psuInputVolR > VIN_MAX_VOLTAGE_UL ||
//                      _psuInputVolS > VIN_MAX_VOLTAGE_UL ||
//                      _psuInputVolT > VIN_MAX_VOLTAGE_UL)
//              {
//                  log_info("UL _psuInputVolR = %f, _psuInputVolS = %f, _psuInputVolT = %f \n",
//                          _psuInputVolR, _psuInputVolS, _psuInputVolT);
//                  _chargingData[index]->StopChargeFlag = YES;
//              }
//          }
//      }
//      else
        //log_info("CheckAcInputOvpStatus\r\n");
        _chargingData[index]->StopChargeFlag = YES;
    }
}

void CheckPhaseLossStatus(uint8_t index)
{
    if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputUVP == YES ||
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputUVP == YES ||
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL3InputUVP == YES) {
        //log_info("CheckPhaseLossStatus\r\n");
        _chargingData[index]->StopChargeFlag = YES;
    }
}

void SetParalleRelayStatus(void)
{
#if defined  DD360 || defined DD360Audi || defined DD360ComBox
    return;
#endif //!defined  DD360 || !defined DD360Audi || !defined DD360ComBox

    // 之後雙槍單模機種,橋接都會上
    if (gunCount >= 2) {
        if (_chargingData[0]->SystemStatus == S_BOOTING || _chargingData[1]->SystemStatus == S_BOOTING ||
                (_chargingData[0]->SystemStatus == S_IDLE && _chargingData[1]->SystemStatus == S_IDLE)) {
            // 初始化~ 不搭橋接
            if (regRelay.relay_event.bits.Gun1_Parallel_P == YES) {
                outputRelay.relay_event.bits.Gun1_Parallel_P = NO;
            } else if (regRelay.relay_event.bits.Gun1_Parallel_N == YES) {
                outputRelay.relay_event.bits.Gun1_Parallel_N = NO;
            }
        } else {
            if (_chargingData[0]->IsReadyToCharging == YES ||
                    _chargingData[1]->IsReadyToCharging == YES) {
                // ************需考慮在切換中 - 切開 relay 與搭回 relay 的時機點************
                if (ShmSysConfigAndInfo->SysInfo.MainChargingMode == _MAIN_CHARGING_MODE_MAX) {
                    if (ShmSysConfigAndInfo->SysInfo.ReAssignedFlag < _REASSIGNED_RELAY_M_TO_A) {
                        // 最大充 - 搭上橋接
                        if (regRelay.relay_event.bits.Gun1_Parallel_N == NO) {
                            outputRelay.relay_event.bits.Gun1_Parallel_N = YES;
                        } else if (regRelay.relay_event.bits.Gun1_Parallel_P == NO) {
                            outputRelay.relay_event.bits.Gun1_Parallel_P = YES;
                        }
                    } else {
                        // 平均充 - 不搭
                        if (regRelay.relay_event.bits.Gun1_Parallel_P == YES) {
                            outputRelay.relay_event.bits.Gun1_Parallel_P = NO;
                        } else if (regRelay.relay_event.bits.Gun1_Parallel_N == YES) {
                            outputRelay.relay_event.bits.Gun1_Parallel_N = NO;
                        }
                    }
                } else if (ShmSysConfigAndInfo->SysInfo.MainChargingMode == _MAIN_CHARGING_MODE_AVER) {
                    if (ShmSysConfigAndInfo->SysInfo.ReAssignedFlag < _REASSIGNED_RELAY_A_TO_M) {
                        // 平均充 - 不搭
                        if (regRelay.relay_event.bits.Gun1_Parallel_P == YES) {
                            outputRelay.relay_event.bits.Gun1_Parallel_P = NO;
                        } else if (regRelay.relay_event.bits.Gun1_Parallel_N == YES) {
                            outputRelay.relay_event.bits.Gun1_Parallel_N = NO;
                        }
                    } else {
                        // 最大充 - 搭上橋接
                        if (regRelay.relay_event.bits.Gun1_Parallel_N == NO) {
                            outputRelay.relay_event.bits.Gun1_Parallel_N = YES;
                        } else if (regRelay.relay_event.bits.Gun1_Parallel_P == NO) {
                            outputRelay.relay_event.bits.Gun1_Parallel_P = YES;
                        }
                    }
                }
            }
        }
    }
}

void CheckAlarmOccur()
{
    bool isErr = false;
    for (uint8_t count = 0; count < sizeof(_alarm_code) / sizeof(_alarm_code[0]); count++) {
        if (acAlarmCode.AcAlarmCode & _alarm_code[count]) {
            isErr = true;
            switch (_alarm_code[count]) {
            case AC_OVP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.AcSystemInputOVP = YES; break;
            case AC_UVP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.AcSystemInputUVP = YES; break;
            case AC_OCP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemAcOutputOCP = YES; break;
            case AC_OTP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemAmbientOTP = YES; break;
            case AC_GMI_FAULT: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.AcGroundfaultFail = YES; break;
            case AC_CP_ERROR: ShmStatusCodeData->InfoCode.InfoEvents.bits.PilotFault = YES; break;
            case AC_AC_LEAKAGE: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.RcdTrip = YES; break;
            case AC_DC_LEAKAGE: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.RcdTrip = YES; break;
            case AC_SYSTEM_SELFTEST_FAULT: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.McuSelftestFail = YES; break;
            case AC_HANDSHAKE_TIMEOUT: break;
            //case AC_EMC_STOP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.EmergencyStopTrip = YES; break;
            case AC_RELAY_WELDING: ShmStatusCodeData->FaultCode.FaultEvents.bits.AcOutputRelayWelding = YES; break;
            case AC_GF_MODULE_FAULT: ShmStatusCodeData->FaultCode.FaultEvents.bits.RcdSelfTestFail = YES; break;
            case AC_SHUTTER_FAULT: break;
            case AC_LOCKER_FAULT: ShmStatusCodeData->FaultCode.FaultEvents.bits.AcConnectorLockFail = YES; break;
            case AC_POWER_DROP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputDrop = YES; break;
            case AC_CIRCUIT_SHORT: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CircuitShort = YES; break;
            case AC_ROTARY_SWITCH_FAULT: break;
            case AC_RELAY_DRIVE_FAULT: ShmStatusCodeData->FaultCode.FaultEvents.bits.AcOutputRelayDrivingFault = YES; break;
            }
        } else {
            switch (_alarm_code[count]) {
            case AC_OVP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.AcSystemInputOVP = NO; break;
            case AC_UVP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.AcSystemInputUVP = NO; break;
            case AC_OCP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemAcOutputOCP = NO; break;
            case AC_OTP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemAmbientOTP = NO; break;
            case AC_GMI_FAULT: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.AcGroundfaultFail = NO; break;
            case AC_CP_ERROR: ShmStatusCodeData->InfoCode.InfoEvents.bits.PilotFault = NO; break;
            case AC_AC_LEAKAGE: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.RcdTrip = NO; break;
            case AC_DC_LEAKAGE: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.RcdTrip = NO; break;
            case AC_SYSTEM_SELFTEST_FAULT: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.McuSelftestFail = NO; break;
            case AC_HANDSHAKE_TIMEOUT: break;
            //case AC_EMC_STOP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.EmergencyStopTrip = NO; break;
            case AC_RELAY_WELDING: ShmStatusCodeData->FaultCode.FaultEvents.bits.AcOutputRelayWelding = NO; break;
            case AC_GF_MODULE_FAULT: ShmStatusCodeData->FaultCode.FaultEvents.bits.RcdSelfTestFail = NO; break;
            case AC_SHUTTER_FAULT: break;
            case AC_LOCKER_FAULT: ShmStatusCodeData->FaultCode.FaultEvents.bits.AcConnectorLockFail = NO; break;
            case AC_POWER_DROP: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL1InputDrop = NO; break;
            case AC_CIRCUIT_SHORT: ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CircuitShort = NO; break;
            case AC_ROTARY_SWITCH_FAULT:  break;
            case AC_RELAY_DRIVE_FAULT: ShmStatusCodeData->FaultCode.FaultEvents.bits.AcOutputRelayDrivingFault = NO; break;
            }
        }
    }

    ac_chargingInfo[0]->IsErrorOccur = isErr;
}

bool IsNoneMatchLedColor()
{
    bool result = false;

    if (cur_led_color.Connect_1_Red != led_color.Connect_1_Red ||
            cur_led_color.Connect_1_Green != led_color.Connect_1_Green ||
            cur_led_color.Connect_1_Blue != led_color.Connect_1_Blue ||
            cur_led_color.Connect_2_Red != led_color.Connect_2_Red ||
            cur_led_color.Connect_2_Green != led_color.Connect_2_Green ||
            cur_led_color.Connect_2_Blue != led_color.Connect_2_Blue) {
        result = true;
    }

    return result;
}

void SetLedColor(struct ChargingInfoData *chargingData_1, struct ChargingInfoData *chargingData_2)
{
    uint8_t _colorBuf = COLOR_MAX_LV * LED_INTENSITY_BRIGHTEST;

    if (ShmSysConfigAndInfo->SysConfig.LedInfo.Intensity == _LED_INTENSITY_DARKEST) {
        _colorBuf = COLOR_MAX_LV * LED_INTENSITY_DARKEST;
    } else if (ShmSysConfigAndInfo->SysConfig.LedInfo.Intensity == _LED_INTENSITY_MEDIUM) {
        _colorBuf = COLOR_MAX_LV * LED_INTENSITY_MEDIUM;
    }

    //printf("chargingData_1->SystemStatus=%d\n",chargingData_1->SystemStatus);
    //printf("chargingData_2->SystemStatus=%d\n",chargingData_2->SystemStatus);
    //printf("ShmSysConfigAndInfo->SysWarningInfo.Level=%d\n",ShmSysConfigAndInfo->SysWarningInfo.Level);
    if (ShmSysConfigAndInfo->SysWarningInfo.Level == 2) {
        led_color.Connect_1_Green = COLOR_MIN_LV;
        led_color.Connect_1_Blue = COLOR_MIN_LV;
        led_color.Connect_1_Red = _colorBuf;
        led_color.Connect_2_Green = COLOR_MIN_LV;
        led_color.Connect_2_Blue = COLOR_MIN_LV;
        led_color.Connect_2_Red = _colorBuf;
    } else {
        if (ShmSysConfigAndInfo->SysInfo.IsAlternatvieConf) {
            if ((chargingData_1->SystemStatus == S_BOOTING ||
                    chargingData_1->SystemStatus == S_IDLE ||
                    chargingData_1->SystemStatus == S_RESERVATION) &&
                    (chargingData_2->SystemStatus == S_BOOTING ||
                     chargingData_2->SystemStatus == S_IDLE ||
                     chargingData_2->SystemStatus == S_RESERVATION)) {
#if defined DD360Audi
                led_color.Connect_1_Green = _colorBuf;
                led_color.Connect_1_Blue = _colorBuf;
                led_color.Connect_1_Red = _colorBuf;

                led_color.Connect_2_Green = _colorBuf;
                led_color.Connect_2_Blue = _colorBuf;
                led_color.Connect_2_Red = _colorBuf;
#else
                led_color.Connect_1_Green = _colorBuf;
                led_color.Connect_1_Blue = COLOR_MIN_LV;
                led_color.Connect_1_Red = COLOR_MIN_LV;

                led_color.Connect_2_Green = _colorBuf;
                led_color.Connect_2_Blue = COLOR_MIN_LV;
                led_color.Connect_2_Red = COLOR_MIN_LV;
#endif
            } else if ((chargingData_1->SystemStatus >= S_AUTHORIZING &&
                        chargingData_1->SystemStatus <= S_COMPLETE) ||
                       (chargingData_1->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
                        chargingData_1->SystemStatus <= S_CCS_PRECHARGE_ST1) ||
                       (chargingData_2->SystemStatus >= S_AUTHORIZING &&
                        chargingData_2->SystemStatus <= S_COMPLETE) ||
                       (chargingData_2->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
                        chargingData_2->SystemStatus <= S_CCS_PRECHARGE_ST1)) {
                led_color.Connect_1_Green = COLOR_MIN_LV;
                led_color.Connect_1_Blue = _colorBuf;
                led_color.Connect_1_Red = COLOR_MIN_LV;
                led_color.Connect_2_Green = COLOR_MIN_LV;
                led_color.Connect_2_Blue = _colorBuf;
                led_color.Connect_2_Red = COLOR_MIN_LV;
            }
        } else {
            if (chargingData_1->SystemStatus == S_BOOTING ||
                    chargingData_1->SystemStatus == S_IDLE ||
                    chargingData_1->SystemStatus == S_RESERVATION ||
                    chargingData_1->SystemStatus == S_MAINTAIN) {

                if (chargingData_1->IsAvailable == NO) { //For Audi
                    led_color.Connect_1_Green = COLOR_MIN_LV;
                    led_color.Connect_1_Blue = COLOR_MIN_LV;
                    led_color.Connect_1_Red = _colorBuf;
                } else {
#if defined DD360Audi
                    led_color.Connect_1_Green = _colorBuf;
                    led_color.Connect_1_Blue = _colorBuf;
                    led_color.Connect_1_Red = _colorBuf;
#else
                    led_color.Connect_1_Green = _colorBuf;
                    led_color.Connect_1_Blue = COLOR_MIN_LV;
                    led_color.Connect_1_Red = COLOR_MIN_LV;
#endif
                }
            } else if ((chargingData_1->SystemStatus >= S_AUTHORIZING &&
                        chargingData_1->SystemStatus <= S_COMPLETE) ||
                       (chargingData_1->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
                        chargingData_1->SystemStatus <= S_CCS_PRECHARGE_ST1)) {
                led_color.Connect_1_Green = COLOR_MIN_LV;
                led_color.Connect_1_Blue = _colorBuf;
                led_color.Connect_1_Red = COLOR_MIN_LV;
            }

            // --------------------------------------------------------------------------
            if (chargingData_2->SystemStatus == S_BOOTING ||
                    chargingData_2->SystemStatus == S_IDLE ||
                    chargingData_2->SystemStatus == S_RESERVATION ||
                    chargingData_2->SystemStatus == S_MAINTAIN) {
                if (chargingData_2->IsAvailable == NO) {
                    led_color.Connect_2_Green = COLOR_MIN_LV;
                    led_color.Connect_2_Blue = COLOR_MIN_LV;
                    led_color.Connect_2_Red = _colorBuf;
                } else {
#if defined DD360Audi
                    led_color.Connect_2_Green = _colorBuf;
                    led_color.Connect_2_Blue = _colorBuf;
                    led_color.Connect_2_Red = _colorBuf;
#else
                    led_color.Connect_2_Green = _colorBuf;
                    led_color.Connect_2_Blue = COLOR_MIN_LV;
                    led_color.Connect_2_Red = COLOR_MIN_LV;
#endif
                }
            } else if ((chargingData_2->SystemStatus >= S_AUTHORIZING &&
                        chargingData_2->SystemStatus <= S_COMPLETE) ||
                       (chargingData_2->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
                        chargingData_2->SystemStatus <= S_CCS_PRECHARGE_ST1)) {
                led_color.Connect_2_Green = COLOR_MIN_LV;
                led_color.Connect_2_Blue = _colorBuf;
                led_color.Connect_2_Red = COLOR_MIN_LV;
            }
        }
    }

    if (_checkLedChanged > 0) {
        if (Config_Led_Color(Uart5Fd, ADDR_LED, &led_color) == PASS) {
            _checkLedChanged--;

            cur_led_color.Connect_1_Red     = led_color.Connect_1_Red;
            cur_led_color.Connect_1_Green   = led_color.Connect_1_Green;
            cur_led_color.Connect_1_Blue    = led_color.Connect_1_Blue;
            cur_led_color.Connect_2_Red     = led_color.Connect_2_Red;
            cur_led_color.Connect_2_Green   = led_color.Connect_2_Green;
            cur_led_color.Connect_2_Blue    = led_color.Connect_2_Blue;
        }
    } else if (IsNoneMatchLedColor()) {
        _checkLedChanged = 3;
    }
}

//==========================================
// Init all share memory
//==========================================
int InitShareMemory()
{
    int result = PASS;
    int MeterSMId;

    if ((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo),  0777)) < 0) {
        result = FAIL;
    } else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }

    if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0) {
        result = FAIL;
    } else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }

    if ((MeterSMId = shmget(ShmFanBdKey, sizeof(struct FanModuleData),  0777)) < 0) {
        result = FAIL;
    } else if ((ShmFanModuleData = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }
    memset(ShmFanModuleData, 0, sizeof(struct FanModuleData));

    if ((MeterSMId = shmget(ShmRelayBdKey, sizeof(struct RelayModuleData),  0777)) < 0) {
        result = FAIL;
    } else if ((ShmRelayModuleData = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }
    memset(ShmRelayModuleData, 0, sizeof(struct RelayModuleData));

    if ((MeterSMId = shmget(ShmLedBdKey, sizeof(struct LedModuleData),  0777)) < 0) {
        result = FAIL;
    } else if ((ShmLedModuleData = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }
    memset(ShmLedModuleData, 0, sizeof(struct LedModuleData));

    if ((MeterSMId = shmget(ShmPsuKey, sizeof(struct PsuData),  0777)) < 0) {
        result = FAIL;
    } else if ((ShmPsuData = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }

    if ((MeterSMId = shmget(ShmOcppModuleKey, sizeof(struct OCPP16Data), 0777)) < 0) {
        result = FAIL;
    } else if ((ShmOCPP16Data = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }

    if ((MeterSMId = shmget(ShmCommonKey, sizeof(DcCommonInfo), IPC_CREAT | 0777)) < 0) {
        return FAIL;
    } else if ((ShmDcCommonData = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        return FAIL;
    }

    return result;
}

int InitComPort()
{
    int fd;
    struct termios tios;

    fd = open(relayRs485PortName, O_RDWR);
    if (fd <= 0) {
#ifdef SystemLogMessage
        log_error("Module_InternalComm. InitComPort NG\n");
#endif
        if (ShmStatusCodeData != NULL) {
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CsuInitFailed = 1;
        }
        sleep(5);
        return -1;
    }
    ioctl (fd, TCGETS, &tios);
    tios.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
    tios.c_lflag = 0;
    tios.c_iflag = 0;
    tios.c_oflag = 0;
    tios.c_cc[VMIN] = 0;
    tios.c_cc[VTIME] = (uint8_t)0;     // timeout 0.5 second
    tios.c_lflag = 0;
    tcflush(fd, TCIFLUSH);
    ioctl (fd, TCSETS, &tios);

    return fd;
}

//================================================
// Main process
//================================================
bool FindChargingInfoData(uint8_t target, struct ChargingInfoData **chargingData)
{
    for (uint8_t index = 0; index < CHAdeMO_QUANTITY; index++) {
        if (ShmSysConfigAndInfo->SysInfo.ChademoChargingData[index].Index
                == target) {
            chargingData[target] =
                &ShmSysConfigAndInfo->SysInfo.ChademoChargingData[index];
            return true;
        }
    }

    for (uint8_t index = 0; index < CCS_QUANTITY; index++) {
        if (ShmSysConfigAndInfo->SysInfo.CcsChargingData[index].Index
                == target) {
            chargingData[target] =
                &ShmSysConfigAndInfo->SysInfo.CcsChargingData[index];
            return true;
        }
    }

    for (uint8_t index = 0; index < GB_QUANTITY; index++) {
        if (ShmSysConfigAndInfo->SysInfo.GbChargingData[index].Index
                == target) {
            chargingData[target] =
                &ShmSysConfigAndInfo->SysInfo.GbChargingData[index];
            return true;
        }
    }

    return false;
}

bool FindAcChargingInfoData(uint8_t target, struct ChargingInfoData **acChargingData)
{
    if (target < AC_QUANTITY) {
        acChargingData[target] = &ShmSysConfigAndInfo->SysInfo.AcChargingData[target];
        return true;
    }

    return false;
}

void Initialization()
{
    bool isPass = false;

    for (uint8_t index = 0; index < ARRAY_SIZE(outputRelay.relay_event.relay_status); index++) {
        outputRelay.relay_event.relay_status[index] = 0x00;
    }

    while (!isPass) {
        isPass = true;
        for (uint8_t _index = 0; _index < gunCount; _index++) {
            if (!FindChargingInfoData(_index, &_chargingData[0])) {
                log_error("InternalComm : FindChargingInfoData false \n");
                isPass = false;
                break;
            }
        }

        sleep(1);
    }

    isPass = false;

    if (acgunCount > 0) {
        while (!isPass) {
            isPass = true;
            for (uint8_t _index = 0; _index < acgunCount; _index++) {
                if (!FindAcChargingInfoData(_index, &ac_chargingInfo[0])) {
                    log_error("EvComm : FindAcChargingInfoData false \n");
                    isPass = false;
                    break;
                }
            }

            sleep(1);
        }
    }
}

bool IsNoneMatchRelayStatus()
{
    bool result = false;

    if (
#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
        (regRelay.relay_event.bits.AC_Contactor != outputRelay.relay_event.bits.AC_Contactor) ||
        (regRelay.relay_event.bits.CCS_Precharge != outputRelay.relay_event.bits.CCS_Precharge) ||
#endif //!defined DD360 && !defined DD360Audi
        (regRelay.relay_event.bits.Gun1_P != outputRelay.relay_event.bits.Gun1_P) ||
        (regRelay.relay_event.bits.Gun1_N != outputRelay.relay_event.bits.Gun1_N) ||
        (regRelay.relay_event.bits.Gun2_P != outputRelay.relay_event.bits.Gun2_P) ||
        (regRelay.relay_event.bits.Gun2_N != outputRelay.relay_event.bits.Gun2_N)
#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
        ||
        (regRelay.relay_event.bits.Gun1_Parallel_P != outputRelay.relay_event.bits.Gun1_Parallel_P) ||
        (regRelay.relay_event.bits.Gun1_Parallel_N != outputRelay.relay_event.bits.Gun1_Parallel_N)
#endif //!defined DD360 && !defined DD360Audi
    ) {
        /*if (regRelay.relay_event.bits.AC_Contactor != outputRelay.relay_event.bits.AC_Contactor) {
            log_info("AC Contact Relay none match. \n");
        }

        if (regRelay.relay_event.bits.CCS_Precharge != outputRelay.relay_event.bits.CCS_Precharge) {
            log_info("CCS Precharge Relay none match. \n");
        }

        if (regRelay.relay_event.bits.Gun1_P != outputRelay.relay_event.bits.Gun1_P) {
            log_info("SMR1:D+ Relay none match. \n");
        }

        if (regRelay.relay_event.bits.Gun1_N != outputRelay.relay_event.bits.Gun1_N) {
            log_info("SMR1:D- Relay none match. \n");
        }

        if (regRelay.relay_event.bits.Gun2_P != outputRelay.relay_event.bits.Gun2_P) {
            log_info("SMR2:D+ Relay none match. \n");
        }

        if (regRelay.relay_event.bits.Gun2_N != outputRelay.relay_event.bits.Gun2_N) {
            log_info("SMR2:D- Relay none match. \n");
        }

        if (regRelay.relay_event.bits.Gun1_Parallel_P != outputRelay.relay_event.bits.Gun1_Parallel_P) {
            log_info("Parallel:D+ Relay none match. \n");
        }

        if (regRelay.relay_event.bits.Gun1_Parallel_N != outputRelay.relay_event.bits.Gun1_Parallel_N) {
            log_info("Parallel:D- Relay none match. \n");
        }*/

        result = true;
    }

    return result;
}

void MatchRelayStatus()
{
    // 因為 AC Contactor 沒有 Feedback,所以暫時先這樣處理
    //regRelay.relay_event.bits.AC_Contactor = outputRelay.relay_event.bits.AC_Contactor;
#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
    ShmSysConfigAndInfo->SysInfo.AcContactorStatus  = regRelay.relay_event.bits.AC_Contactor = outputRelay.relay_event.bits.AC_Contactor;
#endif //!defined DD360 && !defined DD360Audi

    regRelay.relay_event.bits.CCS_Precharge = outputRelay.relay_event.bits.CCS_Precharge;
    regRelay.relay_event.bits.Gun1_P = outputRelay.relay_event.bits.Gun1_P;
    regRelay.relay_event.bits.Gun1_N = outputRelay.relay_event.bits.Gun1_N;
    regRelay.relay_event.bits.Gun2_P = outputRelay.relay_event.bits.Gun2_P;
    regRelay.relay_event.bits.Gun2_N = outputRelay.relay_event.bits.Gun2_N;
    regRelay.relay_event.bits.Gun1_Parallel_P = outputRelay.relay_event.bits.Gun1_Parallel_P;
    regRelay.relay_event.bits.Gun1_Parallel_N = outputRelay.relay_event.bits.Gun1_Parallel_N;
}

void CheckRelayStatusByADC()
{
    if (ShmRelayModuleData->Gun1FuseOutputVolt > 0 && ShmRelayModuleData->Gun1RelayOutputVolt > 0 &&
            (ShmRelayModuleData->Gun1FuseOutputVolt == ShmRelayModuleData->Gun1RelayOutputVolt)) {
        // Relay 前後電壓一致
        _chargingData[0]->RelayK1K2Status = 0x01;
    } else {
        _chargingData[0]->RelayK1K2Status = 0x00;
    }

    if (ShmRelayModuleData->Gun2FuseOutputVolt > 0 && ShmRelayModuleData->Gun2RelayOutputVolt > 0 &&
            (ShmRelayModuleData->Gun2FuseOutputVolt == ShmRelayModuleData->Gun2RelayOutputVolt)) {
        // Relay 前後電壓一致
        _chargingData[1]->RelayK1K2Status = 0x01;
    } else {
        _chargingData[1]->RelayK1K2Status = 0x00;
    }
}

void SetGfdConfig(uint8_t index, uint8_t resister)
{
    gfd_config.index = index;
    gfd_config.state = resister;

    //log_info("************************GFD Vol = %d, GFD Res = %d \n", gfd_config.reqVol, gfd_config.resister);
    if (Config_Gfd_Value(Uart5Fd, ADDR_RELAY, &gfd_config) == PASS) {
//      log_info("Set reqVol = %f, resister = %d \n",
//              gfd_config.reqVol,
//              gfd_config.resister);
    }
}

void CableCheckDetected(uint8_t index)
{
    uint8_t targetID = 0;
    // Cable Check
    // 當火線上的電壓 = 車端要求的電壓電流
    // _chargingData[targetGun]->EvBatterytargetVoltage
    // 才可以開始偵測 1s
    // Warning : Rgfd <= 150 歐/V 假設電壓為 500V 則~ Rgfd <= 75000 歐
    // Pre-Warning : 150 歐/V < Rgfd <= 500 歐/V 假設電壓為 500V 則 75000 歐 < Rgfd <= 250000
    // SO Normal : Rgfd > 500 歐/V 假設電壓為 500 V 則 Rgfd > 250000 歐

    if (gunCount == 1) {
        if (strncmp((char *)&ShmSysConfigAndInfo->SysConfig.ModelName[7], "0", 1) != 0) {
            targetID = 0;
        } else if (strncmp((char *)&ShmSysConfigAndInfo->SysConfig.ModelName[9], "0", 1) != 0) {
            targetID = 1;
        }
    } else {
        targetID = index;
    }

    if ((_chargingData[index]->Type >= _Type_Chademo &&
            _chargingData[index]->Type <= _Type_GB) ||
            (_chargingData[index]->Type == 0x09 &&
             ShmSysConfigAndInfo->SysConfig.AlwaysGfdFlag)) {
        if ((_chargingData[index]->SystemStatus >= S_PREPARING_FOR_EVSE &&
                _chargingData[index]->SystemStatus <= S_TERMINATING) ||
                (_chargingData[index]->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
                 _chargingData[index]->SystemStatus <= S_CCS_PRECHARGE_ST1)) {
            if (_chargingData[index]->SystemStatus == S_PREPARING_FOR_EVSE &&
                    _chargingData[index]->RelayWeldingCheck == YES) {
                SetGfdConfig(targetID, GFD_CABLECHK);
            } else if (_chargingData[index]->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
                       _chargingData[index]->SystemStatus <= S_CCS_PRECHARGE_ST1) {
                SetGfdConfig(targetID, GFD_PRECHARGE);
            } else if (_chargingData[index]->SystemStatus >= S_CHARGING &&
                       _chargingData[index]->SystemStatus <= S_TERMINATING) {
                if (_chargingData[index]->Type == _Type_GB || _chargingData[index]->Type == _Type_Chademo) {
                    SetGfdConfig(targetID, GFD_IDLE);
                } else {
                    SetGfdConfig(targetID, GFD_CHARGING);
                }
            }
        } else if (_chargingData[index]->SystemStatus == S_COMPLETE ||
                   _chargingData[index]->SystemStatus == S_PREPARNING
                   || _chargingData[index]->SystemStatus == S_IDLE) {
            SetGfdConfig(targetID, GFD_IDLE);
        }
    }
}

void CheckOutputPowerOverCarReq(uint8_t index)
{
    float fireV = _chargingData[index]->FireChargingVoltage;
    float carV = _chargingData[index]->EvBatteryMaxVoltage * 10;

    if ((_chargingData[index]->EvBatterytargetVoltage * 10) > 1500 &&
            (_chargingData[index]->Type == _Type_Chademo ||
             _chargingData[index]->Type == _Type_CCS_2 ||
             _chargingData[index]->Type == _Type_GB)) {
        if (fireV >= (carV + (carV * 0.02))) {
            if (!_isOvpChkTimeFlag[index]) {
                if ((_chargingData[index]->PresentChargingVoltage * 10) >= VOUT_MIN_VOLTAGE * 10) {
                    gettimeofday(&_checkOutputVolProtectTimer[index], NULL);
                    _isOvpChkTimeFlag[index] = YES;
                }
            } else {
                log_info("[Module_InternalComm]CheckOutputPowerOverCarReq NG : fire = %f, battery = %f \n",
                         _chargingData[index]->FireChargingVoltage, (_chargingData[index]->EvBatterytargetVoltage * 10));
                log_error("[Module_InternalComm]CheckOutputPowerOverCarReq NG : fire = %f, battery = %f \n",
                          _chargingData[index]->FireChargingVoltage, (_chargingData[index]->EvBatterytargetVoltage * 10));
                if ((GetTimeoutValue(_checkOutputVolProtectTimer[index]) / 1000) >= OUTPUT_VOL_CHK_TIME) {
                    //if (_chargingData[index]->Type == _Type_Chademo) {
                    //    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemChademoOutputOVP = YES;
                    //} else if (_chargingData[index]->Type == _Type_CCS_2) {
                    //    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemCcsOutputOVP = YES;
                    //} else if (_chargingData[index]->Type == _Type_GB) {
                    //    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemGbOutputOVP = YES;
                    //}
                    if (_chargingData[index]->Type == _Type_Chademo) {
                        ShmDcCommonData->ConnectErrList[index].GunBits.ChaConnectOVP = YES;
                        if (strncmp((char *)_chargingData[index]->ConnectorAlarmCode, "", 6) == EQUAL) {
                            memcpy(_chargingData[index]->ConnectorAlarmCode, "012217", 6);
                        }
                    } else if (_chargingData[index]->Type == _Type_CCS_2) {
                        ShmDcCommonData->ConnectErrList[index].GunBits.CCSConnectOVP = YES;
                        if (strncmp((char *)_chargingData[index]->ConnectorAlarmCode, "", 6) == EQUAL) {
                            memcpy(_chargingData[index]->ConnectorAlarmCode, "012219", 6);
                        }
                    } else if (_chargingData[index]->Type == _Type_GB) {
                        ShmDcCommonData->ConnectErrList[index].GunBits.GBTConnectOVP = YES;
                        if (strncmp((char *)_chargingData[index]->ConnectorAlarmCode, "", 6) == EQUAL) {
                            memcpy(_chargingData[index]->ConnectorAlarmCode, "012221", 6);
                        }
                    }

                    _chargingData[index]->StopChargeFlag = YES;
                }
            }
        } else {
            if (_isOvpChkTimeFlag[index] == YES) {
                _isOvpChkTimeFlag[index] = NO;
            }
        }
    }
}

void CheckOutputVolNoneMatchFire(uint8_t index)
{
    if ((_chargingData[index]->EvBatterytargetVoltage * 10) > 1500 &&
            (_chargingData[index]->Type == _Type_Chademo ||
             _chargingData[index]->Type == _Type_CCS_2 ||
             _chargingData[index]->Type == _Type_GB)) {
        if (((_chargingData[index]->PresentChargingVoltage * 10) < _chargingData[index]->FireChargingVoltage - 300) ||
                ((_chargingData[index]->PresentChargingVoltage * 10) > _chargingData[index]->FireChargingVoltage + 300)) {
            if (!_isOutputNoneMatch[index]) {
                _isOutputNoneMatch[index] = YES;
                gettimeofday(&_checkOutputNoneMatchTimer[index], NULL);
            } else {
                if ((GetTimeoutValue(_checkOutputNoneMatchTimer[index]) / 1000) >= 5000) {
                    /*log_info("[Module_InternalComm]CheckOutputVolNoneMatchFire NG (%d) : pre = %f, fire = %f \n",
                            index, (_chargingData[index]->PresentChargingVoltage * 10), _chargingData[index]->FireChargingVoltage);
                    log_error("[Module_InternalComm]CheckOutputVolNoneMatchFire NG (%d): pre = %f, fire = %f \n",
                            index, (_chargingData[index]->PresentChargingVoltage * 10), _chargingData[index]->FireChargingVoltage);
                    _chargingData[index]->StopChargeFlag = YES;*/
                }
            }
        } else {
            _isOutputNoneMatch[index] = NO;
        }
    }
}

void CheckRelayWeldingStatus(uint8_t index)
{
    if (!_isRelayWelding[index]) {
        if ((_chargingData[index]->PresentChargingVoltage * 10) >= VOUT_MIN_VOLTAGE * 10) {
            gettimeofday(&_checkRelayWeldingTimer[index], NULL);
            _isRelayWelding[index] = YES;
        }
    } else {
        if ((GetTimeoutValue(_checkRelayWeldingTimer[index]) / 1000) >= 1000) {
            _chargingData[index]->RelayWeldingCheck = YES;
            return;
        }

        if (_chargingData[index]->FireChargingVoltage >= VOUT_MIN_VOLTAGE) {
            if (_chargingData[index]->Type == _Type_Chademo) {
                ShmStatusCodeData->FaultCode.FaultEvents.bits.ChademoOutputRelayWelding = YES;
            } else if (_chargingData[index]->Type == _Type_GB) {
                ShmStatusCodeData->FaultCode.FaultEvents.bits.GbOutputRelayWelding = YES;
            } else if (_chargingData[index]->Type == _Type_CCS_2) {
                ShmStatusCodeData->FaultCode.FaultEvents.bits.CcsOutputRelayWelding = YES;
            }

            log_info("CheckRelayWeldingStatus : fail \n");
            _chargingData[index]->StopChargeFlag = YES;
        }
    }
}

void GetPsuTempForFanSpeed()
{
    char temp = 0;

    for (uint8_t index = 0; index < ShmPsuData->GroupCount; index++) {
        for (uint8_t count = 0; count < ShmPsuData->PsuGroup[index].GroupPresentPsuQuantity; count++) {
            if (temp < ShmPsuData->PsuGroup[index].PsuModule[count].ExletTemp) {
                temp = ShmPsuData->PsuGroup[index].PsuModule[count].ExletTemp;
            }
        }
    }

    ShmSysConfigAndInfo->SysInfo.SystemAmbientTemp = temp;

    if (ShmSysConfigAndInfo->SysConfig.SwitchDebugFlag == NO) {
        if (ShmFanModuleData->TestFanSpeed == NORMAL_FAN_SPEED) {
            if (temp >= ENV_TEMP_MAX) {
                ShmFanModuleData->TestFanSpeed = MAX_FAN_SPEED;
            }
        } else if (ShmFanModuleData->TestFanSpeed == MAX_FAN_SPEED) {
            if (temp <= ENV_TEMP_MIN) {
                ShmFanModuleData->TestFanSpeed = NORMAL_FAN_SPEED;
            }
        } else {
            ShmFanModuleData->TestFanSpeed = NORMAL_FAN_SPEED;
        }
    }
}

void GetFanSpeedByFunction()
{
    if (ShmSysConfigAndInfo->SysConfig.SwitchDebugFlag == YES) {
        return;
    }

    // 風控修改 :
    // ******************************************************* //
    //
    //       當前PSU輸出總 KW       PSU Temp
    // 30 x -------------------- x ---------- + 14 x (PSU Temp - 45)
    //       當前樁最大功率 KW         45
    //
    // ******************************************************* //

    // 當前樁最大功率 KW : ShmPsuData->SystemAvailablePower
    unsigned int _maxPower = ShmPsuData->SystemAvailablePower;
    // 當前PSU輸出總 KW & PSU Temp :
    uint8_t temp = 0;
    float power = 0;

    for (uint8_t index = 0; index < ShmPsuData->GroupCount; index++) {
        for (uint8_t count = 0; count < ShmPsuData->PsuGroup[index].GroupPresentPsuQuantity; count++) {
            if (temp < ShmPsuData->PsuGroup[index].PsuModule[count].ExletTemp) {
                temp = ShmPsuData->PsuGroup[index].PsuModule[count].ExletTemp;
            }
        }
    }

    for (uint8_t gunIndex = 0; gunIndex < gunCount; gunIndex++) {
        power += (_chargingData[gunIndex]->PresentChargingPower * 10);
    }

    double _pw_rate = 0;
    if (_maxPower > 0) {
        _pw_rate = power / (double)_maxPower;
    }
    double _temp_rate = 0;
    if (temp > 0) {
        _temp_rate = (double)temp / 50;
    }
    uint8_t _temp_diff = 0;
    if (temp > 45) {
        _temp_diff = temp - 70;
    }

    ShmFanModuleData->TestFanSpeed = (((50 * _pw_rate * _temp_rate) + (0.5 * _temp_diff)) / 100) * MAX_FAN_SPEED;

    if (ShmFanModuleData->TestFanSpeed > MAX_FAN_SPEED) {
        ShmFanModuleData->TestFanSpeed = MAX_FAN_SPEED;
    }

    if (ShmFanModuleData->TestFanSpeed < 0) {
        ShmFanModuleData->TestFanSpeed = 0;
    }
//
//  printf("power = %f \n", power);
//  printf("_maxPower = %d \n", _maxPower);
//  printf("temp = %d \n", temp);
//
//  printf("_pw_rate = %f \n", _pw_rate);
//  printf("_temp_rate = %f \n", _temp_rate);
//  printf("_temp_diff = %d \n", _temp_diff);
//  printf("fan rate = %f \n", (30 * _pw_rate * _temp_rate + 14 * _temp_diff));
//  printf("ShmFanModuleData->TestFanSpeed = %d \n", ShmFanModuleData->TestFanSpeed);
}

void GetAcStatus()
{
    if (Query_AC_Status(Uart5Fd, ADDR_AC_PLUG, &acStatus) == PASS) {
        ShmSysConfigAndInfo->SysConfig.AcRatingCurrent = acStatus.MaxCurrent;

        if (ShmSysConfigAndInfo->SysConfig.AcMaxChargingCurrent == 0) {
            ShmSysConfigAndInfo->SysConfig.AcMaxChargingCurrent = ShmSysConfigAndInfo->SysConfig.AcRatingCurrent;
        }

        ac_chargingInfo[0]->ConnectorPlugIn = acStatus.CpStatus;
        //  log_info("CpStatus = %d \n", acStatus.CpStatus);
        //              printf("CurLimit = %d \n", acStatus.CurLimit);
        //              printf("PilotVol_P = %d \n", acStatus.PilotVol_P);
        //              printf("PilotVol_N = %d \n", acStatus.PilotVol_N);
        //              printf("LockStatus = %d \n", acStatus.LockStatus);
        //              printf("RelayStatus = %d \n", acStatus.RelayStatus);
        //              printf("ShutterStatus = %d \n", acStatus.ShutterStatus);
        //              printf("MeterStatus = %d \n", acStatus.MeterStatus);
        //              printf("PpStatus = %d \n", acStatus.PpStatus);
        //              printf("MaxCurrent = %d \n", acStatus.MaxCurrent);
        //              printf("RotateSwitchStatus = %d \n", acStatus.RelayStatus);
        //              printf("============================== \n");
        //
        //              ac_chargingInfo[0]->SystemStatus = acStatus.CpStatus;
    }
//  else
//      log_info("GetAcStatus return fail. \n");
}

void GetAcAlarmCode()
{
    if (Query_AC_Alarm_Code(Uart5Fd, ADDR_AC_PLUG, &acAlarmCode) == PASS) {
        CheckAlarmOccur();
    }
}

uint8_t GetChargingEnergy()
{
    return Query_Charging_Energy(Uart5Fd, ADDR_AC_PLUG, &acChargingEnergy);
}

uint8_t GetChargingCurrent()
{
    return Query_Charging_Current(Uart5Fd, ADDR_AC_PLUG, &acChargingCurrent);
}

void ChangeLedStatus()
{
    if (ac_chargingInfo[0]->SystemStatus == S_IDLE) {
        ledStatus.ActionMode = 1;
    } else if (ac_chargingInfo[0]->SystemStatus == S_PREPARNING) {
        ledStatus.ActionMode = 3;
    } else if (ac_chargingInfo[0]->SystemStatus == S_CHARGING) {
        ledStatus.ActionMode = 4;
    }

    Config_LED_Status(Uart5Fd, ADDR_AC_PLUG, &ledStatus);
}

void SetLegacyReq(uint8_t _switch)
{
    Config_Legacy_Req(Uart5Fd, ADDR_AC_PLUG, _switch);
}

void SetCpDuty(uint8_t _value)
{
    Config_Ac_Duty(Uart5Fd, ADDR_AC_PLUG, _value);
}

void ChangeToCsuMode()
{
    ac_chargingInfo[0]->IsModeChagned = Config_CSU_Mode(Uart5Fd, ADDR_AC_PLUG);

//  if (ac_chargingInfo[0]->IsModeChagned == PASS)
//  {
//      Config_Reset_MCU(Uart5Fd, ADDR_AC_PLUG);
//  }
}

void ChangeStartOrStopDateTime(uint8_t isStart)
{
    char cmdBuf[32];
    struct timeb csuTime;
    struct tm *tmCSU;

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

    sprintf(cmdBuf, "%04d-%02d-%02d %02d:%02d:%02d", tmCSU->tm_year + 1900,
            tmCSU->tm_mon + 1, tmCSU->tm_mday, tmCSU->tm_hour, tmCSU->tm_min,
            tmCSU->tm_sec);
    if (isStart) {
        strcpy((char *)ac_chargingInfo[0]->StartDateTime, cmdBuf);
    } else {
        strcpy((char *)ac_chargingInfo[0]->StopDateTime, cmdBuf);
    }
}

void OcppStartTransation(uint8_t gunIndex)
{
    if (strcmp((char *)ac_chargingInfo[0]->StartUserId, "") == EQUAL) {
        strcpy((char *)ShmOCPP16Data->StartTransaction[gunIndex].IdTag, (char *)ShmOCPP16Data->StartTransaction[gunIndex].IdTag);
    } else {
        strcpy((char *)ShmOCPP16Data->StartTransaction[gunIndex].IdTag, (char *)ac_chargingInfo[0]->StartUserId);
    }

    log_info("AC IdTag = %s \n", ShmOCPP16Data->StartTransaction[gunIndex].IdTag);
    ShmOCPP16Data->CpMsg.bits[gunIndex].StartTransactionReq = YES;
}

void OcppStopTransation(uint8_t gunIndex)
{
    if (strcmp((char *)ac_chargingInfo[0]->StartUserId, "") == EQUAL) {
        strcpy((char *)ShmOCPP16Data->StopTransaction[gunIndex].IdTag, (char *)ShmOCPP16Data->StopTransaction[gunIndex].IdTag);
    } else {
        strcpy((char *)ShmOCPP16Data->StopTransaction[gunIndex].IdTag, (char *)ac_chargingInfo[0]->StartUserId);
    }

    log_info("AC IdTag = %s \n", ShmOCPP16Data->StopTransaction[gunIndex].IdTag);
    ShmOCPP16Data->CpMsg.bits[gunIndex].StopTransactionReq = YES;
}

bool OcppRemoteStop(uint8_t gunIndex)
{
    bool result = ShmOCPP16Data->CsMsg.bits[gunIndex].RemoteStopTransactionReq;

    if (ShmOCPP16Data->CsMsg.bits[gunIndex].RemoteStopTransactionReq == YES) {
        strcpy((char *)ShmOCPP16Data->StopTransaction[gunIndex].StopReason, "Remote");
        ShmOCPP16Data->CsMsg.bits[gunIndex].RemoteStopTransactionReq = NO;
    }

    return result;
}

uint8_t isModeChange()
{
    uint8_t result = NO;

    if (ac_chargingInfo[0]->SystemStatus != ac_chargingInfo[0]->PreviousSystemStatus) {
        result = YES;
        ac_chargingInfo[0]->PreviousSystemStatus = ac_chargingInfo[0]->SystemStatus;
    }

    return result;
}

void AcChargeTypeProcess()
{
    if (acgunCount > 0) {
        //ac_chargingInfo[0]->SelfTest_Comp = YES;
        //ac_chargingInfo[0]->IsModeChagned = PASS;
        //---------------------------------------------
        if (ac_chargingInfo[0]->SelfTest_Comp == NO) {
            ac_chargingInfo[0]->IsModeChagned = NO;
            GetFwVersion_AC();
            GetAcModelName();
        } else if (ac_chargingInfo[0]->SelfTest_Comp == YES) {
            if (ac_chargingInfo[0]->IsModeChagned != PASS) {
                ChangeToCsuMode();
                return;
            }
            GetAcStatus();
            GetAcAlarmCode();

            uint8_t _status = S_NONE;

            if (ac_chargingInfo[0]->SystemStatus == S_IDLE && ac_chargingInfo[0]->IsErrorOccur) {
                _status = S_ALARM;
            } else if (acStatus.CpStatus == AC_SYS_A || ac_chargingInfo[0]->IsErrorOccur) {
                if (ac_chargingInfo[0]->SystemStatus == S_CHARGING) {
                    _status = S_TERMINATING;
                } else if (ac_chargingInfo[0]->SystemStatus >= S_TERMINATING) {
                    if (GetTimeoutValue(_ac_charging_comp) >= 10000000 && acStatus.CpStatus == AC_SYS_A) {
                        _status = S_IDLE;
                    }
                } else {
                    _status = S_IDLE;
                }
            } else if (ac_chargingInfo[0]->SystemStatus >= S_PREPARNING &&
                       ac_chargingInfo[0]->SystemStatus < S_CHARGING) {
                if (acStatus.CpStatus == AC_SYS_C && acStatus.RelayStatus == YES) {
                    _status = S_CHARGING;
                } else if (GetTimeoutValue(_ac_preparing) >= 30000000) {
                    _status = S_IDLE;
                }
            } else if ((acStatus.CpStatus == AC_SYS_B || ac_chargingInfo[0]->ConnectorPlugIn == AC_SYS_B) &&
                       ac_chargingInfo[0]->IsAvailable &&
                       !ac_chargingInfo[0]->IsErrorOccur &&
                       (ShmSysConfigAndInfo->SysInfo.WaitForPlugit == YES ||
                        ShmSysConfigAndInfo->SysConfig.AuthorisationMode == AUTH_MODE_DISABLE)) {
                if (ac_chargingInfo[0]->RemoteStartFlag == YES) {
                    log_info("** AC Remote \n");
                    ac_chargingInfo[0]->RemoteStartFlag = NO;
                    strcpy((char *)ac_chargingInfo[0]->StartUserId, "");
                    ShmSysConfigAndInfo->SysInfo.WaitForPlugit = NO;
                    _status = S_PREPARNING;
                } else if (ShmSysConfigAndInfo->SysInfo.OrderCharging == NO_DEFINE) {
                    log_info("** UserId = %s \n", ShmSysConfigAndInfo->SysConfig.UserId);
                    strcpy((char *)ac_chargingInfo[0]->StartUserId, (char *)ShmSysConfigAndInfo->SysConfig.UserId);
                    log_info("** CardNumber = %s \n", ac_chargingInfo[0]->StartUserId);
                    strcpy((char *)ShmSysConfigAndInfo->SysConfig.UserId, "");
                    ShmSysConfigAndInfo->SysInfo.WaitForPlugit = NO;
                    _status = S_PREPARNING;
                }
            } else if (ac_chargingInfo[0]->SystemStatus == S_CHARGING) {
                if (OcppRemoteStop(1)) {
                    _status = S_TERMINATING;
                }
            }

            //printf("_status = %d \n", _status);

            if (_status != S_NONE && ac_chargingInfo[0]->SystemStatus != _status) {
                ac_chargingInfo[0]->SystemStatus = _status;
            }

            // 設定限制最大充電電流 >= 6 ~ <= 32
            switch (ac_chargingInfo[0]->SystemStatus) {
            case S_IDLE:
            case S_ALARM: {
                if (isModeChange()) {
                    ac_chargingInfo[0]->PresentChargedEnergy = 0.0;
                    ac_chargingInfo[0]->PresentChargingVoltage = 0;
                    ac_chargingInfo[0]->ChargingFee = 0.0;
                    strcpy((char *)ac_chargingInfo[0]->StartDateTime, "");
                    strcpy((char *)ac_chargingInfo[0]->StopDateTime, "");
                    _beforeChargingTotalEnergy = 0.0;
                }

                ChangeLedStatus();
            }
            break;
            case S_PREPARNING: {
                if (isModeChange()) {
                    ShmSysConfigAndInfo->SysInfo.SystemPage = _LCM_NONE;
                    ShmSysConfigAndInfo->SysInfo.CurGunSelectedByAc = DEFAULT_AC_INDEX;
                    if (ShmSysConfigAndInfo->SysInfo.OrderCharging != NO_DEFINE) {
                        ShmSysConfigAndInfo->SysInfo.OrderCharging = NO_DEFINE;
                    }
                    gettimeofday(&_ac_preparing, NULL);
                }

                if (GetChargingEnergy() == PASS) {
                    //ac_chargingInfo[0]->PresentChargedEnergy = acChargingEnergy.Energy / 100;
                    _beforeChargingTotalEnergy = acChargingEnergy.Energy;
                }

                SetLegacyReq(YES);
                ChangeLedStatus();
            }
            break;
            case S_CHARGING: {
                if (isModeChange()) {
                    ftime(&_ac_startChargingTime);
                    OcppStartTransation(1);
                    ChangeStartOrStopDateTime(YES);
                    ShmSysConfigAndInfo->SysInfo.CurGunSelectedByAc = DEFAULT_AC_INDEX;
                }

                if (GetChargingEnergy() == PASS) {
                    if ((acChargingEnergy.Energy - _beforeChargingTotalEnergy) > 0) {
                        ac_chargingInfo[0]->PresentChargedEnergy += (acChargingEnergy.Energy - _beforeChargingTotalEnergy) / 100;
                        if (ShmSysConfigAndInfo->SysConfig.BillingData.isBilling) {
                            ac_chargingInfo[0]->ChargingFee += ac_chargingInfo[0]->PresentChargedEnergy * ShmSysConfigAndInfo->SysConfig.BillingData.Cur_fee;
                        }
                    }

                    _beforeChargingTotalEnergy = acChargingEnergy.Energy;
                }

                if (GetChargingCurrent() == PASS) {
                    ac_chargingInfo[0]->PresentChargingPower = (((float)(AC_DEFAULT_VOL * acChargingCurrent.OuputCurrentL1) / 10) / 1000);
                }

                ftime(&_ac_endChargingTime);
                ac_chargingInfo[0]->PresentChargedDuration = DiffTimeb(_ac_startChargingTime, _ac_endChargingTime);
                ac_chargingInfo[0]->PresentChargingVoltage = AC_DEFAULT_VOL;
                ac_chargingInfo[0]->PresentChargingCurrent = ((float)acChargingCurrent.OuputCurrentL1 / 10);

                // 用以判斷是否有在輸出
                ac_chargingInfo[0]->IsCharging = acStatus.RelayStatus;

                SetCpDuty(ShmSysConfigAndInfo->SysConfig.AcMaxChargingCurrent);
                ChangeLedStatus();
            }
            break;
            case S_TERMINATING: {
                if (isModeChange()) {
                    ChangeStartOrStopDateTime(NO);
                    gettimeofday(&_ac_charging_comp, NULL);
                }

                SetLegacyReq(NO);
                if (acStatus.RelayStatus == NO) {
                    ac_chargingInfo[0]->SystemStatus = S_COMPLETE;
                }
            }
            break;
            case S_COMPLETE: {
                if (isModeChange()) {
                    gettimeofday(&_ac_charging_comp, NULL);
                    ftime(&_ac_endChargingTime);
                    if (strcmp((char *)ac_chargingInfo[0]->StartDateTime, "") != EQUAL) {
                        // AC 固定為第2把槍
                        OcppStopTransation(1);
                    }

                    ChangeStartOrStopDateTime(NO);
                    ac_chargingInfo[0]->PresentChargedDuration = DiffTimeb(_ac_startChargingTime, _ac_endChargingTime);
                }
            }
            break;
            }
        }
    }
}

void ResetDetAlarmStatus(uint8_t gun)
{
    if (_chargingData[gun]->Type == _Type_Chademo) {
        if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemChademoOutputOVP == YES) {
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemChademoOutputOVP = NO;
        }
    } else if (_chargingData[gun]->Type == _Type_GB) {
        if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemGbOutputOVP == YES) {
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemGbOutputOVP = NO;
        }
    } else if (_chargingData[gun]->Type == _Type_CCS_2) {
        if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemCcsOutputOVP == YES) {
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemCcsOutputOVP = NO;
        }
    }
}

int main(void)
{
    uint8_t i = 0;
    if (InitShareMemory() == FAIL) {
        log_error("InitShareMemory NG\n");
        if (ShmStatusCodeData != NULL) {
            ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory = 1;
        }
        sleep(5);
        return 0;
    }

    gunCount = ShmSysConfigAndInfo->SysConfig.TotalConnectorCount;
    acgunCount = ShmSysConfigAndInfo->SysConfig.AcConnectorCount;
    // Open Uart5 for RB
    Uart5Fd = InitComPort();
    Initialization();
    sleep(1);

    if (Uart5Fd < 0) {
        log_info("(Internal) open port error. \n");
        return 0;
    }

    outputRelay.relay_event.bits.AC_Contactor = 0x00;
    outputRelay.relay_event.bits.CCS_Precharge = 0x00;
    outputRelay.relay_event.bits.Gun1_Parallel_P = 0x00;
    outputRelay.relay_event.bits.Gun1_Parallel_N = 0x00;
    outputRelay.relay_event.bits.Gun1_P = 0x00;
    outputRelay.relay_event.bits.Gun1_N = 0x00;
    outputRelay.relay_event.bits.Gun2_N = 0x00;
    outputRelay.relay_event.bits.Gun2_P = 0x00;
    if (Config_Relay_Output(Uart5Fd, ADDR_RELAY, &outputRelay) != PASS) {
        log_info("Config_Relay_Output fail \n");

    }

    cur_led_color.Connect_1_Red = COLOR_MIN_LV;
    cur_led_color.Connect_1_Green = COLOR_MIN_LV;
    cur_led_color.Connect_1_Blue = COLOR_MIN_LV;
    cur_led_color.Connect_2_Red = COLOR_MIN_LV;
    cur_led_color.Connect_2_Green = COLOR_MIN_LV;
    cur_led_color.Connect_2_Blue = COLOR_MIN_LV;

    //bool printRelayStatus = true;
    for (;;) {
        bool isCharging = false;

        // 程序開始之前~ 必須先確定 FW 版本與硬體版本,確認後!!~ 該模組才算是真正的 Initial Comp.
        if (ShmRelayModuleData->SelfTest_Comp == NO) {
            GetFwAndHwVersion_Relay();
            SetModelName_Relay(); //DS60-120 add
            SetRtcData_Relay();
            sleep(1);
        }

#if !defined NO_FAN_BOARD && !defined DD360ComBox
        if (ShmFanModuleData->SelfTest_Comp == NO) {
            GetFwAndHwVersion_Fan();
            SetModelName_Fan();
            SetRtcData_Fan();
            sleep(1);
            gettimeofday(&_priority_time, NULL);
        }
#endif //NO_FAN_BOARD

#if !defined DD360ComBox
        // 自檢階段處理,自檢階段如果讀不到版號則代表該系統沒有掛燈板
        if (ShmLedModuleData->SelfTest_Comp == NO) {
#if defined DD360 ||defined DD360Audi
            GetFwAndHwVersion_Led();
            sleep(1);
            gettimeofday(&_led_priority_time, NULL);
#else
            // 自檢階段
            if (ShmSysConfigAndInfo->SysInfo.SelfTestSeq <= _STEST_PSU_CAP) {
                GetFwAndHwVersion_Led();
                sleep(1);
                gettimeofday(&_led_priority_time, NULL);
            } else {
                // 自檢階段沒有問到版號
                if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.LedboardStestFail == NO) {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.LedboardStestFail = YES;
                }
            }
#endif //defined DD360 || defined DD360Audi
        }
#endif //!defined DD360ComBox

        AcChargeTypeProcess();

        if (ShmRelayModuleData->SelfTest_Comp == YES) {
            // ==============優先權最高 10 ms ==============
            // 輸出電壓
            GetPersentOutputVol();

#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
            // 三相輸入電壓
            GetPresentInputVol();
            // 讀取當前 AC relay 狀態
            regRelay.relay_event.bits.AC_Contactor = ShmSysConfigAndInfo->SysInfo.AcContactorStatus;

            GetRelayOutputStatus();
#endif //!defined DD360 && !defined DD360Audi

            for (i = 0; i < gunCount; i++) {
                // Cable check (Set)
                CableCheckDetected(i);

                // check k1 k2 relay 狀態
                CheckK1K2RelayOutput(i);

                // 依據當前各槍的狀態選擇 搭上/放開 Relay
                SetK1K2RelayStatus(i);

#if !defined DD360 && !defined DD360Audi && !defined DD360ComBox
                if (ShmSysConfigAndInfo->SysConfig.PhaseLossPolicy == YES) {
                    CheckPhaseLossStatus(i);
                }

                CheckAcInputOvpStatus(i);
#endif //!defined DD360 && !defined DD360Audi

                if (_chargingData[i]->SystemStatus == S_IDLE) {
                    _chargingData[i]->RelayWeldingCheck = NO;
                    _isRelayWelding[i] = NO;
                    _isOvpChkTimeFlag[i] = NO;
                    ResetDetAlarmStatus(i); //DS60-120 add
                }

                if (_chargingData[i]->SystemStatus == S_BOOTING ||
                        (_chargingData[i]->SystemStatus >= S_REASSIGN_CHECK && _chargingData[i]->SystemStatus <= S_COMPLETE) ||
                        (_chargingData[i]->SystemStatus >= S_CCS_PRECHARGE_ST0 && _chargingData[i]->SystemStatus <= S_CCS_PRECHARGE_ST1) ||
                        ShmSysConfigAndInfo->SysInfo.WaitForPlugit == YES ||
                        (ShmSysConfigAndInfo->SysInfo.PageIndex >= _LCM_AUTHORIZING && ShmSysConfigAndInfo->SysInfo.PageIndex <= _LCM_WAIT_FOR_PLUG)) {
                    _chargingData[i]->IsReadyToCharging = YES;
                    isCharging = true;

                    // 限定只有在槍類別為 GBT 的時候才做 relay welding 的判斷
                    //if (_chargingData[i]->Type == _Type_GB) {
                    //    if (_chargingData[i]->SystemStatus >= S_PREPARING_FOR_EVSE &&
                    //            _chargingData[i]->RelayWeldingCheck == NO) {
                    //        CheckRelayWeldingStatus(i);
                    //    }
                    //} else {
                    _chargingData[i]->RelayWeldingCheck = YES;
                    //}

                    if (_chargingData[i]->SystemStatus == S_CHARGING) {
                        CheckOutputPowerOverCarReq(i);
                        //CheckOutputVolNoneMatchFire(i);
                    } else {
                        _isOutputNoneMatch[i] = NO;
                    }
                } else {
                    _chargingData[i]->IsReadyToCharging = NO;
                }
            }

            // Cable check (Get)
            GetGfdAdc();

            // 橋接 relay
            SetParalleRelayStatus();

            // 搭上 AC Contactor
            //if (isCharging) {
            //    outputRelay.relay_event.bits.AC_Contactor = YES;
            //} else {
            //    outputRelay.relay_event.bits.AC_Contactor = NO;
            //}

            if (isCharging ||
                    (ShmPsuData->Work_Step >= _TEST_MODE &&
                     ShmPsuData->Work_Step <= _TEST_MODE)) {
                isStopChargingCount = false;
                outputRelay.relay_event.bits.AC_Contactor = YES;
            } else {
                if (!isStopChargingCount) {
                    gettimeofday(&_close_ac_contactor, NULL);
                    isStopChargingCount = true;
                } else {
                    if ((outputRelay.relay_event.bits.AC_Contactor == YES &&
                            GetTimeoutValue(_close_ac_contactor) / 1000 >= (TEN_MINUTES * 1000))) {
                        outputRelay.relay_event.bits.AC_Contactor = NO;
                    }
                }
            }

            if (ShmPsuData->Work_Step >= _TEST_MODE && ShmPsuData->Work_Step <= _TEST_MODE) {
                outputRelay.relay_event.bits.Gun1_N = outputRelay.relay_event.bits.Gun1_P = YES;
            }

            // 搭上/鬆開 Relay
            if (IsNoneMatchRelayStatus()) {
                if (Config_Relay_Output(Uart5Fd, ADDR_RELAY, &outputRelay)) {
                    //regRelay.relay_event.bits.AC_Contactor = ShmSysConfigAndInfo->SysInfo.AcContactorStatus;
                    regRelay.relay_event.bits.CCS_Precharge = outputRelay.relay_event.bits.CCS_Precharge;
                    regRelay.relay_event.bits.Gun1_P = outputRelay.relay_event.bits.Gun1_P;
                    regRelay.relay_event.bits.Gun1_N = outputRelay.relay_event.bits.Gun1_N;
                    regRelay.relay_event.bits.Gun2_P = outputRelay.relay_event.bits.Gun2_P;
                    regRelay.relay_event.bits.Gun2_N = outputRelay.relay_event.bits.Gun2_N;
                    regRelay.relay_event.bits.Gun1_Parallel_P = outputRelay.relay_event.bits.Gun1_Parallel_P;
                    regRelay.relay_event.bits.Gun1_Parallel_N = outputRelay.relay_event.bits.Gun1_Parallel_N;

                    //log_info("Match Relay, AC = %x, g1_p = %x, g1_n = %x, g2_p = %x, g2_n = %x, pre = %x, bri_p = %x, bri_n = %x \n",
                    //            regRelay.relay_event.bits.AC_Contactor,
                    //            regRelay.relay_event.bits.Gun1_P,
                    //            regRelay.relay_event.bits.Gun1_N,
                    //            regRelay.relay_event.bits.Gun2_P,
                    //            regRelay.relay_event.bits.Gun2_N,
                    //            regRelay.relay_event.bits.CCS_Precharge,
                    //            regRelay.relay_event.bits.Gun1_Parallel_P,
                    //            regRelay.relay_event.bits.Gun1_Parallel_N);

                }
            }
        }

#if !defined NO_FAN_BOARD && !defined DD360ComBox
        if (ShmFanModuleData->SelfTest_Comp == YES ||
                strlen((char *)ShmSysConfigAndInfo->SysInfo.FanModuleFwRev) != 0 ||
                ShmSysConfigAndInfo->SysInfo.FanModuleFwRev[0] != '\0') {
            ShmFanModuleData->SelfTest_Comp = YES;

            if (GetTimeoutValue(_priority_time) / 1000 >= 1000) {
                //GetPsuTempForFanSpeed();
                GetFanSpeedByFunction();
                GetFanSpeed();
                ShmSysConfigAndInfo->SysInfo.SystemFanRotaSpeed = _setFanSpeed;
                gettimeofday(&_priority_time, NULL);

                ShmFanModuleData->SetFan1Speed = ShmFanModuleData->TestFanSpeed;
                ShmFanModuleData->SetFan2Speed = ShmFanModuleData->TestFanSpeed;
                ShmFanModuleData->SetFan3Speed = ShmFanModuleData->TestFanSpeed;
                ShmFanModuleData->SetFan4Speed = ShmFanModuleData->TestFanSpeed;

                //log_info("set fan = %d \n", ShmFanModuleData->SetFan1Speed);
                SetFanModuleSpeed();
            }
        }
#endif //NO_FAN_BOARD

#if !defined DD360ComBox
        if (ShmLedModuleData->SelfTest_Comp == YES) {
            if (GetTimeoutValue(_led_priority_time) / 1000 >= 1000) {
                if (gunCount == 1) {
                    SetLedColor(_chargingData[0], _chargingData[0]);
                } else if (gunCount == 2) {
                    SetLedColor(_chargingData[0], _chargingData[1]);
                }

                gettimeofday(&_led_priority_time, NULL);
            }
        }
#endif //defined DD360ComBox

        usleep(10000);
    }

    return FAIL;
}