#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/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.h"
#include "internalComm.h"
#include "Config.h"

#define ARRAY_SIZE(A)       (sizeof(A) / sizeof(A[0]))
#define PASS                1
#define FAIL                -1
#define YES                 1
#define NO                  0
#define STOP                0
#define START               1
#define RELAY_CHECK_TIME    5       // s
#define OUTPUT_VOL_CHK_TIME 200     // ms
#define TEN_MINUTES         600     // s
//#define TEN_MINUTES           5
#define ENV_TEMP_MIN        45
#define ENV_TEMP_MAX        50
#define DEFAULT_AC_INDEX    2
#define EQUAL               0
#define COLOR_MAX_LV        100
#define COLOR_MIN_LV        0

#define AC_DEFAULT_VOL      220

#define NO_DEFINE           255
#define NDEFAULT_AC_INDEX   2

#define OVP_UVP_CHK_COUNT   3

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

#define VIN_MAX_VOLTAGE_IEC         285 // 大於該值 : OVP
#define VIN_MAX_REV_VOLTAGE_IEC     275 // 小於賦歸 OVP
#define VIN_MIN_VOLTAGE_IEC         160 // 小於該值 : UVP
#define VIN_MIN_REV_VOLTAGE_IEC     170 // 大於賦歸 UVP

#define VIN_MAX_VOLTAGE_UL          315 // 大於該值 : OVP // 美規 (W)
#define VIN_MAX_REV_VOLTAGE_UL      305 // 小於賦歸 OVP
#define VIN_MIN_VOLTAGE_UL          210 // 小於該值 : UVP
#define VIN_MIN_REV_VOLTAGE_UL      220 // 大於賦歸 UVP

#define VIN_DROP_VOLTAGE    150 // 小於該值 : ac drop

#define VOUT_MAX_VOLTAGE    995
#define VOUT_MIN_VOLTAGE    150
#define IOUT_MAX_CURRENT    50

#define MAX_FAN_SPEED       14000
#define MIN_FAN_SPEED       3000
#define NORMAL_FAN_SPEED    7000

// GFD Status
#define GFD_IDLE            0
#define GFD_CABLECHK        1
#define GFD_PRECHARGE       2
#define GFD_CHARGING        3

// LED Intensity (rate)
#define LED_INTENSITY_DARKEST       0.2
#define LED_INTENSITY_MEDIUM        0.6
#define LED_INTENSITY_BRIGHTEST     1

// EE Spec
#define LED_BRIGHTNESS_LV_HIGH      1
#define LED_BRIGHTNESS_LV_MID       0.5
#define LED_BRIGHTNESS_LV_LOW       0.2

// 最小切換 Relay 電壓
#define SELF_TO_CHANGE_RELAY_STATUS         600
// 透過電壓確認 Relay 是否搭上的依據電壓
#define CHECK_RELAY_STATUS                  300
#define CHECK_RELAY_STATUS_GAP              100
// 安全在停止充電程序中斷開 Relay 的電流
#define SEFETY_SWITCH_RELAY_CUR             50//20 DS60-120 modify
// 確認 Relay Welding 電壓
#define RELAY_WELDING_DET                   300

byte gunCount;
byte 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];

#if !defined METER_ENABLE
bool _isRelayWelding[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY];
struct timeval _checkRelayWeldingTimer[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY];
#endif //!defined METER_ENABLE
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

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

bool FindChargingInfoData(byte 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;
byte _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;

#define AC_OVP                      1
#define AC_UVP                      2
#define AC_OCP                      4
#define AC_OTP                      8
#define AC_GMI_FAULT                16
#define AC_CP_ERROR                 32
#define AC_AC_LEAKAGE               64
#define AC_DC_LEAKAGE               128
#define AC_SYSTEM_SELFTEST_FAULT    256
#define AC_HANDSHAKE_TIMEOUT        512
#define AC_EMC_STOP                 1024
#define AC_RELAY_WELDING            2048
#define AC_GF_MODULE_FAULT          4096
#define AC_SHUTTER_FAULT            8192
#define AC_LOCKER_FAULT             16384
#define AC_POWER_DROP               32768
#define AC_CIRCUIT_SHORT            65536
#define AC_ROTARY_SWITCH_FAULT      131072
#define AC_RELAY_DRIVE_FAULT        262144

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

void PRINTF_FUNC(char *string, ...);

int StoreLogMsg(const char *fmt, ...);
unsigned long GetTimeoutValue(struct timeval _sour_time);

#define DEBUG_INFO(format, args...) StoreLogMsg("[%s:%d][%s][Info] "format, __FILE__, __LINE__, __FUNCTION__, ##args)
#define DEBUG_WARN(format, args...) StoreLogMsg("[%s:%d][%s][Warn] "format, __FILE__, __LINE__, __FUNCTION__, ##args)
#define DEBUG_ERROR(format, args...) StoreLogMsg("[%s:%d][%s][Error] "format, __FILE__, __LINE__, __FUNCTION__, ##args)

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

void PRINTF_FUNC(char *string, ...)
{
    va_list args;
    char buffer[4096];
    va_start(args, string);
    vsnprintf(buffer, sizeof(buffer), string, args);
    va_end(args);

    DEBUG_INFO("%s \n", buffer);
}

//==========================================
// 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);
        //PRINTF_FUNC("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);
        //PRINTF_FUNC("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);
        //PRINTF_FUNC("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);
        //PRINTF_FUNC("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);
        //PRINTF_FUNC("GetFwAndHwVersion_Led s1 = %s \n", ver.Version_FW);
        ShmLedModuleData->SelfTest_Comp = YES;
    } else {
        //PRINTF_FUNC("GetFwAndHwVersion_Led fail \n");
    }

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

