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

#define TEN_MINUTES			600
#define ENV_TEMP_MIN		45
#define ENV_TEMP_MAX		50
#define DEFAULT_AC_INDEX	2
#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   5

struct SysConfigAndInfo			*ShmSysConfigAndInfo;
struct StatusCodeData 			*ShmStatusCodeData;
struct FanModuleData			*ShmFanModuleData;
struct RelayModuleData			*ShmRelayModuleData[2];
struct LedModuleData			*ShmLedModuleData;
struct PsuData 					*ShmPsuData;
struct OCPP16Data				*ShmOCPP16Data;
ChargerInfoData                 *ShmChargerInfo;
PsuGroupingInfoData             *ShmPsuGrouping;
PsuGroupOutputRelay             *ShmOutputRelayConfig[MAX_GROUP_QUANTITY];
PsuGroupOutputRelay             *ShmOutputRelayConfirmed[MAX_GROUP_QUANTITY];
PsuGroupParallelRelay           *ShmParallelRelayConfig;
PsuGroupParallelRelay           *ShmParallelRelayConfirmed;
OutputRelayControl              *LocationOutputRelayCtrl[MAX_GROUP_QUANTITY];
OutputRelayControl              *LocationOutputRelayResponse[MAX_GROUP_QUANTITY];
unsigned char                   LocationParallelRelayCtrl[PARALLEL_RELAY_COUNT];
unsigned char                   LocationParallelRelayResponse[PARALLEL_RELAY_COUNT];
Connector_GFD                   *LocaltionGfd[MAX_GROUP_QUANTITY];

#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 DCIN_OVP_THRESHOLD_VOL      825 // dc input ovp threshold voltage
#define DCIN_OVP_RECOVERY_VOL       815 // dc input ovp recovery voltage

#define DCIN_UVP_THRESHOLD_VOL      400 // dc input uvp threshold voltage
#define DCIN_UVP_RECOVERY_VOL       410 // dc input uvp recovery voltage

#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
// 確認 Relay Welding 電壓
#define RELAY_WELDING_DET					300

#if SAFETY_TEST_ENABLE
#define RELAY_OPEN_AT_PRECHARGE             1
#else
#define RELAY_OPEN_AT_PRECHARGE             0
#endif

#define RELAY_DEBUG_MSG                     0

byte gunCount;
byte acgunCount;
// 槍資訊
struct ChargingInfoData *_chargingData[CONNECTOR_QUANTITY];
struct ChargingInfoData *ac_chargingInfo[AC_QUANTITY];

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

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

byte _dcOvpCnt = 0;
byte _dcUvpCnt = 0;
byte _threePhaseOvp[3] = {0, 0, 0};
byte _threePhaseUvp[3] = {0, 0, 0};

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

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

struct timespec _priority_time;
struct timespec _led_priority_time;

unsigned short _setFanSpeed = 0;
float _beforeChargingTotalEnergy = 0.0;
byte _checkLedChanged = 3;
byte _RelaySelfTestOK;
bool _isGfdEnable = false;

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

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

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

	StartTime = (unsigned int) ST.time;
	StopTime = (unsigned int) ET.time;
	//return (StopTime-StartTime)*1000+ET.millitm-ST.millitm;
	return (StopTime - StartTime);
}

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

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

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

void GetFwAndHwVersion_Relay()
{
	if (Query_FW_Ver(Uart5Fd, Addr.DO360_RC1, &ver) == PASS)
	{
		// RelayModuleData
		strcpy((char *) ShmRelayModuleData[0]->version, ver.Version_FW);
		// SystemInfo
		strcpy((char *) ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev, ver.Version_FW);
		LOG_INFO("GetFwAndHwVersion_RC1 s1 = %s", ver.Version_FW);
	}

	if (Query_HW_Ver(Uart5Fd, Addr.DO360_RC1, &ver) == PASS)
	{
		// SystemInfo
		strcpy((char *) ShmSysConfigAndInfo->SysInfo.RelayModuleHwRev, ver.Version_FW);
		LOG_INFO("GetFwAndHwVersion_RC1 s2 = %s", ver.Version_HW);
	}
}

void GetFwAndHwVersion_Relay2()
{
	// DO360 RC2
	if (Query_FW_Ver(Uart5Fd, Addr.DO360_RC2, &ver) == PASS)
	{
		// RelayModuleData
		strcpy((char *) ShmRelayModuleData[1]->version, ver.Version_FW);
		// SystemInfo
		strcpy((char *) ShmSysConfigAndInfo->SysInfo.Relay2ModuleFwRev, ver.Version_FW);
		LOG_INFO("GetFwAndHwVersion_RC2 s1 = %s", ver.Version_FW);
	}

	if (Query_HW_Ver(Uart5Fd, Addr.DO360_RC2, &ver) == PASS)
	{
		// SystemInfo
		strcpy((char *) ShmSysConfigAndInfo->SysInfo.Relay2ModuleHwRev, ver.Version_FW);
		LOG_INFO("GetFwAndHwVersion_RC2 s2 = %s", ver.Version_HW);
	}
}

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

//	if (Query_HW_Ver(Uart5Fd, Addr.Led, &ver) == PASS)
//	{
//		// SystemInfo
//		strcpy((char *) ShmSysConfigAndInfo->SysInfo.RelayModuleHwRev, ver.Version_FW);
//		//LOG_INFO("GetFwAndHwVersion_Relay s2 = %s", 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)
	{
		LOG_INFO("ac model name = %s", ShmSysConfigAndInfo->SysConfig.AcModelName);
	}
}

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

	ftime(&csuTime);
	tmCSU = localtime(&csuTime.time);
	//	LOG_INFO("Time : %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);

	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(index == 0)
	{
		if (Config_Rtc_Data(Uart5Fd, Addr.DO360_RC1, &rtc) == PASS)
		{
			//LOG_INFO("SetRtc (RB) sucessfully.");
		}
	}
	else
	{
		if (Config_Rtc_Data(Uart5Fd, Addr.DO360_RC2, &rtc) == PASS)
		{
			//LOG_INFO("SetRtc (RB) sucessfully.");
		}
	}
}

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

	ftime(&csuTime);
	tmCSU = localtime(&csuTime.time);
	//	LOG_INFO("Time : %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);

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

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

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

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

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

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

	if (Config_Rtc_Data(Uart5Fd, Addr.Fan, &rtc) == PASS)
	{
		//LOG_INFO("SetRtc (FB) sucessfully.");
	}
}

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