void GetFwVersion_AC()
{
    if (Query_FW_Ver(Uart5Fd, Addr.AcPlug, &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.AcPlug, ShmSysConfigAndInfo->SysConfig.AcModelName) == PASS) {
        PRINTF_FUNC("ac model name = %s \n", ShmSysConfigAndInfo->SysConfig.AcModelName);
    }
}

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

    ftime(&csuTime);
    tmCSU = localtime(&csuTime.time);
    //  PRINTF_FUNC("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) {
        //PRINTF_FUNC("SetRtc (RB) sucessfully. \n");
    }
}

void SetModelName_Relay()
{
    if (Config_Model_Name(Uart5Fd, Addr.Relay, ShmSysConfigAndInfo->SysConfig.ModelName) == PASS) {
        //PRINTF_FUNC("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);
    //  PRINTF_FUNC("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) {
        //PRINTF_FUNC("SetRtc (FB) sucessfully. \n");
    }
}

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

// AC 三相輸入電壓
void GetPresentInputVol()
{
    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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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) {
                    PRINTF_FUNC("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()
{
    if (Query_Present_OutputVoltage(Uart5Fd, Addr.Relay, &outputVoltage) == PASS) {
//      PRINTF_FUNC("Conn1 fuse 1 = %f \n", outputVoltage.behindFuse_Voltage_C1);
//      PRINTF_FUNC("Conn1 relay 1 = %f \n", outputVoltage.behindRelay_Voltage_C1);
//      PRINTF_FUNC("Conn2 fuse 2 = %f \n", outputVoltage.behindFuse_Voltage_C2);
//      PRINTF_FUNC("Conn2 relay 2 = %f \n", outputVoltage.behindRelay_Voltage_C2);

        //PRINTF_FUNC("outputVoltage.behindFuse_Voltage_C1 = %f \n", outputVoltage.behindFuse_Voltage_C1);
        //PRINTF_FUNC("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 (int index = 0; index < gunCount; index++) {
            if (index == 0) {
                //if (_chargingData[index]->Evboard_id == 0x01) {
                if (index == 0) {
#if !defined DD360 && !defined DD360Audi
                    _chargingData[index]->FuseChargingVoltage = ShmRelayModuleData->Gun1FuseOutputVolt;
#else
                    _chargingData[index]->PresentChargingCurrent = ShmRelayModuleData->Gun1FuseOutputVolt / 10;
                    _chargingData[index]->PresentChargingVoltage = _chargingData[index]->FireChargingVoltage / 10;
                    _chargingData[index]->FuseChargingVoltage = _chargingData[index]->FireChargingVoltage;
#endif //!defined DD360 && !defined DD360Audi
                    _chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun1RelayOutputVolt;

                    //} else if (_chargingData[index]->Evboard_id == 0x02) {
                } else if (index == 1) {
#if !defined DD360 && !defined DD360Audi
                    _chargingData[index]->FuseChargingVoltage = ShmRelayModuleData->Gun2FuseOutputVolt;
#else
                    _chargingData[index]->PresentChargingCurrent = ShmRelayModuleData->Gun2FuseOutputVolt / 10;
                    _chargingData[index]->PresentChargingVoltage = _chargingData[index]->FireChargingVoltage / 10;
                    _chargingData[index]->FuseChargingVoltage = _chargingData[index]->FireChargingVoltage;
#endif //!defined DD360 && !defined DD360Audi
                    _chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun2RelayOutputVolt;
                }
            } else if (index == 1) {
#if !defined DD360 && !defined DD360Audi
                _chargingData[index]->FuseChargingVoltage = ShmRelayModuleData->Gun2FuseOutputVolt;
#else
                _chargingData[index]->PresentChargingCurrent = ShmRelayModuleData->Gun2FuseOutputVolt / 10;
                _chargingData[index]->PresentChargingVoltage = _chargingData[index]->FireChargingVoltage / 10;
                _chargingData[index]->FuseChargingVoltage = _chargingData[index]->FireChargingVoltage;
#endif //!defined DD360 && !defined DD360Audi
                _chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun2RelayOutputVolt;
            }

            //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()
{
    //PRINTF_FUNC("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];
//      PRINTF_FUNC("SystemFanRotaSpeed_1 = %d \n", fanSpeed.speed[0]);
//      PRINTF_FUNC("SystemFanRotaSpeed_2 = %d \n", fanSpeed.speed[1]);
//      PRINTF_FUNC("SystemFanRotaSpeed_3 = %d \n", fanSpeed.speed[2]);
//      PRINTF_FUNC("SystemFanRotaSpeed_4 = %d \n", fanSpeed.speed[3]);
        // Config_Fan_Speed(Uart5Fd, Addr.Fan, &fanSpeed[0]);
        //SysInfoData (SystemFanRotaSpeed)
    }
}

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

// 確認 K1 K2 relay 的狀態
void CheckK1K2RelayOutput(byte index)
{
    if (index == 0) {
        //if (_chargingData[index]->Evboard_id == 0x01) {
        if (index == 0) {
            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 (gunCount == 1) {
#if !defined DD360 && !defined DD360Audi
                    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
                } else {
                    if (_chargingData[index]->SystemStatus == S_CCS_PRECHARGE_ST0) {
                        _chargingData[index]->RelayKPK2Status = YES;
                    } else {
                        _chargingData[index]->RelayKPK2Status = NO;
                    }
                }
            }
            //} else if (_chargingData[index]->Evboard_id == 0x02) {
        } else if (index == 1) {
            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
                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
            }
        }
    } else if (index == 1) {
        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
            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
        }
    }

#if !defined DD360 && !defined DD360Audi
    //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

//  PRINTF_FUNC("Check Relay Output. index = %d, RelayKPK2Status = %d, BridgeRelayStatus = %d \n",
//          index, _chargingData[index]->RelayKPK2Status, ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus);
}

void GetGfdAdc()
{
    // 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 (int i = 0; i < gunCount; i++) {
            if (_chargingData[i]->Type == 0x09 && !ShmSysConfigAndInfo->SysConfig.AlwaysGfdFlag) {
                if ((_chargingData[i]->PresentChargingVoltage * 10) >= VOUT_MIN_VOLTAGE) {
                    _chargingData[i]->GroundFaultStatus = GFD_PASS;
                }
                continue;
            }

            if (i == 0) {
                if (gfd_adc.result_conn1 == GFD_WARNING) {
                    gfd_adc.result_conn1 = GFD_PASS;
                }
                _chargingData[i]->GroundFaultStatus = gfd_adc.result_conn1;
//              PRINTF_FUNC("GFD ******** Result = %d, Step = %d, R = %d, Vol = %d \n",
//                      _chargingData[i]->GroundFaultStatus, gfd_adc.rb_step_1, gfd_adc.Resister_conn1, gfd_adc.voltage_conn1);
                if (_chargingData[i]->GroundFaultStatus == GFD_FAIL) {
                    PRINTF_FUNC("GFD Fail. index = %d, Step = %d, R = %d, Vol = %d \n",
                                i, gfd_adc.rb_step_1, gfd_adc.Resister_conn1, gfd_adc.voltage_conn1);
                } else if (_chargingData[i]->GroundFaultStatus == GFD_PASS ||
                           _chargingData[i]->GroundFaultStatus == GFD_WARNING) {
                    if (_chargingData[i]->GroundFaultStatus == GFD_WARNING) {
                        PRINTF_FUNC("GFD Warning. index = %d, Result = %d, R = %d, Vol = %d \n",
                                    i, _chargingData[i]->GroundFaultStatus, gfd_adc.Resister_conn1, gfd_adc.voltage_conn1);
                    }
                }
            } else if (i == 1) {
                if (gfd_adc.result_conn2 == GFD_WARNING) {
                    gfd_adc.result_conn2 = GFD_PASS;
                }
                _chargingData[i]->GroundFaultStatus = gfd_adc.result_conn2;
                if (_chargingData[i]->GroundFaultStatus == GFD_FAIL) {
                    PRINTF_FUNC("GFD Fail. index = %d, Step = %d, R = %d, Vol = %d \n",
                                i, gfd_adc.rb_step_2, gfd_adc.Resister_conn2, gfd_adc.voltage_conn2);
                } else if (_chargingData[i]->GroundFaultStatus == GFD_PASS ||
                           _chargingData[i]->GroundFaultStatus == GFD_WARNING) {
                    if (_chargingData[i]->GroundFaultStatus == GFD_WARNING) {
                        PRINTF_FUNC("GFD Warning. index = %d, Result = %d, R = %d, Vol = %d \n",
                                    i, _chargingData[i]->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
            PRINTF_FUNC("RB AC Main Breaker. \n");
        }

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

        if (gpio_in.Door_Open == 1) {
            // Door Open
            PRINTF_FUNC("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
            PRINTF_FUNC("RB AC Drop. \n");
        }

        if (gpio_in.Emergency_IO == 1) {
            // Emergency IO ON
            PRINTF_FUNC("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
        //PRINTF_FUNC("aux1 = %x, \n", auxPower.voltage[0]);
        //PRINTF_FUNC("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) {
            //PRINTF_FUNC("successfully Fan\n");
        }
    }
}

//==========================================
// Common Function
//==========================================
void SetK1K2RelayStatus(byte 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;
    }

    if (_chargingData[index]->SystemStatus < S_PREPARING_FOR_EVSE) {
        //if (_chargingData[index]->Evboard_id == 0x01) {
        if (index == 0) {
            if (regRelay.relay_event.bits.Gun1_P == YES) {
                outputRelay.relay_event.bits.Gun1_P = NO;
            } else if (regRelay.relay_event.bits.Gun1_N == YES) {
                outputRelay.relay_event.bits.Gun1_N = NO;
            }

            if (gunCount == 1 && _chargingData[index]->Type == _Type_CCS_2) {
                if (regRelay.relay_event.bits.CCS_Precharge == YES) {
                    outputRelay.relay_event.bits.CCS_Precharge = NO;
                }
            }
            //} else if (_chargingData[index]->Evboard_id == 0x02) {
        } else if (index == 1) {
            if (regRelay.relay_event.bits.Gun2_P == YES) {
                outputRelay.relay_event.bits.Gun2_P = NO;
            } else if (regRelay.relay_event.bits.Gun2_N == YES) {
                outputRelay.relay_event.bits.Gun2_N = NO;
            }

            if (_chargingData[index]->Type == _Type_CCS_2) {
                if (regRelay.relay_event.bits.CCS_Precharge == YES) {
                    outputRelay.relay_event.bits.CCS_Precharge = NO;
                }
            }
        }
    } else if ((_chargingData[index]->SystemStatus >= S_PREPARING_FOR_EVSE &&
                _chargingData[index]->SystemStatus <= S_CHARGING)) {
#if !defined METER_ENABLE
        if (_chargingData[index]->RelayWeldingCheck == YES)
#endif //!defined METER_ENABLE
        {
            //if (_chargingData[index]->Evboard_id == 0x01) {
            if (index == 0) {
                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;
                }
                //} else if (_chargingData[index]->Evboard_id == 0x02) {
            } else if (index == 1) {
                if (regRelay.relay_event.bits.Gun2_N == NO) {
                    outputRelay.relay_event.bits.Gun2_N = YES;
                } else if (regRelay.relay_event.bits.Gun2_P == NO) {
                    outputRelay.relay_event.bits.Gun2_P = YES;
                }
            }
        }
    } else if ((_chargingData[index]->SystemStatus >= S_TERMINATING &&
                _chargingData[index]->SystemStatus <= S_COMPLETE) ||
               _chargingData[index]->SystemStatus == S_ALARM) {
        if ((_chargingData[index]->PresentChargingCurrent * 10) <= SEFETY_SWITCH_RELAY_CUR) {
            //if (_chargingData[index]->Evboard_id == 0x01) {
            if (index == 0) {
                if (regRelay.relay_event.bits.Gun1_P == YES) {
                    outputRelay.relay_event.bits.Gun1_P = NO;
                } else if (regRelay.relay_event.bits.Gun1_N == YES) {
                    outputRelay.relay_event.bits.Gun1_N = NO;
                }
                //} else if (_chargingData[index]->Evboard_id == 0x02) {
            } else if (index == 1) {
                if (regRelay.relay_event.bits.Gun2_P == YES) {
                    outputRelay.relay_event.bits.Gun2_P = NO;
                } else if (regRelay.relay_event.bits.Gun2_N == YES) {
                    outputRelay.relay_event.bits.Gun2_N = NO;
                }
            }
        }
    } else if (_chargingData[index]->SystemStatus == S_CCS_PRECHARGE_ST0) {
        //if (_chargingData[index]->Evboard_id == 0x01) {
        if (index == 0) {
#if !defined DD360 && !defined DD360Audi
            if (_chargingData[index]->Type == _Type_CCS_2) {
                if (gunCount == 1) {
                    if (regRelay.relay_event.bits.CCS_Precharge == NO) {
                        outputRelay.relay_event.bits.CCS_Precharge = YES;
                    } else if (regRelay.relay_event.bits.CCS_Precharge == YES) {
                        outputRelay.relay_event.bits.Gun1_P = NO;
                    }
                }
            }
#endif //!defined DD360 && !defined DD360Audi
            //} else if (_chargingData[index]->Evboard_id == 0x02) {
        } else if (index == 1) {
#if !defined DD360 && !defined DD360Audi
            if (_chargingData[index]->Type == _Type_CCS_2) {
                if (regRelay.relay_event.bits.CCS_Precharge == NO) {
                    outputRelay.relay_event.bits.CCS_Precharge = YES;
                } else if (regRelay.relay_event.bits.CCS_Precharge == YES) {
                    outputRelay.relay_event.bits.Gun2_P = NO;
                }
            }
#endif //!defined DD360 && !defined DD360Audi
        }
    } else if (_chargingData[index]->SystemStatus == S_CCS_PRECHARGE_ST1) {
        //if (_chargingData[index]->Evboard_id == 0x01) {
        if (index == 0) {
#if !defined DD360 && !defined DD360Audi
            if (_chargingData[index]->Type == _Type_CCS_2) {
                if (gunCount == 1) {
                    if (regRelay.relay_event.bits.Gun1_P == NO) {
                        outputRelay.relay_event.bits.Gun1_P = YES;
                    } else if (regRelay.relay_event.bits.Gun1_P == YES) {
                        outputRelay.relay_event.bits.CCS_Precharge = NO;
                    }
                }
            }
#endif //!defined DD360 && !defined DD360Audi
            //} else if (_chargingData[index]->Evboard_id == 0x02) {
        } else if (index == 1) {
#if !defined DD360 && !defined DD360Audi
            if (_chargingData[index]->Type == _Type_CCS_2) {
                if (regRelay.relay_event.bits.Gun2_P == NO) {
                    outputRelay.relay_event.bits.Gun2_P = YES;
                } else if (regRelay.relay_event.bits.Gun2_P == YES) {
                    outputRelay.relay_event.bits.CCS_Precharge = NO;
                }
            }
#endif //!defined DD360 && !defined DD360Audi
        }
    }
}

void CheckAcInputOvpStatus(byte 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)
//              {
//                  PRINTF_FUNC("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)
//              {
//                  PRINTF_FUNC("UL _psuInputVolR = %f, _psuInputVolS = %f, _psuInputVolT = %f \n",
//                          _psuInputVolR, _psuInputVolS, _psuInputVolT);
//                  _chargingData[index]->StopChargeFlag = YES;
//              }
//          }
//      }
//      else
        //DEBUG_INFO("CheckAcInputOvpStatus\r\n");
        _chargingData[index]->StopChargeFlag = YES;
    }
}

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