// AC 三相輸入電壓
void GetPresentInputVol()
{
    if(ShmChargerInfo->Control.RelayCtrl.bits.AcInputDisable == YES)
    {
        if(Query_DC_InputVoltage(Uart5Fd, Addr.DO360_RC1, &dcInputVoltage) == PASS)
        {
            ShmSysConfigAndInfo->SysInfo.InputVoltageDc = dcInputVoltage.DC_Input_1;

            // DC Input OVP
            if(ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DcInputOVP == NO)
            {
                if(dcInputVoltage.DC_Input_1 > DCIN_OVP_THRESHOLD_VOL)
                {
                    _dcOvpCnt++;
                    if(_dcOvpCnt >= OVP_UVP_CHK_COUNT)
                    {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DcInputOVP = YES;
                        LOG_INFO("Dc Input OVP: %.1f V", dcInputVoltage.DC_Input_1);
                    }
                }
                else
                {
                    _dcOvpCnt = 0;
                }
            }
            else
            {
                if(dcInputVoltage.DC_Input_1 <= DCIN_OVP_RECOVERY_VOL)
                {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DcInputOVP = NO;
                    LOG_INFO("Dc Input OVP Recovery: %.1f V", dcInputVoltage.DC_Input_1);
                }
                _dcOvpCnt = 0;
            }

            // DC Input UVP
            if(ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DcInputUVP == NO)
            {
                if(dcInputVoltage.DC_Input_1 < DCIN_UVP_THRESHOLD_VOL)
                {
                    _dcUvpCnt++;
                    if(_dcUvpCnt >= OVP_UVP_CHK_COUNT)
                    {
                        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DcInputUVP = YES;
                        LOG_INFO("Dc Input UVP: %.1f V", dcInputVoltage.DC_Input_1);
                    }
                }
                else
                {
                    _dcUvpCnt = 0;
                }
            }
            else
            {
                if(dcInputVoltage.DC_Input_1 >= DCIN_UVP_RECOVERY_VOL)
                {
                    ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DcInputUVP = NO;
                    LOG_INFO("Dc Input UVP Recovery: %.1f V", dcInputVoltage.DC_Input_1);
                }
                _dcUvpCnt = 0;
            }
        }
    }
    else
    {
        if (Query_Present_InputVoltage(Uart5Fd, Addr.DO360_RC1, &inputVoltage) == PASS)
        {
            // resolution : 0.1
            ShmSysConfigAndInfo->SysInfo.InputVoltageR = ShmRelayModuleData[0]->InputL1Volt = inputVoltage.L1N_L12;
            ShmSysConfigAndInfo->SysInfo.InputVoltageS = ShmRelayModuleData[0]->InputL2Volt = inputVoltage.L2N_L23;
            ShmSysConfigAndInfo->SysInfo.InputVoltageT = ShmRelayModuleData[0]->InputL3Volt = inputVoltage.L3N_L31;

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

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

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

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

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

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

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

                if (ShmStatusCodeData->AlarmCode.AlarmEvents.bits.SystemL2InputOVP == NO)
                {
                    if (inputVoltage.L2N_L23 > VIN_MAX_VOLTAGE_UL)
                    {
                        LOG_INFO("In Ovp L2N_L23 = %f", 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.SystemL3InputOVP == NO)
                {
                    if (inputVoltage.L3N_L31 > VIN_MAX_VOLTAGE_UL)
                    {
                        LOG_INFO("In Ovp L3N_L31 = %f", 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(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
    {
        // two relay board
        if (Query_Present_OutputVoltage(Uart5Fd, Addr.DO360_RC1, &outputVoltage) == PASS)
        {
            ShmRelayModuleData[0]->Gun1FuseOutputVolt = outputVoltage.behindFuse_Voltage_C1;
            ShmRelayModuleData[0]->Gun1RelayOutputVolt = outputVoltage.behindRelay_Voltage_C1;
            ShmRelayModuleData[0]->Gun2FuseOutputVolt = outputVoltage.behindFuse_Voltage_C2;
            ShmRelayModuleData[0]->Gun2RelayOutputVolt = outputVoltage.behindRelay_Voltage_C2;

            _chargingData[0]->FuseChargingVoltage = ShmRelayModuleData[0]->Gun1FuseOutputVolt;
            _chargingData[0]->FireChargingVoltage = ShmRelayModuleData[0]->Gun1RelayOutputVolt;
            _chargingData[3]->FuseChargingVoltage = ShmRelayModuleData[0]->Gun2FuseOutputVolt;
            _chargingData[3]->FireChargingVoltage = ShmRelayModuleData[0]->Gun2RelayOutputVolt;
        }

        // DO360 RC2
        if (Query_Present_OutputVoltage(Uart5Fd, Addr.DO360_RC2, &outputVoltage) == PASS)
        {
            ShmRelayModuleData[1]->Gun1FuseOutputVolt = outputVoltage.behindFuse_Voltage_C1;
            ShmRelayModuleData[1]->Gun1RelayOutputVolt = outputVoltage.behindRelay_Voltage_C1;
            ShmRelayModuleData[1]->Gun2FuseOutputVolt = outputVoltage.behindFuse_Voltage_C2;
            ShmRelayModuleData[1]->Gun2RelayOutputVolt = outputVoltage.behindRelay_Voltage_C2;

            _chargingData[1]->FuseChargingVoltage = ShmRelayModuleData[1]->Gun2FuseOutputVolt;
            _chargingData[1]->FireChargingVoltage = ShmRelayModuleData[1]->Gun2RelayOutputVolt;
            _chargingData[2]->FuseChargingVoltage = ShmRelayModuleData[1]->Gun1FuseOutputVolt;
            _chargingData[2]->FireChargingVoltage = ShmRelayModuleData[1]->Gun1RelayOutputVolt;
        }
    }
    else
    {
        // only one relay board
        if (Query_Present_OutputVoltage(Uart5Fd, Addr.DO360_RC1, &outputVoltage) == PASS)
        {
            ShmRelayModuleData[0]->Gun1FuseOutputVolt = outputVoltage.behindFuse_Voltage_C1;
            ShmRelayModuleData[0]->Gun1RelayOutputVolt = outputVoltage.behindRelay_Voltage_C1;
            ShmRelayModuleData[0]->Gun2FuseOutputVolt = outputVoltage.behindFuse_Voltage_C2;
            ShmRelayModuleData[0]->Gun2RelayOutputVolt = outputVoltage.behindRelay_Voltage_C2;

            _chargingData[0]->FuseChargingVoltage = ShmRelayModuleData[0]->Gun1FuseOutputVolt;
            _chargingData[0]->FireChargingVoltage = ShmRelayModuleData[0]->Gun1RelayOutputVolt;
            _chargingData[1]->FuseChargingVoltage = ShmRelayModuleData[0]->Gun2FuseOutputVolt;
            _chargingData[1]->FireChargingVoltage = ShmRelayModuleData[0]->Gun2RelayOutputVolt;
        }
    }
}

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

// 讀取 Relay 狀態
void GetRelayOutputStatus(void)
{
    unsigned char location = 0;

    if(Query_Relay_Output(Uart5Fd, Addr.DO360_RC1, &regRelay[0]) == PASS)
    {
        regRelay[0].relay_event.bits.AC_Contactor = outputRelay[0].relay_event.bits.AC_Contactor;
    }
    if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
    {
        if(Query_Relay_Output(Uart5Fd, Addr.DO360_RC2, &regRelay[1]) == PASS)
        {
            regRelay[1].relay_event.bits.AC_Contactor = outputRelay[1].relay_event.bits.AC_Contactor;
        }
    }

    // update output relay feedback status
    for(int i = 0; i < ShmChargerInfo->Control.MaxConnector; i++)
    {
        location = ShmPsuGrouping->GroupCollection[i].Location;

        if(ShmOutputRelayConfirmed[i]->bits.Output_N != LocationOutputRelayResponse[location]->bits.Gun_N)
        {
            LOG_INFO("Gun %d Get K1K2 N %s at Location %d",
                i + 1, LocationOutputRelayResponse[location]->bits.Gun_N ? "On" : "Off", location + 1);
        }
        ShmOutputRelayConfirmed[i]->bits.Output_N = LocationOutputRelayResponse[location]->bits.Gun_N;

        if(ShmOutputRelayConfirmed[i]->bits.Output_P != LocationOutputRelayResponse[location]->bits.Gun_P)
        {
            LOG_INFO("Gun %d Get K1K2 P %s at Location %d",
                i + 1, LocationOutputRelayResponse[location]->bits.Gun_P ? "On" : "Off", location + 1);
        }
        ShmOutputRelayConfirmed[i]->bits.Output_P = LocationOutputRelayResponse[location]->bits.Gun_P;
    }

    // update parallel relay feedback status

    int parallelCnt = ShmChargerInfo->Control.MaxConnector == GENERAL_GUN_QUANTITY ? PARALLEL_RELAY_COUNT : 1;

    for(int i = 0; i < parallelCnt; i++)
    {
        bool relayOnOff = 0;

        if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
        {
            switch(i)
            {
                case 0:
                    LocationParallelRelayResponse[i] = regRelay[0].relay_event.bits.Gun1_Parallel_N;
                    break;
                case 1:
                    LocationParallelRelayResponse[i] = regRelay[0].relay_event.bits.Gun2_Parallel_N;
                    break;
                case 2:
                    LocationParallelRelayResponse[i] = regRelay[1].relay_event.bits.Gun1_Parallel_N;
                    break;
                case 3:
                    LocationParallelRelayResponse[i] = regRelay[1].relay_event.bits.Gun2_Parallel_N;
                    break;
                case 4:
                    LocationParallelRelayResponse[i] = regRelay[0].relay_event.bits.CCS_Precharge;
                    break;
                case 5:
                    LocationParallelRelayResponse[i] = regRelay[1].relay_event.bits.CCS_Precharge;
                    break;
            }
            relayOnOff = LocationParallelRelayResponse[i];
        }
        else
        {
            bool original = ShmParallelRelayConfig->CtrlValue & (1 << i) ? false : true;

            relayOnOff = regRelay[0].relay_event.bits.Gun1_Parallel_N == regRelay[0].relay_event.bits.Gun2_Parallel_N ?
                regRelay[0].relay_event.bits.Gun1_Parallel_N : original;
#if RELAY_DEBUG_MSG
            if(regRelay[0].relay_event.bits.Gun1_Parallel_N != regRelay[0].relay_event.bits.Gun2_Parallel_N)
            {
                LOG_INFO("Parallel Relay N & P at Location %d is Not Match: %d, %d",
                    i + 1, regRelay[0].relay_event.bits.Gun1_Parallel_N, regRelay[0].relay_event.bits.Gun2_Parallel_N);
            }
#endif
        }

        if((ShmParallelRelayConfirmed->CtrlValue & (1 << i)) != (relayOnOff << i))
        {
            LOG_INFO("Get Parallel Relay N & P %s at Location %d", relayOnOff ? "On" : "Off", i + 1);
        }

        if(relayOnOff)
        {
            ShmParallelRelayConfirmed->CtrlValue |= 1 << i;
        }
        else
        {
            ShmParallelRelayConfirmed->CtrlValue &= ~(1 << i);
        }
    }
}

// 確認 K1 K2 relay 的狀態
void CheckK1K2RelayOutput(byte index)
{
    unsigned char location = 0;
    if(index < MAX_GROUP_QUANTITY && index < ShmChargerInfo->Control.MaxConnector)
    {
        location = ShmPsuGrouping->GroupCollection[index].Location;

        if(LocationOutputRelayCtrl[location]->bits.Gun_N == LocationOutputRelayCtrl[location]->bits.Gun_P)
        {
            _chargingData[index]->RelayK1K2Status = LocationOutputRelayCtrl[location]->bits.Gun_N ? YES : NO;
        }
        else
        {
            _chargingData[index]->RelayK1K2Status = NO;
        }
    }
}

void GetGfdAdc(void)
{
    unsigned char location = 0, result = 0;
    char *str_gfd[] = {"Idle", "Pass", "Fail", "Warning"};

    if(!_isGfdEnable)
    {
        for(int i = 0; i < ShmChargerInfo->Control.MaxConnector; i++)
        {
            if(_chargingData[i]->PantographFlag == YES)
            {
                _isGfdEnable = true;
                LOG_INFO("Enable Power Cabinet GFD Function");
            }
        }
    }

    if(_isGfdEnable)
    {
        // define : 每 0.2 ~ 1 秒一次
        // occur : <= 75k 歐姆 @ 150 - 750 Vdc
        // warning : >= 100 歐姆 && <= 500 歐姆 @ 150-750 Vdc
        if(Query_Gfd_Adc(Uart5Fd, Addr.DO360_RC1, &gfd_adc[0]) == PASS)
        {
//            if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[0].Parameter.bits.GfdDetection ||
//                ShmSysConfigAndInfo->SysInfo.ConnectorInfo[1].Parameter.bits.GfdDetection)
//            {
//                LOG_INFO("Query Relay1 GFD ADC1 Status = %d, ADC2 Status = %d", gfd_adc[0].result_conn1, gfd_adc[0].result_conn2);
//            }
        }
//        else
//        {
//            if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[0].Parameter.bits.GfdDetection ||
//                ShmSysConfigAndInfo->SysInfo.ConnectorInfo[1].Parameter.bits.GfdDetection)
//            {
//                LOG_INFO("Query Relay1 GFD ADC Fail");
//            }
//        }
        if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
        {
            if(Query_Gfd_Adc(Uart5Fd, Addr.DO360_RC2, &gfd_adc[1]) == PASS)
            {

            }
        }

        // update output relay feedback status
        for(int i = 0; i < ShmChargerInfo->Control.MaxConnector; i++)
        {
            location = ShmPsuGrouping->GroupCollection[i].Location;

            result = LocaltionGfd[location]->bits.GFD_Result;
            if(_chargingData[i]->GroundFaultStatus != result)
            {
                if(result <= GFD_WARNING)
                {
                    LOG_INFO("Gun %d GFD Result %s at Location %d", i + 1, str_gfd[result], location + 1);
                    if(result == GFD_FAIL || result == GFD_WARNING)
                    {
                        LOG_INFO("Gun %d GFD Step = %d, R = %d, Vol = %d", i + 1, LocaltionGfd[location]->bits.rb_step,
                            LocaltionGfd[location]->bits.Resister, LocaltionGfd[location]->bits.Voltage);
                    }
                }
                else
                {
                    LOG_INFO("Gun %d Unknown GFD Result %d at Location %d", i + 1, result, location + 1);
                }
            }
            _chargingData[i]->GroundFaultStatus = result;
        }
    }
}

void SetFanModuleSpeed()
{
	{
		FanSpeed _fanSpeed;

		_setFanSpeed += fanSpeedSmoothValue;

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

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

		_fanSpeed.speed[1] = _setFanSpeed;

		_fanSpeed.speed[2] = _setFanSpeed;

		_fanSpeed.speed[3] = _setFanSpeed;

		if (Config_Fan_Speed(Uart5Fd, Addr.Fan, &_fanSpeed) == PASS)
		{
			//LOG_INFO("successfully Fan");
		}
	}
}

void SetPCabinetOutputRelayOff(byte index)
{
    if(ShmChargerInfo->Control.CabinetRole == _CROLE_MASTER)
    {
        if(ShmChargerInfo->ParallelCabinet.PCabinet[index].OutputRelaySetting[index] != NO)
        {
            LOG_INFO("Set Parallel Cabinet Gun %d Output Relay OFF", index + 1);
        }
        ShmChargerInfo->ParallelCabinet.PCabinet[index].OutputRelaySetting[index] = NO;
    }
}

void SetMCabinetOutputRelay(byte index)
{
    if(ShmChargerInfo->Control.RelayCtrl.bits.AbnormalRelay)
    {
        return;
    }

    if ((_chargingData[index]->SystemStatus >= S_PREPARING_FOR_EVSE &&
            _chargingData[index]->SystemStatus <= S_CHARGING))
    {
        if(_chargingData[index]->GroundFaultStatus == GFD_FAIL)
        {
            if(ShmOutputRelayConfig[index]->bits.Output_N || ShmOutputRelayConfig[index]->bits.Output_P)
            {
                LOG_INFO("Gun %d Set K1K2 Open By GFD Fail", index + 1);
            }
            ShmOutputRelayConfig[index]->bits.Output_N = false;
            ShmOutputRelayConfig[index]->bits.Output_P = false;
            SetPCabinetOutputRelayOff(index);
        }
        else
        {
#if RELAY_OPEN_AT_PRECHARGE

            if(_chargingData[index]->SystemStatus == S_PREPARING_FOR_EVSE)
            {
                if(ShmPsuGrouping->GroupCollection[index].GroupCtrl.bits.CableCheckDone == false &&
                    ShmPsuGrouping->GroupCollection[index].GroupCtrl.bits.InPrechargeMode == false)
                {
                    if(!ShmOutputRelayConfig[index]->bits.Output_N || !ShmOutputRelayConfig[index]->bits.Output_P)
                    {
                        LOG_INFO("Gun %d Set K1K2 Close And Prepare To Cable Check", index + 1);
                    }
                    ShmOutputRelayConfig[index]->bits.Output_N = true;
                    ShmOutputRelayConfig[index]->bits.Output_P = true;
                }
                else if(ShmPsuGrouping->GroupCollection[index].GroupCtrl.bits.CableCheckDone == true &&
                    ShmPsuGrouping->GroupCollection[index].GroupCtrl.bits.InPrechargeMode == false)
                {
                    if(_chargingData[index]->FireChargingVoltage <= SELF_TO_CHANGE_RELAY_STATUS)
                    {
                        if(ShmOutputRelayConfig[index]->bits.Output_N || ShmOutputRelayConfig[index]->bits.Output_P)
                        {
                            LOG_INFO("Gun %d Set K1K2 Open By Cable Check Done", index + 1);
                        }
                        ShmOutputRelayConfig[index]->bits.Output_N = false;
                        ShmOutputRelayConfig[index]->bits.Output_P = false;
                    }
                }
                else if(ShmPsuGrouping->GroupCollection[index].GroupCtrl.bits.CableCheckDone == true &&
                    ShmPsuGrouping->GroupCollection[index].GroupCtrl.bits.InPrechargeMode == true)
                {
                    unsigned short voltage = 0, diffVol = 0;
                    voltage = (int)(_chargingData[index]->PresentChargingVoltage * 10);
                    diffVol = voltage >= ShmPsuGrouping->GroupOutput[index].GTargetVoltage ?
                        voltage - ShmPsuGrouping->GroupOutput[index].GTargetVoltage :
                        ShmPsuGrouping->GroupOutput[index].GTargetVoltage - voltage;

                    if(diffVol <= 30)
                    {
                        if(!ShmOutputRelayConfig[index]->bits.Output_N || !ShmOutputRelayConfig[index]->bits.Output_P)
                        {
                            LOG_INFO("Gun %d Set K1K2 Close And Voltage Is Balance", index + 1);
                        }
                        ShmOutputRelayConfig[index]->bits.Output_N = true;
                        ShmOutputRelayConfig[index]->bits.Output_P = true;
                    }
                }
            }
            else
            {
                if(!ShmOutputRelayConfig[index]->bits.Output_N || !ShmOutputRelayConfig[index]->bits.Output_P)
                {
                    LOG_INFO("Gun %d Set K1K2 Close In Charging Status", index + 1);
                }
                ShmOutputRelayConfig[index]->bits.Output_N = true;
                ShmOutputRelayConfig[index]->bits.Output_P = true;
            }
#else
            if(!ShmOutputRelayConfig[index]->bits.Output_N || !ShmOutputRelayConfig[index]->bits.Output_P)
            {
                LOG_INFO("Gun %d Set K1K2 Close And Prepare To Charging", index + 1);
            }
            ShmOutputRelayConfig[index]->bits.Output_N = true;
            ShmOutputRelayConfig[index]->bits.Output_P = true;
#endif
        }
    }
    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 ||
            _chargingData[index]->GroundFaultStatus == GFD_FAIL)
        {
            if(ShmOutputRelayConfig[index]->bits.Output_N || ShmOutputRelayConfig[index]->bits.Output_P)
            {
                LOG_INFO("Gun %d Set K1K2 Open And Charging Stop", index + 1);
            }
            ShmOutputRelayConfig[index]->bits.Output_N = false;
            ShmOutputRelayConfig[index]->bits.Output_P = false;
            SetPCabinetOutputRelayOff(index);
        }
    }
    else
    {
        if(ShmOutputRelayConfig[index]->bits.Output_N || ShmOutputRelayConfig[index]->bits.Output_P)
        {
            LOG_INFO("Gun %d Set K1K2 Open At Idle Mode", index + 1);
        }
        ShmOutputRelayConfig[index]->bits.Output_N = false;
        ShmOutputRelayConfig[index]->bits.Output_P = false;

        if(_chargingData[index]->SystemStatus == S_IDLE ||
            _chargingData[index]->SystemStatus == S_MAINTAIN ||
            _chargingData[index]->SystemStatus == S_FAULT)
        {
            SetPCabinetOutputRelayOff(index);
        }
    }
}

void SetSCabinetOutputRelay(byte index)
{
    if(ShmChargerInfo->Control.RelayCtrl.bits.AbnormalRelay)
    {
        return;
    }

    ShmOutputRelayConfig[index]->bits.Output_N = ShmChargerInfo->SCabinetControl.SOutputRelay[index] > 0 ? true : false;
    ShmOutputRelayConfig[index]->bits.Output_P = ShmChargerInfo->SCabinetControl.SOutputRelay[index] > 0 ? true : false;
}

//==========================================
// Common Function
//==========================================
void SetK1K2RelayStatus(byte index)
{
    unsigned char location = 0;

    if(index < MAX_GROUP_QUANTITY && index < ShmChargerInfo->Control.MaxConnector)
    {
        if(ShmChargerInfo->Control.CabinetRole != _CROLE_SLAVE)
        {
            SetMCabinetOutputRelay(index);
        }
        else
        {
            SetSCabinetOutputRelay(index);
        }

        location = ShmPsuGrouping->GroupCollection[index].Location;

        if(ShmOutputRelayConfig[index]->bits.Output_N != LocationOutputRelayCtrl[location]->bits.Gun_N)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("Connector %d Set K1K2 N %s at Location %d",
                index + 1, ShmOutputRelayConfig[index]->bits.Output_N ? "On" : "Off", location + 1);
#endif
        }
        LocationOutputRelayCtrl[location]->bits.Gun_N = ShmOutputRelayConfig[index]->bits.Output_N;

        if(ShmOutputRelayConfig[index]->bits.Output_P != LocationOutputRelayCtrl[location]->bits.Gun_P)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("Connector %d Set K1K2 P %s at Location %d",
                index + 1, ShmOutputRelayConfig[index]->bits.Output_P ? "On" : "Off", location + 1);
#endif
        }
        LocationOutputRelayCtrl[location]->bits.Gun_P = ShmOutputRelayConfig[index]->bits.Output_P;
    }
}

void SetParalleRelayStatus()
{
    int parallelCnt = ShmChargerInfo->Control.MaxConnector == GENERAL_GUN_QUANTITY ? PARALLEL_RELAY_COUNT : 1;
    for(int i = 0; i < parallelCnt; i++)
    {
        if((ShmParallelRelayConfig->CtrlValue & (1 << i)) != (LocationParallelRelayCtrl[i] << i))
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("Set Parallel Relay N & P %s at Location %d",
                (ShmParallelRelayConfig->CtrlValue & (1 << i)) ? "On" : "Off", i + 1);
#endif
        }
        LocationParallelRelayCtrl[i] = (ShmParallelRelayConfig->CtrlValue & (1 << i)) ? YES : NO;

        switch(i)
        {
            case 0:
                outputRelay[0].relay_event.bits.Gun1_Parallel_N = LocationParallelRelayCtrl[i];
                if(!ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
                {
                    outputRelay[0].relay_event.bits.Gun2_Parallel_N = LocationParallelRelayCtrl[i];
                }
                break;
            case 1:
                outputRelay[0].relay_event.bits.Gun2_Parallel_N = LocationParallelRelayCtrl[i];
                break;
            case 2:
                outputRelay[1].relay_event.bits.Gun1_Parallel_N = LocationParallelRelayCtrl[i];
                break;
            case 3:
                outputRelay[1].relay_event.bits.Gun2_Parallel_N = LocationParallelRelayCtrl[i];
                break;
            case 4:
                outputRelay[0].relay_event.bits.CCS_Precharge = LocationParallelRelayCtrl[i];
                break;
            case 5:
                outputRelay[1].relay_event.bits.CCS_Precharge = LocationParallelRelayCtrl[i];
                break;
        }
    }
}

void SetAcContactorStatus(void)
{
    if(ShmChargerInfo->Control.RelayCtrl.bits.AcContactor == YES &&
        ShmChargerInfo->Control.RelayCtrl.bits.AcContactorOffByPsu == NO &&
        ShmChargerInfo->Control.RelayCtrl.bits.AcContactorOffByEmergency == NO)
    {
        outputRelay[0].relay_event.bits.AC_Contactor = YES;
    }
    else
    {
        outputRelay[0].relay_event.bits.AC_Contactor = NO;
    }

    if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
    {
        if(ShmChargerInfo->Control.RelayCtrl.bits.AcContactor == YES &&
            ShmChargerInfo->Control.RelayCtrl.bits.AcContactorOffByPsu == NO &&
            ShmChargerInfo->Control.RelayCtrl.bits.AcContactorOffByEmergency == NO)
        {
            outputRelay[1].relay_event.bits.AC_Contactor = YES;
        }
        else
        {
            outputRelay[1].relay_event.bits.AC_Contactor = NO;
        }
    }
}

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;

	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))
		 {
			 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;
		 }
		 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)
		{
			led_color.Connect_1_Green = _colorBuf;
			led_color.Connect_1_Blue = COLOR_MIN_LV;
			led_color.Connect_1_Red = COLOR_MIN_LV;
		}
		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)
		{
			led_color.Connect_2_Green = _colorBuf;
			led_color.Connect_2_Blue = COLOR_MIN_LV;
			led_color.Connect_2_Red = COLOR_MIN_LV;
		}
		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 (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;
	}

	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)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmget ShmSysConfigAndInfo NG");
		#endif
		result = FAIL;
	}
	else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("[shmat ShmSysConfigAndInfo NG");
		#endif
		result = FAIL;
	}

	if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData), 0777)) < 0)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmget ShmStatusCodeData NG");
		#endif
		result = FAIL;
	}
	else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmat ShmStatusCodeData NG");
		#endif
		result = FAIL;
	}

	if ((MeterSMId = shmget(ShmFanBdKey, sizeof(struct FanModuleData), 0777)) < 0)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmget ShmFanModuleData NG");
		#endif
		result = FAIL;
	}
	else if ((ShmFanModuleData = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmat ShmFanModuleData NG");
		#endif
		result = FAIL;
	}

	if ((MeterSMId = shmget(ShmRelayBdKey, sizeof(struct RelayModuleData), 0777)) < 0)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmget ShmRelayModuleData NG");
		#endif
		result = FAIL;
	}
	else if ((ShmRelayModuleData[0] = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmat ShmRelayModuleData NG");
		#endif
		result = FAIL;
	}

	// DO360 RC2
	if ((MeterSMId = shmget(ShmRelay2BdKey, sizeof(struct RelayModuleData), 0777)) < 0)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmget ShmRelay2ModuleData NG");
		#endif
		result = FAIL;
	}
	else if ((ShmRelayModuleData[1] = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmat ShmRelay2ModuleData NG");
		#endif
		result = FAIL;
	}

	if ((MeterSMId = shmget(ShmLedBdKey, sizeof(struct LedModuleData), 0777)) < 0)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmget ShmLedModuleData NG");
		#endif
		result = FAIL;
	}
	else if ((ShmLedModuleData = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmat ShmLedModuleData NG");
		#endif
		result = FAIL;
	}

	if ((MeterSMId = shmget(ShmPsuKey, sizeof(struct PsuData), 0777)) < 0)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmget ShmPsuData NG");
		#endif
		result = FAIL;
	}
	else if ((ShmPsuData = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmat ShmPsuData NG");
		#endif
		result = FAIL;
	}

	if ((MeterSMId = shmget(ShmOcppModuleKey, sizeof(struct OCPP16Data), 0777)) < 0)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmat ShmOCPP16Data NG");
		#endif
		result = FAIL;
	}
	else if ((ShmOCPP16Data = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("shmat ShmOCPP16Data NG");
		#endif
		result = FAIL;
	}

    if ((MeterSMId = shmget(SM_ChargerInfoKey, sizeof(ChargerInfoData), 0777)) < 0)
    {
        #ifdef SystemLogMessage
        LOG_ERROR("shmat ChargerInfoData NG");
        #endif
        result = FAIL;
    }
    else if ((ShmChargerInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
        #ifdef SystemLogMessage
        LOG_ERROR("shmat ChargerInfoData NG");
        #endif
        result = FAIL;
    }
    if(result == PASS)
    {
        ShmPsuGrouping = &ShmChargerInfo->PsuGrouping;
        for(int i = 0; i < MAX_GROUP_QUANTITY; i++)
        {
            ShmOutputRelayConfig[i] = &ShmChargerInfo->PsuGrouping.OutputRelayConfig[i];
            ShmOutputRelayConfirmed[i] = &ShmChargerInfo->PsuGrouping.OutputRelayConfirmed[i];
        }
        ShmParallelRelayConfig = &ShmChargerInfo->PsuGrouping.ParallelRelayConfig;
        ShmParallelRelayConfirmed = &ShmChargerInfo->PsuGrouping.ParallelRelayConfirmed;

        if(ShmChargerInfo->Control.SysCtrl.bits.RelayBoardDisable == false)
        {
            LocationOutputRelayCtrl[0] = (OutputRelayControl *)&outputRelay[0].relay_event.relay_status[1];
            LocationOutputRelayCtrl[1] = (OutputRelayControl *)&outputRelay[0].relay_event.relay_status[2];
            LocationOutputRelayResponse[0] = (OutputRelayControl *)&regRelay[0].relay_event.relay_status[1];
            LocationOutputRelayResponse[1] = (OutputRelayControl *)&regRelay[0].relay_event.relay_status[2];
            memset(LocationParallelRelayCtrl, 0x00, sizeof(LocationParallelRelayCtrl));
            memset(LocationParallelRelayResponse, 0x00, sizeof(LocationParallelRelayResponse));
            LocaltionGfd[0] = (Connector_GFD *)&gfd_adc[0].Resister_conn1;
            LocaltionGfd[1] = (Connector_GFD *)&gfd_adc[0].Resister_conn2;

            if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
            {
                LocationOutputRelayCtrl[2] = (OutputRelayControl *)&outputRelay[1].relay_event.relay_status[1];
                LocationOutputRelayCtrl[3] = (OutputRelayControl *)&outputRelay[1].relay_event.relay_status[2];
                LocationOutputRelayResponse[2] = (OutputRelayControl *)&regRelay[1].relay_event.relay_status[1];
                LocationOutputRelayResponse[3] = (OutputRelayControl *)&regRelay[1].relay_event.relay_status[2];
                LocaltionGfd[2] = (Connector_GFD *)&gfd_adc[1].Resister_conn1;
                LocaltionGfd[3] = (Connector_GFD *)&gfd_adc[1].Resister_conn2;
            }
        }
    }

	return result;
}

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

	fd = open(relayRs485PortName, O_RDWR);
	if(fd <= 0)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("Module_InternalComm. InitComPort NG");
		#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;
		}
	}

	//DO360
	if(GENERAL_GUN_QUANTITY > 0 && target < GENERAL_GUN_QUANTITY)
	{
		//ShmSysConfigAndInfo->SysInfo.ConnectorInfo[target].GeneralChargingData.Index = target;
		chargingData[target] = &ShmSysConfigAndInfo->SysInfo.ConnectorInfo[target].GeneralChargingData;
		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[0].relay_event.relay_status); index++)
	{
		outputRelay[0].relay_event.relay_status[index] = 0x00;
		outputRelay[1].relay_event.relay_status[index] = 0x00;
	}

	while(!isPass)
	{
		isPass = true;
		for (byte _index = 0; _index < CONNECTOR_QUANTITY; _index++)
		{
			if (!FindChargingInfoData(_index, &_chargingData[0]))
			{
				LOG_ERROR("InternalComm : FindChargingInfoData false");
				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]))
				{
					LOG_ERROR("EvComm : FindAcChargingInfoData false");
					isPass = false;
					break;
				}
			}

			sleep(1);
		}
	}
}