void SetParalleRelayStatus()
{
    // 之後雙槍單模機種,橋接都會上
    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 (byte 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)
{
    byte _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 defined METER_ENABLE
    if ((MeterSMId = shmget(ShmCommonKey, sizeof(struct DcCommonInformation), IPC_CREAT | 0777)) < 0) {
        result = FAIL;
    } else if ((ShmDcCommonData = shmat(MeterSMId, NULL, 0)) == (void *) - 1) {
        result = FAIL;
    }
#endif//defined METER_ENABLE

    return result;
}

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

    fd = open(relayRs485PortName, O_RDWR);
    if (fd <= 0) {
#ifdef SystemLogMessage
        DEBUG_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] = (byte)0;     // timeout 0.5 second
    tios.c_lflag = 0;
    tcflush(fd, TCIFLUSH);
    ioctl (fd, TCSETS, &tios);

    return fd;
}

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

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

    for (byte 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(byte target, struct ChargingInfoData **acChargingData)
{
    if (target < AC_QUANTITY) {
        acChargingData[target] = &ShmSysConfigAndInfo->SysInfo.AcChargingData[target];
        return true;
    }

    return false;
}

void Initialization()
{
    bool isPass = false;

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

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

        sleep(1);
    }

    isPass = false;

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

            sleep(1);
        }
    }
}

#if defined METER_ENABLE
void StartCheckRelayInfo(byte _chkIndex, byte toState)
{
    if (ShmDcCommonData->RelayCheckStatus[_chkIndex] == STOP) {
        gettimeofday(&_relayStateChkTimer[_chkIndex], NULL);
        ShmDcCommonData->RelayCheckStatus[_chkIndex] = START;
    } else {
        if ((GetTimeoutValue(_relayStateChkTimer[_chkIndex]) / 1000) > RELAY_CHECK_TIME * 1000) {
            //PRINTF_FUNC ("relay welding or driving fault = %d \n", _chkIndex);
            if (toState == 1) {
                ShmDcCommonData->RelayCheckStatus[_chkIndex] = RELAY_STATUS_ERROR_DRIVING;
            } else {
                ShmDcCommonData->RelayCheckStatus[_chkIndex] = RELAY_STATUS_ERROR_WELDING;
            }
        }
    }
}

void StopCheckRelayInfo(byte _chkIndex)
{
    if (ShmDcCommonData->RelayCheckStatus[_chkIndex] != STOP) {
        ShmDcCommonData->RelayCheckStatus[_chkIndex] = STOP;
    }
}
#endif //defined METER_ENABLE

bool IsNoneMatchRelayStatus()
{
    bool result = false;

    if (
#if !defined DD360 && !defined DD360Audi
        (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
        ||
        (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) {
            PRINTF_FUNC("AC Contact Relay none match. \n");
        }
        if (regRelay.relay_event.bits.CCS_Precharge != outputRelay.relay_event.bits.CCS_Precharge) {
            PRINTF_FUNC("CCS Precharge Relay none match. \n");
        }
        if (regRelay.relay_event.bits.Gun1_P != outputRelay.relay_event.bits.Gun1_P) {
            PRINTF_FUNC("SMR1:D+ Relay none match. \n");
        }
        if (regRelay.relay_event.bits.Gun1_N != outputRelay.relay_event.bits.Gun1_N) {
            PRINTF_FUNC("SMR1:D- Relay none match. \n");
        }
        if (regRelay.relay_event.bits.Gun2_P != outputRelay.relay_event.bits.Gun2_P) {
            PRINTF_FUNC("SMR2:D+ Relay none match. \n");
        }
        if (regRelay.relay_event.bits.Gun2_N != outputRelay.relay_event.bits.Gun2_N) {
            PRINTF_FUNC("SMR2:D- Relay none match. \n");
        }
        if (regRelay.relay_event.bits.Gun1_Parallel_P != outputRelay.relay_event.bits.Gun1_Parallel_P) {
            PRINTF_FUNC("Parallel:D+ Relay none match. \n");
        }
        if (regRelay.relay_event.bits.Gun1_Parallel_N != outputRelay.relay_event.bits.Gun1_Parallel_N) {
            PRINTF_FUNC("Parallel:D- Relay none match. \n");
        }*/

        result = true;
    }

#if !defined DD360 && !defined DD360Audi
    if (regRelay.relay_event.bits.AC_Contactor != outputRelay.relay_event.bits.AC_Contactor) {
        //PRINTF_FUNC("AC Contact Relay none match, try to switch to %d \n", outputRelay.relay_event.bits.AC_Contactor);
    }

    if (regRelay.relay_event.bits.CCS_Precharge != outputRelay.relay_event.bits.CCS_Precharge) {
        //PRINTF_FUNC("CCS Precharge Relay none match, try to switch to %d \n", outputRelay.relay_event.bits.CCS_Precharge);
    }
#endif //!defined DD360 && !defined DD360Audi

    if (regRelay.relay_event.bits.Gun1_P != outputRelay.relay_event.bits.Gun1_P) {
        //StartCheckRelayInfo(RELAY_SMR1_P_STATUS, outputRelay.relay_event.bits.Gun1_P);
        //PRINTF_FUNC("SMR1:D+ Relay none match, try to switch to %d \n", outputRelay.relay_event.bits.Gun1_P);
    } else {
        //StopCheckRelayInfo(RELAY_SMR1_P_STATUS);
        //PRINTF_FUNC("SMR1:D+ Release %d \n");
    }

    if (regRelay.relay_event.bits.Gun1_N != outputRelay.relay_event.bits.Gun1_N) {
        //StartCheckRelayInfo(RELAY_SMR1_N_STATUS, outputRelay.relay_event.bits.Gun1_N);
        //PRINTF_FUNC("SMR1:D- Relay none match, try to switch to %d \n", outputRelay.relay_event.bits.Gun1_N);
    } else {
        //StopCheckRelayInfo(RELAY_SMR1_N_STATUS);
    }

    if (regRelay.relay_event.bits.Gun2_P != outputRelay.relay_event.bits.Gun2_P) {
        //StartCheckRelayInfo(RELAY_SMR2_P_STATUS, outputRelay.relay_event.bits.Gun2_P);
        //PRINTF_FUNC("SMR2:D+ Relay none match, try to switch to %d \n", outputRelay.relay_event.bits.Gun2_P);
    } else {
        //StopCheckRelayInfo(RELAY_SMR2_P_STATUS);
    }

    if (regRelay.relay_event.bits.Gun2_N != outputRelay.relay_event.bits.Gun2_N) {
        //StartCheckRelayInfo(RELAY_SMR2_N_STATUS, outputRelay.relay_event.bits.Gun2_N);
        //PRINTF_FUNC("SMR2:D- Relay none match, try to switch to %d \n", outputRelay.relay_event.bits.Gun2_N);
    } else {
        //StopCheckRelayInfo(RELAY_SMR2_N_STATUS);
    }

#if !defined DD360 && !defined DD360Audi
    if (regRelay.relay_event.bits.Gun1_Parallel_P != outputRelay.relay_event.bits.Gun1_Parallel_P) {
        //StartCheckRelayInfo(RELAY_PARA_P_STATUS, outputRelay.relay_event.bits.Gun1_Parallel_P);
        //PRINTF_FUNC("Parallel:D+ Relay none match, try to switch to %d \n", outputRelay.relay_event.bits.Gun1_Parallel_P);
    } else {
        //StopCheckRelayInfo(RELAY_PARA_P_STATUS);
    }

    if (regRelay.relay_event.bits.Gun1_Parallel_N != outputRelay.relay_event.bits.Gun1_Parallel_N) {
        //StartCheckRelayInfo(RELAY_PARA_N_STATUS, outputRelay.relay_event.bits.Gun1_Parallel_N);
        //PRINTF_FUNC("Parallel:D- Relay none match, try to switch to %d \n", outputRelay.relay_event.bits.Gun1_Parallel_N);
    } else {
        //StopCheckRelayInfo(RELAY_PARA_N_STATUS);
    }
#endif //!defined DD360 && !defined DD360Audi

    return result;
}

void MatchRelayStatus()
{
    // 因為 AC Contactor 沒有 Feedback,所以暫時先這樣處理
    //regRelay.relay_event.bits.AC_Contactor = outputRelay.relay_event.bits.AC_Contactor;
#if !defined DD360 && !defined DD360Audi
    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(byte index, byte resister)
{
    gfd_config.index = index;
    gfd_config.state = resister;

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

void CableCheckDetected(byte index)
{
    // 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 ((_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(index, GFD_CABLECHK);
            } else if (_chargingData[index]->SystemStatus >= S_CCS_PRECHARGE_ST0 &&
                       _chargingData[index]->SystemStatus <= S_CCS_PRECHARGE_ST1) {
                SetGfdConfig(index, 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(index, GFD_IDLE);
                } else {
                    SetGfdConfig(index, GFD_CHARGING);
                }
            }
        } else if (_chargingData[index]->SystemStatus == S_COMPLETE || _chargingData[index]->SystemStatus == S_PREPARNING
                   || _chargingData[index]->SystemStatus == S_IDLE) {
            SetGfdConfig(index, GFD_IDLE);
        }
    }
}