bool IsNoneMatchRelayStatus(byte index)
{
    bool result = false;

    if(regRelay[index].relay_event.relay_status[0] != outputRelay[index].relay_event.relay_status[0] ||
        regRelay[index].relay_event.relay_status[1] != outputRelay[index].relay_event.relay_status[1] ||
        regRelay[index].relay_event.relay_status[2] != outputRelay[index].relay_event.relay_status[2])
	{
        if(TempRegRelay[index].relay_event.bits.AC_Contactor != outputRelay[index].relay_event.bits.AC_Contactor)
        {
            LOG_INFO("[%d]AC Contact Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.AC_Contactor == YES ? "On" : "Off");
        }
        if(TempRegRelay[index].relay_event.bits.CCS_Precharge != outputRelay[index].relay_event.bits.CCS_Precharge)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("[%d]CCS Precharge Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.CCS_Precharge == YES ? "On" : "Off");
#endif
        }
        if(TempRegRelay[index].relay_event.bits.Gun1_P != outputRelay[index].relay_event.bits.Gun1_P)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("[%d]SMR1:D+ Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.Gun1_P == YES ? "On" : "Off");
#endif
        }
        if(TempRegRelay[index].relay_event.bits.Gun1_N != outputRelay[index].relay_event.bits.Gun1_N)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("[%d]SMR1:D- Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.Gun1_N == YES ? "On" : "Off");
#endif
        }
        if(TempRegRelay[index].relay_event.bits.Gun2_P != outputRelay[index].relay_event.bits.Gun2_P)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("[%d]SMR2:D+ Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.Gun2_P == YES ? "On" : "Off");
#endif
        }
        if(TempRegRelay[index].relay_event.bits.Gun2_N != outputRelay[index].relay_event.bits.Gun2_N)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("[%d]SMR2:D- Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.Gun2_N == YES ? "On" : "Off");
#endif
        }
        if(TempRegRelay[index].relay_event.bits.Gun1_Parallel_P != outputRelay[index].relay_event.bits.Gun1_Parallel_P)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("[%d]Parallel:D+ Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.Gun1_Parallel_P == YES ? "On" : "Off");
#endif
        }
        if(TempRegRelay[index].relay_event.bits.Gun1_Parallel_N != outputRelay[index].relay_event.bits.Gun1_Parallel_N)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("[%d]Parallel:D- Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.Gun1_Parallel_N == YES ? "On" : "Off");
#endif
        }
        if(TempRegRelay[index].relay_event.bits.Gun2_Parallel_P != outputRelay[index].relay_event.bits.Gun2_Parallel_P)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("[%d]Parallel2:D+ Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.Gun2_Parallel_P == YES ? "On" : "Off");
#endif
        }
        if(TempRegRelay[index].relay_event.bits.Gun2_Parallel_N != outputRelay[index].relay_event.bits.Gun2_Parallel_N)
        {
#if RELAY_DEBUG_MSG
            LOG_INFO("[%d]Parallel2:D- Relay none match, need to %s", index,
                outputRelay[index].relay_event.bits.Gun2_Parallel_N == YES ? "On" : "Off");
#endif
        }

        TempRegRelay[index].relay_event.relay_status[0] = outputRelay[index].relay_event.relay_status[0];
        TempRegRelay[index].relay_event.relay_status[1] = outputRelay[index].relay_event.relay_status[1];
        TempRegRelay[index].relay_event.relay_status[2] = outputRelay[index].relay_event.relay_status[2];

		result = true;
	}

	return result;
}

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

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

void SetGfdConfig(byte index, byte resister)
{
    unsigned char add = 0;

	gfd_config.index = (index % 2);
	gfd_config.state = resister;
	add = index < 2 ? Addr.DO360_RC1 : Addr.DO360_RC2;

	//LOG_INFO("************************GFD Vol = %d, GFD Res = %d", gfd_config.reqVol, gfd_config.resister);
	if (Config_Gfd_Value(Uart5Fd, add, &gfd_config) == PASS)
	{
//		LOG_INFO("Set reqVol = %f, resister = %d",
//				gfd_config.reqVol,
//				gfd_config.resister);
//        if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[0].Parameter.bits.GfdDetection ||
//            ShmSysConfigAndInfo->SysInfo.ConnectorInfo[1].Parameter.bits.GfdDetection)
//        {
//            LOG_INFO("Set Relay %02X GFD Config index = %d, state = %d OK", add, gfd_config.index, gfd_config.state);
//        }
	}
//	else
//	{
//        if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[0].Parameter.bits.GfdDetection ||
//            ShmSysConfigAndInfo->SysInfo.ConnectorInfo[1].Parameter.bits.GfdDetection)
//        {
//            LOG_INFO("Set Relay %02X GFD Config index = %d, state = %d NG", add, gfd_config.index, gfd_config.state);
//        }
//	}
}

char GfdConfig[4];

void CableCheckDetected(byte index)
{
    unsigned char location = 0;
    char *strGfdConfig[] = {"Idle", "CableCheck", "PreCharge", "Charging"};

	// 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]->PantographFlag == YES)
    {
        location = ShmPsuGrouping->GroupCollection[index].Location;

        if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[index].Parameter.bits.GfdDetection &&
            _chargingData[index]->SystemStatus >= S_PREPARING_FOR_EVSE && _chargingData[index]->SystemStatus < S_TERMINATING)
        {
            if(_chargingData[index]->SystemStatus == S_PREPARING_FOR_EVSE && _chargingData[index]->RelayWeldingCheck == YES)
            {
                SetGfdConfig(location, GFD_CABLECHK);
                if(GfdConfig[index] != GFD_CABLECHK)
                {
                    LOG_INFO("Gun %d Set GFD = %s", index + 1, strGfdConfig[GFD_CABLECHK]);
                }
                GfdConfig[index] = GFD_CABLECHK;
            }
            else
            {
                SetGfdConfig(location, GFD_CHARGING);
                if(GfdConfig[index] != GFD_CHARGING)
                {
                    LOG_INFO("Gun %d Set GFD = %s", index + 1, strGfdConfig[GFD_CHARGING]);
                }
                GfdConfig[index] = GFD_CHARGING;
            }
        }
        else
        {
            SetGfdConfig(location, GFD_IDLE);
            if(GfdConfig[index] != GFD_IDLE)
            {
                LOG_INFO("Gun %d Set GFD = %s", index + 1, strGfdConfig[GFD_IDLE]);
            }
            GfdConfig[index] = GFD_IDLE;
            ShmSysConfigAndInfo->SysInfo.ConnectorInfo[index].Parameter.bits.GfdDetection = 0;
        }
    }
}