void CheckOutputPowerOverCarReq(byte 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 {
                PRINTF_FUNC("[Module_InternalComm]CheckOutputPowerOverCarReq NG : fire = %f, battery = %f \n",
                            _chargingData[index]->FireChargingVoltage, (_chargingData[index]->EvBatterytargetVoltage * 10));
                DEBUG_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;
                    }
                    _chargingData[index]->StopChargeFlag = YES;
                }
            }
        } else {
            if (_isOvpChkTimeFlag[index] == YES) {
                _isOvpChkTimeFlag[index] = NO;
            }
        }
    }
}

void CheckOutputVolNoneMatchFire(byte 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) {
                    /*PRINTF_FUNC("[Module_InternalComm]CheckOutputVolNoneMatchFire NG (%d) : pre = %f, fire = %f \n",
                            index, (_chargingData[index]->PresentChargingVoltage * 10), _chargingData[index]->FireChargingVoltage);
                    DEBUG_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(byte index)
{
#if !defined METER_ENABLE
    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;
            }

            PRINTF_FUNC("CheckRelayWeldingStatus : fail \n");
            _chargingData[index]->StopChargeFlag = YES;
        }
    }
#endif //!defined METER_ENABLE
}

void GetPsuTempForFanSpeed()
{
    char temp = 0;

    for (byte index = 0; index < ShmPsuData->GroupCount; index++) {
        for (byte 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 :
    unsigned char temp = 0;
    float power = 0;

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

    for (byte 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;
    }
    unsigned char _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.AcPlug, &acStatus) == PASS) {
        ShmSysConfigAndInfo->SysConfig.AcRatingCurrent = acStatus.MaxCurrent;

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

        ac_chargingInfo[0]->ConnectorPlugIn = acStatus.CpStatus;
        //  PRINTF_FUNC("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
//      PRINTF_FUNC("GetAcStatus return fail. \n");
}

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

unsigned char GetChargingEnergy()
{
    return Query_Charging_Energy(Uart5Fd, Addr.AcPlug, &acChargingEnergy);
}

unsigned char GetChargingCurrent()
{
    return Query_Charging_Current(Uart5Fd, Addr.AcPlug, &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.AcPlug, &ledStatus);
}

void SetLegacyReq(byte _switch)
{
    Config_Legacy_Req(Uart5Fd, Addr.AcPlug, _switch);
}

void SetCpDuty(byte _value)
{
    Config_Ac_Duty(Uart5Fd, Addr.AcPlug, _value);
}

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

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

void ChangeStartOrStopDateTime(byte 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(byte 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);
    }

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

void OcppStopTransation(byte 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);
    }

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

bool OcppRemoteStop(byte 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;
}

unsigned char isModeChange()
{
    unsigned char 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();

            byte _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) {
                    PRINTF_FUNC("** 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) {
                    PRINTF_FUNC("** UserId = %s \n", ShmSysConfigAndInfo->SysConfig.UserId);
                    strcpy((char *)ac_chargingInfo[0]->StartUserId, (char *)ShmSysConfigAndInfo->SysConfig.UserId);
                    PRINTF_FUNC("** 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(byte 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)
{
    byte relayMatchFlag = NO;

    if (InitShareMemory() == FAIL) {
        DEBUG_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) {
        PRINTF_FUNC("(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) {
        PRINTF_FUNC("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);
        }

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

        // 自檢階段處理,自檢階段如果讀不到版號則代表該系統沒有掛燈板
        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
        }

        AcChargeTypeProcess();

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

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

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

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

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

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

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

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

                if (_chargingData[i]->SystemStatus == S_IDLE) {
#if !defined METER_ENABLE
                    _chargingData[i]->RelayWeldingCheck = NO;
                    _isRelayWelding[i] = NO;
#endif //!defined METER_ENABLE
                    _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()) {
                relayMatchFlag = NO;
                if (Config_Relay_Output(Uart5Fd, Addr.Relay, &outputRelay)) {
                    //regRelay.relay_event.bits.AC_Contactor = ShmSysConfigAndInfo->SysInfo.AcContactorStatus;
#if 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;
#endif //defined DD360 || defined DD360Audi
                    //PRINTF_FUNC("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 DD360 && !defined DD360Audi
            else if (relayMatchFlag == NO) {
                relayMatchFlag = YES;
                PRINTF_FUNC("======== Relay Status Start========\n");
                if (regRelay.relay_event.bits.AC_Contactor == YES) {
                    PRINTF_FUNC("AC Power : ON \n");
                } else {
                    PRINTF_FUNC("AC Power : OFF \n");
                }

                if (regRelay.relay_event.bits.Gun1_P == YES) {
                    PRINTF_FUNC("Conn1(+) : ON \n");
                } else {
                    PRINTF_FUNC("Conn1(+) : OFF \n");
                }

                if (regRelay.relay_event.bits.Gun1_N == YES) {
                    PRINTF_FUNC("Conn1(-) : ON \n");
                } else {
                    PRINTF_FUNC("Conn1(-) : OFF \n");
                }

                if (regRelay.relay_event.bits.Gun2_P == YES) {
                    PRINTF_FUNC("Conn2(+) : ON \n");
                } else {
                    PRINTF_FUNC("Conn2(+) : OFF \n");
                }

                if (regRelay.relay_event.bits.Gun2_N == YES) {
                    PRINTF_FUNC("Conn2(-) : ON \n");
                } else {
                    PRINTF_FUNC("Conn2(-) : OFF \n");
                }

                if (regRelay.relay_event.bits.CCS_Precharge == YES) {
                    PRINTF_FUNC("Precharge : ON \n");
                } else {
                    PRINTF_FUNC("Precharge : OFF \n");
                }

                if (regRelay.relay_event.bits.Gun1_Parallel_P == YES) {
                    PRINTF_FUNC("Parallel(+) : ON \n");
                } else {
                    PRINTF_FUNC("Parallel(+) : OFF \n");
                }

                if (regRelay.relay_event.bits.Gun1_Parallel_N == YES) {

                    PRINTF_FUNC("Parallel(-) : ON \n");
                } else {
                    PRINTF_FUNC("Parallel(-) : OFF \n");
                }
                PRINTF_FUNC("======== Relay Status End========\n");
            }
#endif //!defined DD360 && !defined DD360Audi
        }

#ifndef NO_FAN_BOARD
        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;

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

        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);
            }
        }

        usleep(10000);
    }

    return FAIL;
}