void CheckOutputPowerOverCarReq(byte index)
{
	float fireV = _chargingData[index]->FireChargingVoltage;
	float carV = _chargingData[index]->EvBatterytargetVoltage * 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.1)))
		{
			LOG_INFO("[Module_InternalComm]CheckOutputPowerOverCarReq NG : fire = %f, battery = %f",
					_chargingData[index]->FireChargingVoltage, (_chargingData[index]->EvBatterytargetVoltage * 10));
			LOG_INFO("[Module_InternalComm]CheckOutputPowerOverCarReq NG : fire = %f, battery = %f",
					_chargingData[index]->FireChargingVoltage, (_chargingData[index]->EvBatterytargetVoltage * 10));
			_chargingData[index]->StopChargeFlag = YES;
		}
	}
}

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;
				GetClockTime(&_checkOutputNoneMatchTimer[index]);
			}
			else
			{
				if ((GetTimeoutValue(_checkOutputNoneMatchTimer[index]) / 1000) >= 5000)
				{
					LOG_INFO("[Module_InternalComm]CheckOutputVolNoneMatchFire NG (%d) : pre = %f, fire = %f",
							index, (_chargingData[index]->PresentChargingVoltage * 10), _chargingData[index]->FireChargingVoltage);
					LOG_INFO("[Module_InternalComm]CheckOutputVolNoneMatchFire NG (%d): pre = %f, fire = %f",
							index, (_chargingData[index]->PresentChargingVoltage * 10), _chargingData[index]->FireChargingVoltage);
					_chargingData[index]->StopChargeFlag = YES;
				}
			}
		}
		else
			_isOutputNoneMatch[index] = NO;
	}
}

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

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

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

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

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

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

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

            if (temp < ExletTemp)
            {
                temp = ExletTemp;
            }
		}
		power += (_chargingData[index]->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 > 70)
		_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);
}

int main(void)
{
	if(InitShareMemory() == FAIL)
	{
		#ifdef SystemLogMessage
		LOG_ERROR("InitShareMemory NG");
		#endif
		if(ShmStatusCodeData!=NULL)
		{
			ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory=1;
		}
		sleep(5);
		return 0;
	}

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

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

	_RelaySelfTestOK = NO;
	memset(&outputRelay[0], 0x00, sizeof(Relay));
	memset(&outputRelay[1], 0x00, sizeof(Relay));

    if(ShmChargerInfo->Control.SysCtrl.bits.RelayBoardDisable == false)
    {
        if(ShmRelayModuleData[0]->SelfTest_Comp == NO)
        {
            if(Config_Relay_Output(Uart5Fd, Addr.DO360_RC1, &outputRelay[0]) != PASS)
                LOG_INFO("Config_Relay1_Output fail");
        }
        else
        {
            if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable == false)
            {
                _RelaySelfTestOK = YES;
            }
        }

        if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
        {
            if(ShmRelayModuleData[1]->SelfTest_Comp == NO)
            {
                if(Config_Relay_Output(Uart5Fd, Addr.DO360_RC2, &outputRelay[1]) != PASS)
                    LOG_INFO("Config_Relay2_Output fail");
            }
            else
            {
                _RelaySelfTestOK = YES;
            }
        }
    }

	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(;;)
	{
	    if(!ShmChargerInfo->Control.RelayCtrl.bits.Paused)
	    {
            // 程序開始之前~ 必須先確定 FW 版本與硬體版本,確認後!!~ 該模組才算是真正的 Initial Comp.
            if(ShmChargerInfo->Control.SysCtrl.bits.RelayBoardDisable == false)
            {
                if (ShmRelayModuleData[0]->SelfTest_Comp == NO && !ShmChargerInfo->Control.TestCtrl.bits.ChargingSimulation)
                {
                    // clena fw version
                    memset(ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev, 0x00, 32);

                    GetFwAndHwVersion_Relay();
                    SetRtcData_Relay(0);
                    sleep(1);

                    if(strlen((char *)ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev))
                    {
                        ShmRelayModuleData[0]->SelfTest_Comp = YES;

                        if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable == false)
                        {
                            _RelaySelfTestOK = YES;
                        }
                    }
                }

                // DO360 RC2
                if (ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable == true &&
                    ShmRelayModuleData[1]->SelfTest_Comp == NO && !ShmChargerInfo->Control.TestCtrl.bits.ChargingSimulation)
                {
                    // clena fw version
                    memset(ShmSysConfigAndInfo->SysInfo.Relay2ModuleFwRev, 0x00, 32);

                    GetFwAndHwVersion_Relay2();
                    SetRtcData_Relay(1);
                    sleep(1);

                    if (strlen((char *)ShmSysConfigAndInfo->SysInfo.Relay2ModuleFwRev) != 0)
                    {
                        ShmRelayModuleData[1]->SelfTest_Comp = YES;

                        if(ShmRelayModuleData[0]->SelfTest_Comp == YES)
                        {
                            _RelaySelfTestOK = YES;
                        }
                    }
                }
            }

            if(ShmChargerInfo->Control.SysCtrl.bits.FanBoardDisable == false)
            {
                if (ShmFanModuleData->SelfTest_Comp == NO && !ShmChargerInfo->Control.TestCtrl.bits.ChargingSimulation)
                {
                    // clena fw version
                    memset(ShmSysConfigAndInfo->SysInfo.FanModuleFwRev, 0x00, 32);

                    GetFwAndHwVersion_Fan();
                    SetModelName_Fan();
                    SetRtcData_Fan();
                    sleep(1);
                    GetClockTime(&_priority_time);

                    if(strlen((char *)ShmSysConfigAndInfo->SysInfo.FanModuleFwRev) != 0)
                    {
                        ShmFanModuleData->SelfTest_Comp = YES;
                    }
                }
            }


            if(_RelaySelfTestOK == YES || ShmChargerInfo->Control.TestCtrl.bits.ChargingSimulation)
            {
                // ==============優先權最高 10 ms ==============
                // 輸出電壓
                GetPersentOutputVol();

                // 三相輸入電壓
                GetPresentInputVol();

                GetRelayOutputStatus();

                GetGfdAdc();

                for(int i = 0; i < ShmChargerInfo->Control.MaxConnector; i++)
                {
                    // Cable check (Set)
                    CableCheckDetected(i);

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

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

                    if (_chargingData[i]->SystemStatus == S_IDLE)
                    {
                        _chargingData[i]->RelayWeldingCheck = NO;
                        _isRelayWelding[i] = NO;
                    }

                    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;

                        // 限定只有在槍類別為 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)
                        {
                            // DO360 do not check under voltage output & any voltage difference
                            //CheckOutputPowerOverCarReq(i);
                            //CheckOutputVolNoneMatchFire(i);
                        }
                        else
                            _isOutputNoneMatch[i] = NO;
                    }
                    else
                        _chargingData[i]->IsReadyToCharging = NO;
                }

                // 橋接 relay
                SetParalleRelayStatus();

                SetAcContactorStatus();

                // 搭上/鬆開 Relay
                if(IsNoneMatchRelayStatus(0))
                {
                    if (Config_Relay_Output(Uart5Fd, Addr.DO360_RC1, &outputRelay[0]))
                    {
                        //regRelay[0].relay_event.relay_status[0] = outputRelay[0].relay_event.relay_status[0];
                        //regRelay[0].relay_event.relay_status[1] = outputRelay[0].relay_event.relay_status[1];
                        //regRelay[0].relay_event.relay_status[2] = outputRelay[0].relay_event.relay_status[2];
                    }
                    if(ShmChargerInfo->Control.TestCtrl.bits.ChargingSimulation)
                    {
                        regRelay[0].relay_event.relay_status[0] = outputRelay[0].relay_event.relay_status[0];
                        regRelay[0].relay_event.relay_status[1] = outputRelay[0].relay_event.relay_status[1];
                        regRelay[0].relay_event.relay_status[2] = outputRelay[0].relay_event.relay_status[2];
                    }
                }

                if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
                {
                    // 搭上/鬆開 Relay
                    if(IsNoneMatchRelayStatus(1))
                    {
                        if (Config_Relay_Output(Uart5Fd, Addr.DO360_RC2, &outputRelay[1]))
                        {
                            //regRelay[1].relay_event.relay_status[0] = outputRelay[1].relay_event.relay_status[0];
                            //regRelay[1].relay_event.relay_status[1] = outputRelay[1].relay_event.relay_status[1];
                            //regRelay[1].relay_event.relay_status[2] = outputRelay[1].relay_event.relay_status[2];
                        }
                    }
                    if(ShmChargerInfo->Control.TestCtrl.bits.ChargingSimulation)
                    {
                        regRelay[1].relay_event.relay_status[0] = outputRelay[1].relay_event.relay_status[0];
                        regRelay[1].relay_event.relay_status[1] = outputRelay[1].relay_event.relay_status[1];
                        regRelay[1].relay_event.relay_status[2] = outputRelay[1].relay_event.relay_status[2];
                    }
                }
            }
	    }
	    if(!ShmChargerInfo->Control.FanCtrl.bits.Paused)
	    {
            if (ShmFanModuleData->SelfTest_Comp == YES ||
                    strlen((char *)ShmSysConfigAndInfo->SysInfo.FanModuleFwRev) != 0 ||
                    ShmSysConfigAndInfo->SysInfo.FanModuleFwRev[0] != '\0')
            {
                if (GetTimeoutValue(_priority_time) / 1000 >= 1000)
                {
                    GetFanSpeedByFunction();
                    GetFanSpeed();
                    ShmSysConfigAndInfo->SysInfo.SystemFanRotaSpeed = _setFanSpeed;
                    GetClockTime(&_priority_time);

                    unsigned short TargetSpeed = ShmFanModuleData->TestFanSpeed;

                    if(TargetSpeed != 0 && TargetSpeed < MIN_FAN_SPEED)
                    {
                        TargetSpeed = MIN_FAN_SPEED;
                    }
                    ShmFanModuleData->SetFan1Speed = TargetSpeed;
                    ShmFanModuleData->SetFan2Speed = TargetSpeed;
                    ShmFanModuleData->SetFan3Speed = TargetSpeed;
                    ShmFanModuleData->SetFan4Speed = TargetSpeed;

                    //LOG_INFO("set fan = %d", ShmFanModuleData->SetFan1Speed);
                    SetFanModuleSpeed();
                }
            }
	    }

		usleep(10000);
	}

	return FAIL;
}