/*
 * Module_Dispenser.c
 *
 *  Created on: 2021/10/26
 *      Author: folus
 */
#include "Module_Dispenser.h"

//==========================
// Timeout constant define
//==========================
#define TIMEOUT_SPEC_HANDSHAKING				180
#define TIMEOUT_SPEC_AUTH						15
#define TIMEOUT_SPEC_HANDSHAKING_LED			185
#define TIMEOUT_SPEC_REFRESH_CHARGING_INFO		30
#define TIMEOUT_SPEC_PROFILE_PREPARE			60
#define TIMEOUT_SPEC_BS_HLC_HANDSHAKE			60
#define TIMEOUT_SPEC_EV_READY					30
#define TIMEOUT_SPEC_CCS_HEARTBEAT_COUNT_RESET	10
#define TIMEOUT_SPEC_CCS_HANDSHAKE				120
#define TIMEOUT_SPEC_PWN_CHANGE					5
#define TIMEOUT_SPEC_POWERSAVING_LCD			120
#define TIMEOUT_SPEC_POWERSAVING_RFID			120
#define TIMEOUT_SPEC_POWERSAVING_METER			120
#define TIMEOUT_SPEC_POWERSAVING_LED_STATUS		120
#define TIMEOUT_SPEC_CEHCK_POWER_CONSUMPTION	15

#define is_error(ptr) 					((unsigned long)ptr > (unsigned long)-4000L)
#define ARRAY_SIZE(A)					(sizeof(A) / sizeof(A[0]))
#define PASS							1
#define FAIL							-1
#define YES								1
#define NO								0
#define ON								1
#define OFF								0
#define MtdBlockSize 					0x300000

struct SysConfigAndInfo					*ShmSysConfigAndInfo;
struct StatusCodeData 					*ShmStatusCodeData;
struct PrimaryMcuData					*ShmPrimaryMcuData;
struct DISPENSER						*ShmDispenser;

struct timespec							startTime[TMR_IDX_CNT];
struct timespec							startChargingTime;
struct timespec							endChargingTime;

struct SysConfigData					SysConfigOrg;

/*
 *
 * @param dest
 * @param src
 * @param start
 * @param cnt
 */
void substr(char *dest, const char* src, unsigned int start, unsigned int cnt)
{
	strncpy(dest, src + start, cnt);
	dest[cnt] = 0;
}

/*
 *
 * @param fmt
 * @return
 */
int StoreLogMsg(const char *fmt, ...)
{
	char Buf[4096+256];
	char buffer[4096];
	time_t CurrentTime;
	struct tm *tm;
	struct timeval tv;
	va_list args;

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

	memset(Buf,0,sizeof(Buf));
	CurrentTime = time(NULL);
	tm=localtime(&CurrentTime);
	gettimeofday(&tv, NULL); // get microseconds, 10^-6

	sprintf(Buf,"echo -n \"[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s\" >> /Storage/SystemLog/[%04d.%02d]DispenserLog",
				tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,tv.tv_usec,
				buffer,
				tm->tm_year+1900,tm->tm_mon+1);

#ifdef SystemLogMessage
	system(Buf);
#endif

#ifdef ConsloePrintLog
	printf("[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s", tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,tv.tv_usec, buffer);
#endif

	return rc;
}

/*
 *
 * @param timer
 */
void refreshStartTimer(struct timespec *timer)
{
	clock_gettime(CLOCK_MONOTONIC, timer);
}

/*
 *
 * @param clk
 * @return
 */
int getDiffSecNow(struct timespec timer)
{
	struct timespec timerNow;

	clock_gettime(CLOCK_MONOTONIC, &timerNow);

	return (int)((((unsigned long)(timerNow.tv_sec - timer.tv_sec) * 1000) + ((unsigned long)((timerNow.tv_nsec / 1000000) - (timer.tv_nsec / 1000000))))/1000);
}

/*
 *
 * @param start
 * @param end
 * @return
 */
int getDiffSecBetween(struct timespec start, struct timespec end)
{
	return (int)((((unsigned long)(end.tv_sec - start.tv_sec) * 1000) + ((unsigned long)((end.tv_nsec / 1000000) - (start.tv_nsec / 1000000))))/1000);
}

/*
 *
 * @param result
 */
void getDateTimeString(char* result)
{
	time_t CurrentTime;
	struct tm *tm;

	CurrentTime = time(NULL);
	tm=localtime(&CurrentTime);

	sprintf(result, "%04d.%02d.%02d %02d:%02d:%02d", tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
}

/*
 *
 * @param mode
 * @return
 */
char* getSystemModeName(unsigned char mode)
{
	char* result;

	switch(mode)
	{
		case SYS_MODE_BOOTING:
			result = "booting";
			break;
		case SYS_MODE_IDLE:
			result = "idle";
			break;
		case SYS_MODE_AUTHORIZING:
			result = "authorizing";
			break;
		case SYS_MODE_PREPARING:
			result = "preparing";
			break;
		case SYS_MODE_CHARGING:
			result = "charging";
			break;
		case SYS_MODE_TERMINATING:
			result = "terminating";
			break;
		case SYS_MODE_COMPLETE:
			result = "complete";
			break;
		case SYS_MODE_ALARM:
			result = "alarm";
			break;
		case SYS_MODE_FAULT:
			result = "fault";
			break;
		case SYS_MODE_MAINTAIN:
			result = "maintain";
			break;
		case SYS_MODE_RESERVATION:
			result = "reservation";
			break;
		case SYS_MODE_BOOKING:
			result = "booking";
			break;
		case SYS_MODE_DEBUG:
			result = "debug";
			break;
		case SYS_MODE_UPDATE:
			result = "upgrade";
			break;
		default:
			result = "unknown";
			break;
	}

	return result;
}

/*
 *
 * @param ptr
 * @return
 */
int LoadSysConfigAndInfo(struct SysConfigData *ptr)
{
	int fd,wrd;
	unsigned char *buf;
	unsigned int ChkSum,ChkSumOrg;

	if((buf=malloc(MtdBlockSize))==NULL)
	{

		DEBUG_ERROR("malloc buffer NG,rebooting..\n");

		if(ShmStatusCodeData!=NULL)
		{
			ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CsuInitFailed=1;
		}
		sleep(5);
		system("reboot -f");
		sleep(5);
		system("reboot -f");
	}
	memset(buf, 0, MtdBlockSize);

	//================================================
	// Load configuration from mtdblock10
	//================================================
	system("nanddump /dev/mtd10 -f /mnt/EvseConfig.bin");
	fd = open("/mnt/EvseConfig.bin", O_RDWR);
	if (fd < 0)
	{
		free(buf);

		DEBUG_ERROR("open mtdblock10 NG,rebooting..\n");

		if(ShmStatusCodeData!=NULL)
		{
			ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CsuInitFailed=1;
		}
		sleep(5);
		system("reboot -f");
		sleep(5);
		system("reboot -f");
	}
    wrd=read(fd, buf, MtdBlockSize);
	close(fd);
	if(wrd<MtdBlockSize)
	{
		free(buf);

		DEBUG_ERROR("read SysConfigData data NG,rebooting..\n");

		if(ShmStatusCodeData!=NULL)
		{
			ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CsuInitFailed=1;
		}
		sleep(5);
		system("reboot -f");
		sleep(5);
		system("reboot -f");
	}

	ChkSum=0;
	for(wrd=ARRAY_SIZE(ptr->CsuBootLoadFwRev);wrd<MtdBlockSize-4;wrd++)
	{
		ChkSum+=buf[wrd];
	}
	memcpy(&ChkSumOrg,buf+(MtdBlockSize-4),sizeof(ChkSumOrg));
	memcpy(&ptr->ModelName,buf+(ARRAY_SIZE(ptr->CsuBootLoadFwRev)),ARRAY_SIZE(ptr->ModelName));
	memcpy(&ptr->SerialNumber,buf+(ARRAY_SIZE(ptr->CsuBootLoadFwRev)+ARRAY_SIZE(ptr->ModelName)+ARRAY_SIZE(ptr->AcModelName)),ARRAY_SIZE(ptr->SerialNumber));

	//================================================
	// Load configuration from mtdblock11
	//================================================
	if(ChkSum!=ChkSumOrg)
	{
		DEBUG_ERROR("Primary SysConfigData checksum NG, read backup\n");
		system("nanddump /dev/mtd11 -f /mnt/EvseConfig.bin");
		fd = open("/mnt/EvseConfig.bin", O_RDWR);
		if (fd < 0)
		{
			free(buf);

			DEBUG_ERROR("open mtdblock11 (backup) NG,rebooting..\n");

			if(ShmStatusCodeData!=NULL)
			{
				ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CsuInitFailed=1;
			}
			sleep(5);
			system("reboot -f");
			sleep(5);
			system("reboot -f");
	    }
	    memset(buf, 0, MtdBlockSize);
   		wrd=read(fd, buf,MtdBlockSize);
    	close(fd);
		if(wrd<MtdBlockSize)
		{
			free(buf);

			DEBUG_ERROR("read backup SysConfigData data NG,rebooting..\n");

			if(ShmStatusCodeData!=NULL)
			{
				ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CsuInitFailed=1;
			}
			sleep(5);
			system("reboot -f");
			sleep(5);
			system("reboot -f");
		}

		ChkSum=0;
		for(wrd=ARRAY_SIZE(ptr->CsuBootLoadFwRev);wrd<MtdBlockSize-4;wrd++)
		{
			ChkSum+=buf[wrd];
		}
		memcpy(&ChkSumOrg,buf+(MtdBlockSize-4),sizeof(ChkSumOrg));

		//================================================
		// Load configuration from mtdblock12 (Factory default)
		//================================================
		if(ChkSum!=ChkSumOrg)
		{
			DEBUG_WARN("backup SysConfigData checksum NG, read Factory default\n");
			system("nanddump /dev/mtd12 -f /mnt/EvseConfig.bin");
			fd = open("/mnt/EvseConfig.bin", O_RDWR);
			if (fd < 0)
			{
				DEBUG_ERROR("open mtdblock12 (Factory default) NG,rebooting..\n");

				free(buf);
				if(ShmStatusCodeData!=NULL)
				{
					ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CsuInitFailed=1;
				}
				sleep(5);
				system("reboot -f");
				sleep(5);
				system("reboot -f");
	    	}
	    	memset(buf, 0, MtdBlockSize);
   			wrd=read(fd, buf,MtdBlockSize);
    		close(fd);
			if(wrd<MtdBlockSize)
			{
				DEBUG_ERROR("read factory default  SysConfigData data NG,rebooting..\n");

				free(buf);
				if(ShmStatusCodeData!=NULL)
				{
					ShmStatusCodeData->AlarmCode.AlarmEvents.bits.CsuInitFailed=1;
				}
				sleep(5);
				system("reboot -f");
				sleep(5);
				system("reboot -f");
			}

			ChkSum=0;
			for(wrd=ARRAY_SIZE(ptr->CsuBootLoadFwRev);wrd<MtdBlockSize-4;wrd++)
			{
				ChkSum+=buf[wrd];
			}
			memcpy(&ChkSumOrg,buf+(MtdBlockSize-4),sizeof(ChkSumOrg));
			memcpy(buf+(ARRAY_SIZE(ptr->CsuBootLoadFwRev)), &ptr->ModelName, ARRAY_SIZE(ptr->ModelName));
			memcpy(buf+(ARRAY_SIZE(ptr->CsuBootLoadFwRev)+ARRAY_SIZE(ptr->ModelName)+ARRAY_SIZE(ptr->AcModelName)), &ptr->SerialNumber, ARRAY_SIZE(ptr->SerialNumber));

			if(ChkSum!=ChkSumOrg)
			{
				DEBUG_WARN("factory default  SysConfigData checksum NG, restore factory default\n");
				free(buf);
				system("cd /root;./Module_FactoryConfig -m");
				system("rm -f /Storage/OCPP/OCPPConfiguration");
				system("sync");
				sleep(5);
				system("reboot -f");
				sleep(5);
				system("reboot -f");

				return FAIL;
			}
		}
	}

	//load OK
	memcpy((struct SysConfigData *)ptr,buf,sizeof(struct SysConfigData));
	free(buf);

	system("rm -f /mnt/EvseConfig.bin");

	// SysConfig in flash is empty (0xffffffff)
	if((strlen((char*)ShmSysConfigAndInfo->SysConfig.ModelName) > ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.ModelName)) ||
	   (strlen((char*)ShmSysConfigAndInfo->SysConfig.SerialNumber) > ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.SerialNumber)) ||
	   (strlen((char*)ShmSysConfigAndInfo->SysConfig.SystemId) > ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.SystemId)) ||
	   (ShmSysConfigAndInfo->SysConfig.Eth0Interface.EthDhcpClient == 0xff))
	{
		if(strlen((char*)ShmSysConfigAndInfo->SysConfig.ModelName) > ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.ModelName))
		{
			memset(ShmSysConfigAndInfo->SysConfig.ModelName, 0x00, ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.ModelName));
		}

		if(strlen((char*)ShmSysConfigAndInfo->SysConfig.SerialNumber) > ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.SerialNumber))
		{
			memset(ShmSysConfigAndInfo->SysConfig.SerialNumber, 0x00, ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.SerialNumber));
		}

		if(strlen((char*)ShmSysConfigAndInfo->SysConfig.SystemId) > ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.SystemId))
		{
			memset(ShmSysConfigAndInfo->SysConfig.SystemId, 0x00, ARRAY_SIZE(ShmSysConfigAndInfo->SysConfig.SystemId));
		}

		if(ShmSysConfigAndInfo->SysConfig.Eth0Interface.EthDhcpClient == 0xff)
		{
			DEBUG_INFO("Ethernet dhcp config is null.\n");
		}

		if(strlen((char*)ShmSysConfigAndInfo->SysConfig.ModelName) == 0x00)
		{
			DEBUG_INFO("Model name over length.\n");
		}

		if(strlen((char*)ShmSysConfigAndInfo->SysConfig.SerialNumber) == 0x00)
		{
			DEBUG_INFO("Model serial number over length.\n");
		}

		if(strlen((char*)ShmSysConfigAndInfo->SysConfig.SystemId) == 0x00)
		{
			DEBUG_INFO("SystemId over length.\n");
		}

		system("cd /root;./Module_FactoryConfig -m");
		sleep(3);
		system("/usr/bin/run_evse_restart.sh");
	}

	DEBUG_INFO("Load SysConfigData OK\n");

	return PASS;
}

/*
 *
 * @param UsrData
 * @return
 */
int StoreUsrConfigData(struct SysConfigData *UsrData)
{
	int result = PASS;
	int fd,wrd;
	unsigned int i,Chk;
	unsigned char *ptr, *BufTmp;

	Chk=0;
	ptr=(unsigned char *)UsrData;
	if((BufTmp=malloc(MtdBlockSize))!=NULL)
	{
		memset(BufTmp,0,MtdBlockSize);
		memcpy(BufTmp,ptr,sizeof(struct SysConfigData));
		for(i=0;i<MtdBlockSize-4;i++)
			Chk+=*(BufTmp+i);
		memcpy(BufTmp+MtdBlockSize-4, &Chk, 4);

		// Output configuration to file.
		fd = open("/mnt/EvseConfig.bin", O_RDWR|O_CREAT);
		if (fd < 0)
		{
			DEBUG_ERROR("open /mnt/EvseConfig.bin NG\n");

			free(BufTmp);
			return 0;
		}
		wrd=write(fd, BufTmp, MtdBlockSize);
		close(fd);
		if(wrd<MtdBlockSize)
		{
			DEBUG_ERROR("write /mnt/EvseConfig.bin NG\n");

			free(BufTmp);
			return 0;
		}
		DEBUG_INFO("EvseConfig write to file in /mnt OK.\n");

		DEBUG_INFO("Erase /dev/mtd10.\n");
		runShellCmd("flash_erase /dev/mtd10 0 0");
		DEBUG_INFO("Write /dev/mtd10.\n");
		runShellCmd("nandwrite -p /dev/mtd10 /mnt/EvseConfig.bin");

		DEBUG_INFO("Erase /dev/mtd11.\n");
		runShellCmd("flash_erase /dev/mtd11 0 0");
		DEBUG_INFO("Write /dev/mtd11.\n");
		runShellCmd("nandwrite -p /dev/mtd11 /mnt/EvseConfig.bin");

		system("rm -f /mnt/EvseConfig.bin");
		DEBUG_INFO("EvseConfig write to flash OK\n");
	}
	else
	{
		DEBUG_ERROR("alloc BlockSize NG\r\n");
    		result = FAIL;
	}

	if(BufTmp!=NULL)
		free(BufTmp);

	return result;
}


/*
 *
 * @param gun_index
 * @param mode
 * @return
 */
unsigned char isMode(unsigned char mode)
{
	return ((ShmDispenser->gun_info.SystemStatus == mode)?YES:NO);
}

/*
 *
 * @param gun_index
 * @return
 */
unsigned char isModeChange()
{
	unsigned char result = NO;

	if(!isMode(ShmDispenser->gun_info.PreviousSystemStatus))
	{
		result = YES;
		ShmDispenser->gun_info.PreviousSystemStatus = ShmDispenser->gun_info.SystemStatus;
	}

	return result;
}

/*
 *
 * @param gun_index
 * @param mode
 */
void setChargerMode(unsigned char mode)
{
	ShmDispenser->gun_info.PreviousSystemStatus = ShmDispenser->gun_info.SystemStatus;
	ShmDispenser->gun_info.SystemStatus = mode;

	DEBUG_INFO("System mode switch from %s to %s\n", getSystemModeName(ShmDispenser->gun_info.PreviousSystemStatus), getSystemModeName(ShmDispenser->gun_info.SystemStatus));
}

/*
 *
 * @return
 */
int getRequest()
{
	return ShmDispenser->gun_info.legacyRequest.isLegacyRequest;
}

/*
 *
 * @param gun_index
 * @return
 */
int getRelay()
{
	return ShmDispenser->gun_info.legacyRequest.isRelayOn;
}

/*
 *
 * @param gun_index
 * @return
 */
int presentChargedEnergyClear()
{
	int result = FAIL;

	ShmDispenser->gun_info.PresentChargedEnergy = 0;
	memset(ShmDispenser->gun_info.presentChargedEnergyPeriod, 0x00, ARRAY_SIZE(ShmDispenser->gun_info.presentChargedEnergyPeriod)*sizeof(float));
	result = PASS;

	return result;
}

/*
 *
 * @param gun_index
 * @return
 */
float presentChargedEnergyTotal()
{
	float result = 0.0f;

	for(int idx=0;idx<ARRAY_SIZE(ShmDispenser->gun_info.presentChargedEnergyPeriod);idx++)
	{
		result += ShmDispenser->gun_info.presentChargedEnergyPeriod[idx];
	}

	return result;
}

/*
 *
 * @param gun_index
 * @return
 */
int presentChargedEnergyUpdate()
{
	int result = FAIL;
	time_t CurrentTime;
	struct tm *tm;

	CurrentTime = time(NULL);
	tm=localtime(&CurrentTime);

	if(ShmDispenser->ConfigData.AcPhaseCount==1)
	{
		// Resolution: 0.0001 kwh
		ShmDispenser->gun_info.presentChargedEnergyPeriod[tm->tm_hour] += (((float)(ShmDispenser->gun_info.powerConsumptionTotal.power_consumption - ShmDispenser->gun_info.powerConsumptionTotal.power_consumption_at_start))/10000.0) - presentChargedEnergyTotal();

	}
	else
	{
		// Resolution: 0.0001 kwh
		ShmDispenser->gun_info.presentChargedEnergyPeriod[tm->tm_hour] += ((((float)(ShmDispenser->gun_info.powerConsumption[0].power_consumption - ShmDispenser->gun_info.powerConsumption[0].power_consumption_at_start))/10000.0) +
																				    (((float)(ShmDispenser->gun_info.powerConsumption[1].power_consumption - ShmDispenser->gun_info.powerConsumption[1].power_consumption_at_start))/10000.0) +
																				    (((float)(ShmDispenser->gun_info.powerConsumption[2].power_consumption - ShmDispenser->gun_info.powerConsumption[2].power_consumption_at_start))/10000.0))
																					- presentChargedEnergyTotal();
	}

	ShmDispenser->gun_info.PresentChargedEnergy = presentChargedEnergyTotal();

	return result;
}


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

	//Initial ShmSysConfigAndInfo
	if ((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo),  0777)) < 0)
    {
		DEBUG_ERROR("shmget ShmSysConfigAndInfo NG\n");
		result = FAIL;
	}
    else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
    	DEBUG_ERROR("shmat ShmSysConfigAndInfo NG\n");
    	result = FAIL;
   	 }
    else
    {}

   	//Initial ShmStatusCodeData
   	if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0)
    {
   		DEBUG_ERROR("shmget ShmStatusCodeData NG\n");
   		result = FAIL;
	}
    else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
    	DEBUG_ERROR("shmat ShmStatusCodeData NG\n");
    	result = FAIL;
   	}
    else
    {}

	//Initial ShmSDispenser
	if ((MeterSMId = shmget(ShmDispenserKey, sizeof(struct DISPENSER), 0777)) < 0)
	{
		DEBUG_ERROR("shmget ShmSDispenser NG\n");
		result = FAIL;
	}
	else if ((ShmDispenser = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		DEBUG_ERROR("shmat ShmSDispenser NG\n");
		result = FAIL;
	}

    return result;
}


/*
 * Upgrade firmware
 * @return
 */
int upgrade_check()
{
	int result = PASS;
	int fd;
	DIR *dir;
	struct dirent *file;
	char cmd[512];
	long int MaxLen=48*1024*1024;

	if ((dir = opendir ("/mnt")) != NULL)
	{
		/* print all the files and directories within directory */
		while ((file = readdir (dir)) != NULL)
		{
			if((strlen(file->d_name)>2))
			{
				// Wait for MCU upgrade finish.
				while(ShmDispenser->gun_info.mcuFlag.isMcuUpgradeReq)sleep(1);

				memset(&ShmDispenser->fwUpgradeInfo, 0xFF, sizeof(Fw_Upgrade_Info));
				DEBUG_INFO("New firmware file: %s\n", file->d_name);
				sprintf(ShmDispenser->fwUpgradeInfo.location, "/mnt/%s", file->d_name);

				if((fd=open(ShmDispenser->fwUpgradeInfo.location, O_RDONLY)) >= 0)
				{
					unsigned char *ptr = malloc(MaxLen); //-48 is take out the header
					memset(ptr, 0xFF, MaxLen);  //-48 is take out the header
					read(fd, ptr, MaxLen);
					close(fd);

					ShmDispenser->fwUpgradeInfo.fwType = ((ptr[0x13]<<0) | (ptr[0x12]<<8) | (ptr[0x11]<<16) | (ptr[0x10]<<24));
					substr(ShmDispenser->fwUpgradeInfo.modelName, (char *)ptr, 0, 0x10);
					DEBUG_INFO("New firmware type: %X\n", ShmDispenser->fwUpgradeInfo.fwType);
					DEBUG_INFO("New firmware model name: %s, %s\n", ShmDispenser->fwUpgradeInfo.modelName, ShmDispenser->ConfigData.ModelName);

					if((ShmDispenser->fwUpgradeInfo.modelName[0] == ShmDispenser->ConfigData.ModelName[0]) &&
					   (ShmDispenser->fwUpgradeInfo.modelName[1] == ShmDispenser->ConfigData.ModelName[1]) &&
					   (ShmDispenser->fwUpgradeInfo.modelName[7] == ShmDispenser->ConfigData.ModelName[7]) &&
					   (ShmDispenser->fwUpgradeInfo.modelName[8] == ShmDispenser->ConfigData.ModelName[8]) &&
					   (ShmDispenser->fwUpgradeInfo.modelName[9] == ShmDispenser->ConfigData.ModelName[9]) &&
					   (ShmDispenser->fwUpgradeInfo.modelName[11] == ShmDispenser->ConfigData.ModelName[11]) &&
					   (ShmDispenser->fwUpgradeInfo.modelName[12] == ShmDispenser->ConfigData.ModelName[12]) &&
					   (ShmDispenser->fwUpgradeInfo.modelName[13] == ShmDispenser->ConfigData.ModelName[13]) &&
					   (ShmDispenser->fwUpgradeInfo.fwType>0))
					{
						switch(ShmDispenser->fwUpgradeInfo.fwType)
						{
							case CSU_MLO:
							case CSU_BOOTLOADER:
							case CSU_KERNEL_CONFIGURATION:
							case CSU_KERNEL_IMAGE:
							case CSU_ROOT_FILE_SYSTEM:
							case CSU_USER_CONFIGURATION:
							case CSU_PRIMARY_CONTROLLER:
								if(Upgrade_Flash(ShmDispenser->fwUpgradeInfo.fwType, ShmDispenser->fwUpgradeInfo.location, ShmDispenser->fwUpgradeInfo.modelName) != PASS)
								{
									result = FAIL;
								}
								else
								{
									if(ShmDispenser->fwUpgradeInfo.fwType == CSU_USER_CONFIGURATION)
									{
										DEBUG_INFO("Restore model name & serial number.\n");
										memcpy(&SysConfigOrg, &ShmDispenser->ConfigData, sizeof(struct SysConfigData));

										if(LoadSysConfigAndInfo(&ShmSysConfigAndInfo->SysConfig) != PASS)
										{
											DEBUG_INFO("Re-load configuration fail.\n");
											result = FAIL;
										}
										else
										{
											memcpy(&ShmSysConfigAndInfo->SysConfig.ModelName, &SysConfigOrg.ModelName, ARRAY_SIZE(SysConfigOrg.ModelName));
											memcpy(&ShmSysConfigAndInfo->SysConfig.SerialNumber, &SysConfigOrg.SerialNumber, ARRAY_SIZE(SysConfigOrg.SerialNumber));
											memcpy(&ShmSysConfigAndInfo->SysConfig.SystemId, &SysConfigOrg.SystemId, ARRAY_SIZE(SysConfigOrg.SystemId));

											if(StoreUsrConfigData(&ShmSysConfigAndInfo->SysConfig) != PASS)
											{
												DEBUG_INFO("Re-write configuration fail.\n");
												result = FAIL;
											}
											else
												DEBUG_INFO("Re-write configuration OK.\n");
										}
									}
								}
								sprintf(cmd, "yes|rm %s", ShmDispenser->fwUpgradeInfo.location);
								system(cmd);
								break;
							case AC_WALLMOUNT_CONTROLLER:
								ShmDispenser->gun_info.mcuFlag.isMcuUpgradeReq = ON;
								sleep(10);
								break;

							default:
								result = FAIL;
								DEBUG_WARN("Image file is unknown type.\n");
								sprintf(cmd, "yes|rm %s", ShmDispenser->fwUpgradeInfo.location);
								system(cmd);
								break;
						}
					}
					else
					{
						result = FAIL;
						DEBUG_ERROR("Model name and Firmware type error.\n");
						sprintf(cmd, "yes|rm %s", ShmDispenser->fwUpgradeInfo.location);
						system(cmd);
					}
					free(ptr);
				}
				else
				{
					result = FAIL;
					DEBUG_ERROR("New firmware open error.\n");
				}
			}
			else
			{
				if(strlen(file->d_name) >= 3)
				{
					result = FAIL;
					DEBUG_ERROR("File name error.\n");
				}
				else
				{
					DEBUG_ERROR("Searching file.\n");
				}
			}
		}
		closedir (dir);
	}
	else
	{
		result = FAIL;
		DEBUG_ERROR("/mnt does not valid.\n");
	}

	return result;
}

/*
 *
 * @return
 */
int main(void)
{
	// Initial share memory
	if(InitShareMemory() == FAIL)
	{
		DEBUG_ERROR("InitShareMemory NG\n");

		if(ShmStatusCodeData!=NULL)
		{
			ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory=ON;
		}
		sleep(5);
		return 0;
	}

	ShmDispenser->gun_info.PreviousSystemStatus = 0xff;
	ShmDispenser->gun_info.primaryMcuState.rotatory_switch = 0xff;
	ShmDispenser->gun_info.mcuResetRequest.isMcuResetRequest = ON;
	ShmDispenser->gun_info.isSetBreatheLedTiming = OFF;
	ShmDispenser->gun_info.isSetLedBrightness = OFF;

	DEBUG_INFO("Module_Dispenser initialized...\n");

	for(;;)
	{
		//==========================================
		// Check initialization "PASS" or "FAIL"
		//==========================================
		if(ShmDispenser->gun_info.SystemStatus != SYS_MODE_BOOTING)
		{
			// Alarm event check
			if((ShmDispenser->gun_info.primaryMcuAlarm.InputAlarmCode > 0))
			{
				if(ShmDispenser->gun_info.SystemStatus != SYS_MODE_ALARM)
				{
					if(ShmDispenser->gun_info.SystemStatus != SYS_MODE_UPDATE)
					{
						setChargerMode(SYS_MODE_ALARM);
					}
				}
			}
		}

		//==========================================
		// Switch operative
		//==========================================
		if(ShmDispenser->gun_info.isOperactive)
		{
			if(isMode(SYS_MODE_MAINTAIN))
			{
				setChargerMode(SYS_MODE_IDLE);
			}
		}
		else
		{
			if(isMode(SYS_MODE_IDLE))
			{
				setChargerMode(SYS_MODE_MAINTAIN);
			}
		}

		//==========================================
		// Upgrade check
		//==========================================
		if(ShmDispenser->gun_info.isUpgradeReq && !ShmDispenser->gun_info.isUpgradeEnd)
		{
			if((ShmDispenser->gun_info.SystemStatus == SYS_MODE_IDLE) || (ShmDispenser->gun_info.SystemStatus == SYS_MODE_ALARM))
			{

				if((ShmDispenser->gun_info.isUpgradePASS = upgrade_check()) == PASS)
				{
					DEBUG_INFO("Upgrade process pass.\n");
				}
				else
				{
					DEBUG_ERROR("Upgrade process fail.\n");
				}

				ShmDispenser->gun_info.isUpgradeEnd = ON;
			}
		}

		//==========================================
		// Gun process
		//==========================================
		switch(ShmDispenser->gun_info.SystemStatus)
		{
			case SYS_MODE_BOOTING:
				if(isModeChange())
				{}

				if(ShmDispenser->gun_info.mcuFlag.isReadFwVerPass &&
				   ShmDispenser->gun_info.mcuFlag.isSetModelNamePass &&
				   ShmDispenser->gun_info.mcuFlag.isSetSerialNumberPass)
				{
					// Set max current to rating current
					ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = ShmDispenser->gun_info.primaryMcuState.rating_current;

					// If rotate switch equal zero, the system needs to change Debug mode
					if(ShmDispenser->gun_info.primaryMcuState.rotatory_switch == 0)
						setChargerMode(SYS_MODE_DEBUG);
					else
						setChargerMode(SYS_MODE_IDLE);
				}

				break;
			case SYS_MODE_IDLE:
				if(isModeChange())
				{
					ShmDispenser->gun_info.isGunPlugged = NO;
					ShmDispenser->gun_info.systemAlarmCode.SystemAlarmCode = 0x00;
					ShmDispenser->gun_info.PresentChargedDuration = 0;
					presentChargedEnergyClear();
					ShmDispenser->gun_info.targetCurrent = 0xFF;
					ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_IDLE;
					ShmDispenser->gun_info.chargingMode = CHARGING_MODE_BS;
					ShmDispenser->gun_info.isDoEvReadyOnce = OFF;
					ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_100;
					ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
				}

				if(ShmDispenser->gun_info.legacyRequest.isLegacyRequest)
				{
					ShmDispenser->gun_info.isGunPlugged = NO;
					ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = YES;

					if(ShmDispenser->isCcsEnable)
					{
						ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_5;
					}
					else
					{
						ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = (ShmDispenser->gun_info.targetCurrent==0?CCS_PWM_DUTY_100:ShmDispenser->gun_info.targetCurrent);
					}
					ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = YES;

					setChargerMode(SYS_MODE_AUTHORIZING);
				}
				else
				{}

				break;
			case SYS_MODE_AUTHORIZING:
				if(isModeChange())
				{}

				setChargerMode(SYS_MODE_PREPARING);

				break;
			case SYS_MODE_PREPARING:
				if(isModeChange())
				{
					refreshStartTimer(&startTime[TMR_IDX_HANDSHAKING]);

					if(ShmDispenser->isCcsEnable)
					{
						ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_DUTY_5;
					}
					else
					{
						ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_CP_STATE_E;
						ShmDispenser->gun_info.chargingMode = CHARGING_MODE_BS;
					}
				}

				// If control pilot detect Bx, skip watch dog time out.
				if((ShmDispenser->gun_info.primaryMcuState.cp_state == CP_STATE_B) ||
				   (ShmDispenser->gun_info.primaryMcuState.cp_state == CP_STATE_C))
				{
					ShmDispenser->gun_info.isGunPlugged = YES;
					switch(ShmDispenser->gun_info.ccsHandshakeState)
					{
						case HANDSHAKE_DUTY_5:
							//Let CCS task start to negotiate
							ShmDispenser->gun_info.acCcsInfo.ChargingPermission = ON;
							ShmDispenser->gun_info.acCcsInfo.EVSENotification = NOTIFICATION_NONE;

							// Set CCS 5% PWM duty
							if(ShmDispenser->gun_info.acCcsInfo.CpSetPWMDuty == CCS_PWM_DUTY_5)
							{
								ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_5;
								ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
								ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_DUTY_5_CHECK;
								DEBUG_INFO("ccsHandshakeState = HANDSHAKE_DUTY_5_CHECK\n");
							}
							break;
						case HANDSHAKE_DUTY_5_CHECK:
							if((ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty == OFF))
							{
								//2 secs timeout
								refreshStartTimer(&startTime[TMR_IDX_BS_HLC_HANDSHAKE]);
								DEBUG_INFO("HLC slac handshake start.\n");
								ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_CCS;
								DEBUG_INFO("ccsHandshakeState = HANDSHAKE_CCS\n");
							}
							break;
						case HANDSHAKE_CCS:
							//CCS handshake timeout
							if(getDiffSecNow(startTime[TMR_IDX_BS_HLC_HANDSHAKE]) > TIMEOUT_SPEC_BS_HLC_HANDSHAKE)
							{
								ShmDispenser->gun_info.acCcsInfo.ChargingPermission = OFF;
								ShmDispenser->gun_info.acCcsInfo.EVSENotification = NOTIFICATION_STOP;
								DEBUG_INFO("HLC %d secs slac handshake timeout.\n", TIMEOUT_SPEC_BS_HLC_HANDSHAKE);

								if(ShmDispenser->isCcsEnable)
								{
									DEBUG_INFO("TIMEOUT_SPEC_BS_HLC_HANDSHAKE.\n");
									ShmDispenser->gun_info.acCcsInfo.ChargingPermission = OFF;
									ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_CP_STATE_F;
									ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
									ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_CP_STATE_E;
									ShmDispenser->gun_info.chargingMode = CHARGING_MODE_BS;
									break;
								}
								else
								{
									ShmDispenser->gun_info.acCcsInfo.ChargingPermission = OFF;
									ShmDispenser->gun_info.acCcsInfo.EVSENotification = NOTIFICATION_STOP;
									DEBUG_INFO("BS/HLC %d secs handshake timeout.\n", TIMEOUT_SPEC_BS_HLC_HANDSHAKE);
								}

							}

							if((ShmDispenser->gun_info.acCcsInfo.ChargingPermission == OFF) && (ShmDispenser->gun_info.acCcsInfo.CpSetPWMDuty != CCS_PWM_DUTY_5))
							{
								DEBUG_INFO("Wait CCS give up negotiagting.\n");
								ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_CP_STATE_F;
								ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
								ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_CP_STATE_E;
								ShmDispenser->gun_info.chargingMode = CHARGING_MODE_BS;
							}

							//CCS status check
							if((16 <= ShmDispenser->gun_info.acCcsInfo.PresentMsgFlowStatus) && (ShmDispenser->gun_info.acCcsInfo.PresentMsgFlowStatus < 253))
							{
								//change PWM duty to BS
								if((ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty == OFF)&&(ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current!=CCS_PWM_DUTY_5))
								{
									ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current =CCS_PWM_DUTY_5;
									ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
									DEBUG_INFO("ccsHandshakeState = HANDSHAKE_CCS (set 5% duty)\n");
								}
								refreshStartTimer(&startTime[TMR_IDX_HANDSHAKING]);
							}

							if((37 < ShmDispenser->gun_info.acCcsInfo.PresentMsgFlowStatus) && (ShmDispenser->gun_info.acCcsInfo.PresentMsgFlowStatus < 49))
							{
								 //chang PWM duty to BS
								if((ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty == OFF)&&(ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current!=CCS_PWM_DUTY_5))
								{
									ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current =CCS_PWM_DUTY_5;
									ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
								}
								ShmDispenser->gun_info.chargingMode = CHARGING_MODE_HLC;
								ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_HLC_MODE;
								DEBUG_INFO("ccsHandshakeState = HANDSHAKE_HLC_MODE\n");
							}
							else if((54 < ShmDispenser->gun_info.acCcsInfo.PresentMsgFlowStatus) && (ShmDispenser->gun_info.acCcsInfo.PresentMsgFlowStatus <= 255))
							{
								DEBUG_INFO("ccsHandshakeState = CHARGING_MODE_BS, CCS terminated\n");
								ShmDispenser->gun_info.acCcsInfo.ChargingPermission = OFF;
								ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_CP_STATE_F;
								ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
								ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_CP_STATE_E;
								ShmDispenser->gun_info.chargingMode = CHARGING_MODE_BS;
							}

							break;
						case HANDSHAKE_CP_STATE_E:
							if(ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty == OFF)
							{
								if(ShmDispenser->isCcsEnable)
								{
									DEBUG_INFO("Change to CP STATE E for 4 secs.\n");
									//CP STATE E for 4 secs
									sleep(4);
								}

								//restore normal CP PWM duty
								// Determine max charging current to MCU
								DEBUG_INFO("Determine max charging current to MCU.\n");
								ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = (ShmDispenser->gun_info.targetCurrent==0?CCS_PWM_DUTY_100:ShmDispenser->gun_info.targetCurrent);
								ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = YES;
								ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_SET_MAX_CURRENT;
								refreshStartTimer(&startTime[TMR_IDX_BS_HLC_HANDSHAKE]);
							}
							break;
						case HANDSHAKE_SET_MAX_CURRENT:
							if(ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty == OFF)
							{
								ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_BS_MODE;
								DEBUG_INFO("Enter BS Mode charging.\n");
								//for EV READY 30 secs didn't start charging to STATE E
								refreshStartTimer(&startTime[TMR_IDX_HANDSHAKING]);
							}
							break;
						case HANDSHAKE_BS_MODE:
							refreshStartTimer(&startTime[TMR_IDX_HANDSHAKING]);

							presentChargedEnergyClear();
							getDateTimeString((char*)ShmDispenser->gun_info.StartDateTime);
							ShmDispenser->gun_info.powerConsumptionTotal.power_consumption_at_start = ShmDispenser->gun_info.powerConsumptionTotal.power_consumption;
							ShmDispenser->gun_info.powerConsumption[0].power_consumption_at_start = ShmDispenser->gun_info.powerConsumption[0].power_consumption;
							ShmDispenser->gun_info.powerConsumption[1].power_consumption_at_start = ShmDispenser->gun_info.powerConsumption[1].power_consumption;
							ShmDispenser->gun_info.powerConsumption[2].power_consumption_at_start = ShmDispenser->gun_info.powerConsumption[2].power_consumption;
							refreshStartTimer(&startChargingTime);

							setChargerMode(SYS_MODE_CHARGING);


							//EV READY CHECK
							/*
							if((DiffTimebWithNow(startTime[gun_index][TMR_IDX_BS_HLC_HANDSHAKE]) > TIMEOUT_SPEC_EV_READY) && (ShmCharger->gun_info[gun_index].isEvReady2StateE == OFF))
							{
								if(ShmCharger->gun_info[gun_index].isDoEvReadyOnce == OFF)
								{
									DEBUG_INFO("EV READY STATE E 4sec.\n");
									ShmCharger->gun_info[gun_index].isEvReady2StateE = ON;
									ShmCharger->gun_info[gun_index].primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_CP_STATE_E;
									ShmCharger->gun_info[gun_index].mcuFlag.isSetCpPwmDuty = ON;
									ShmCharger->gun_info[gun_index].evReadyState = EV_READY_STAT_E;
								}
							}

							if(ShmCharger->gun_info[gun_index].isEvReady2StateE == ON)
							{
								switch(ShmCharger->gun_info[gun_index].evReadyState)
								{
									case EV_READY_STAT_E:
										if(ShmCharger->gun_info[gun_index].mcuFlag.isSetCpPwmDuty == OFF)
										{
											sleep(4);
											ShmCharger->gun_info[gun_index].primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_100;
											ShmCharger->gun_info[gun_index].mcuFlag.isSetCpPwmDuty = ON;
											ShmCharger->gun_info[gun_index].evReadyState = EV_READY_STAT_C;
										}
										break;
									case EV_READY_STAT_C:
										if(ShmCharger->gun_info[gun_index].mcuFlag.isSetCpPwmDuty == OFF)
										{
											usleep(500000);
											DEBUG_INFO("EV READY Determine max charging current to MCU.\n");
											if(ShmSysConfigAndInfo->SysConfig.MaxChargingCurrent == 0)
											{
												ShmCharger->gun_info[gun_index].targetCurrent = ((ShmCharger->gun_info[gun_index].targetCurrent > ShmCharger->gun_info[gun_index].primaryMcuState.rating_current)?ShmCharger->gun_info[gun_index].primaryMcuState.rating_current:ShmCharger->gun_info[gun_index].targetCurrent);
												if(ShmCharger->gun_info[gun_index].targetCurrent != ShmCharger->gun_info[gun_index].primaryMcuCp_Pwn_Duty.max_current)
												{
													ShmCharger->gun_info[gun_index].primaryMcuCp_Pwn_Duty.max_current = ((ShmCharger->gun_info[gun_index].targetCurrent > ShmCharger->gun_info[gun_index].primaryMcuState.rating_current)?ShmCharger->gun_info[gun_index].primaryMcuState.rating_current:ShmCharger->gun_info[gun_index].targetCurrent);
													ShmCharger->gun_info[gun_index].mcuFlag.isSetCpPwmDuty = ON;
												}
											}
											else
											{
												ShmCharger->gun_info[gun_index].targetCurrent = ((ShmCharger->gun_info[gun_index].targetCurrent > ShmSysConfigAndInfo->SysConfig.MaxChargingCurrent)?ShmSysConfigAndInfo->SysConfig.MaxChargingCurrent:ShmCharger->gun_info[gun_index].targetCurrent);
												if(ShmCharger->gun_info[gun_index].targetCurrent != ShmCharger->gun_info[gun_index].primaryMcuCp_Pwn_Duty.max_current)
												{
													ShmCharger->gun_info[gun_index].primaryMcuCp_Pwn_Duty.max_current = ((ShmCharger->gun_info[gun_index].targetCurrent > ShmSysConfigAndInfo->SysConfig.MaxChargingCurrent)?ShmSysConfigAndInfo->SysConfig.MaxChargingCurrent:ShmCharger->gun_info[gun_index].targetCurrent);
													ShmCharger->gun_info[gun_index].mcuFlag.isSetCpPwmDuty = ON;
												}
											}
											ShmCharger->gun_info[gun_index].mcuFlag.isSetCpPwmDuty = ON;
											ShmCharger->gun_info[gun_index].evReadyState = EV_READY_SET_MAX_CURRENT;
										}
										break;
									case EV_READY_SET_MAX_CURRENT:
										if(ShmCharger->gun_info[gun_index].mcuFlag.isSetCpPwmDuty == OFF)
										{
											ShmCharger->gun_info[gun_index].isEvReady2StateE = OFF;
											ShmCharger->gun_info[gun_index].isDoEvReadyOnce = ON;
										}
										break;
									default:
										break;
								}
							}*/

							break;
						case HANDSHAKE_HLC_MODE:
							if(ShmDispenser->gun_info.acCcsInfo.EVChargeProgress == HLC_START_MODE)//powerDelivery
							{
								ShmDispenser->gun_info.isCCSStartTransation = ON;
							}

							if((ShmDispenser->gun_info.isCCSStartTransation == ON))
							{
								presentChargedEnergyClear();
								getDateTimeString((char*)ShmDispenser->gun_info.StartDateTime);
								ShmDispenser->gun_info.powerConsumptionTotal.power_consumption_at_start = ShmDispenser->gun_info.powerConsumptionTotal.power_consumption;
								ShmDispenser->gun_info.powerConsumption[0].power_consumption_at_start = ShmDispenser->gun_info.powerConsumption[0].power_consumption;
								ShmDispenser->gun_info.powerConsumption[1].power_consumption_at_start = ShmDispenser->gun_info.powerConsumption[1].power_consumption;
								ShmDispenser->gun_info.powerConsumption[2].power_consumption_at_start = ShmDispenser->gun_info.powerConsumption[2].power_consumption;

								ShmDispenser->gun_info.isCCSStartTransation = OFF;
								setChargerMode(SYS_MODE_CHARGING);
								refreshStartTimer(&startChargingTime);
								refreshStartTimer(&startTime[TMR_IDX_CCS_HEARTBEAT_COUNT_RESET]);
							}

							//120 sec timeout
							if(getDiffSecNow(startTime[TMR_IDX_HANDSHAKING]) > TIMEOUT_SPEC_CCS_HANDSHAKE)
							{
								if(getDiffSecNow(startTime[TMR_IDX_HANDSHAKING]) > TIMEOUT_SPEC_CCS_HANDSHAKE+5)
								{
									DEBUG_INFO("CCS 120 secs handshake timeout, change to BS Mode...\n");
									//Cancel CCS task negotiating
									ShmDispenser->gun_info.acCcsInfo.ChargingPermission = OFF;
									ShmDispenser->gun_info.acCcsInfo.EVSENotification = NOTIFICATION_STOP;
									//ShmDispenser->gun_info.isCCSWaitChangeDuty = ON;

									if((ShmDispenser->gun_info.acCcsInfo.ChargingPermission == OFF) && (ShmDispenser->gun_info.acCcsInfo.CpSetPWMDuty != CCS_PWM_DUTY_5))
									{
										DEBUG_INFO("Wait CCS give up negotiagting.\n");
										ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_CP_STATE_F;
										ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
										ShmDispenser->gun_info.ccsHandshakeState = HANDSHAKE_CP_STATE_E;
										ShmDispenser->gun_info.chargingMode = CHARGING_MODE_BS;
									}
								}
							}

							break;
						default:
							break;
					}
				}
				else if(ShmDispenser->gun_info.primaryMcuState.socket_e.isSocketEMode)
				{
					DEBUG_INFO("Enter Socket-E Mode charging.\n");
					ShmDispenser->gun_info.chargingMode = CHARGING_MODE_SOCKETE;

					refreshStartTimer(&startChargingTime);
					setChargerMode(SYS_MODE_CHARGING);
				}

				// Unplug charging gun to Idle mode
				if((ShmDispenser->gun_info.primaryMcuState.cp_state == CP_STATE_A) && (ShmDispenser->gun_info.isGunPlugged == YES))
				{
					DEBUG_INFO("Charging gun is plugged before.\n");
					//Cancel CCS task negotiating
					ShmDispenser->gun_info.acCcsInfo.ChargingPermission = OFF;
					ShmDispenser->gun_info.acCcsInfo.EVSENotification = NOTIFICATION_STOP;
					ShmDispenser->gun_info.isCCSWaitChangeDuty = ON;
				}

				if(getDiffSecNow(startTime[TMR_IDX_HANDSHAKING]) > ShmDispenser->timeoutSpec.Present_Timeout_Spec)
				{
					if(getDiffSecNow(startTime[TMR_IDX_HANDSHAKING]) > (ShmDispenser->timeoutSpec.Present_Timeout_Spec+5))
					{
						DEBUG_INFO("Handshaking timeout...\n");

						//Cancel CCS task negotiating
						ShmDispenser->gun_info.acCcsInfo.ChargingPermission = OFF;
						ShmDispenser->gun_info.acCcsInfo.EVSENotification = NOTIFICATION_STOP;
						ShmDispenser->gun_info.isCCSWaitChangeDuty = ON;
					}
				}

				if(((ShmDispenser->gun_info.isCCSWaitChangeDuty == ON) && ShmDispenser->gun_info.acCcsInfo.CpSetPWMDuty != CCS_PWM_DUTY_5) ||
					!ShmDispenser->gun_info.legacyRequest.isLegacyRequest)
				{
					ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_100;
					ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
					ShmDispenser->gun_info.isCCSWaitChangeDuty = OFF;

					setChargerMode(SYS_MODE_IDLE);
				}
				break;

			case SYS_MODE_CHARGING:
				if(isModeChange())
				{
					refreshStartTimer(&startTime[TMR_IDX_REFRESH_CHARGING_INFO]);
					refreshStartTimer(&startTime[TMR_IDX_PROFILE_PREPARE]);
					refreshStartTimer(&startTime[TMR_IDX_PWN_CHANGE]);

					ShmDispenser->gun_info.isChargerStopByCondition = NO;
					ShmDispenser->gun_info.resultAuthorization = DEFAULT_RFID;
				}

				//if time up, clear CCS MSG count
				if((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC) &&
				   (getDiffSecNow(startTime[TMR_IDX_CCS_HEARTBEAT_COUNT_RESET]) > TIMEOUT_SPEC_CCS_HEARTBEAT_COUNT_RESET))
				{
					refreshStartTimer(&startTime[TMR_IDX_CCS_HEARTBEAT_COUNT_RESET]);
				}

				if(!ShmDispenser->gun_info.legacyRequest.isLegacyRequest ||
				   ((ShmDispenser->gun_info.chargingMode != CHARGING_MODE_SOCKETE) && ShmDispenser->gun_info.primaryMcuState.cp_state != CP_STATE_C) ||
				   ((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_SOCKETE) && !ShmDispenser->gun_info.primaryMcuState.socket_e.isSocketEPinOn) ||
				   ((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC) && (ShmDispenser->gun_info.acCcsInfo.EVChargeProgress == HLC_STOP_MODE)) ||
				   ((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC) && ShmDispenser->gun_info.acCcsInfo.EVChargeProgress == HLC_RENEGOTIATE_MODE) ||
				   ((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC) && ShmDispenser->gun_info.acCcsInfo.EVChargeProgress == HLC_STANDBY_MODE) ||
				   ((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC) && ((49 <= ShmDispenser->gun_info.acCcsInfo.PresentMsgFlowStatus) && (ShmDispenser->gun_info.acCcsInfo.PresentMsgFlowStatus <= 255))))
				{
					setChargerMode(SYS_MODE_TERMINATING);
					//setRelay(gun_index, OFF);
				}
				else
				{
					// Charging session info calculation
					refreshStartTimer(&endChargingTime);
					ShmDispenser->gun_info.PresentChargedDuration = getDiffSecBetween(startChargingTime, endChargingTime);
					presentChargedEnergyUpdate();

					// Determine max charging current to MCU
					if(ShmDispenser->gun_info.chargingMode == CHARGING_MODE_BS)
					{
						if(getDiffSecNow(startTime[TMR_IDX_PWN_CHANGE]) > TIMEOUT_SPEC_PWN_CHANGE)
						{
							ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = (ShmDispenser->gun_info.targetCurrent==0?CCS_PWM_DUTY_100:ShmDispenser->gun_info.targetCurrent);
							ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = YES;
							refreshStartTimer(&startTime[TMR_IDX_PWN_CHANGE]);
						}
					}
					else if(ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC)
					{
						ShmDispenser->gun_info.acCcsInfo.EVSEMaxCurrent = (float)((ShmDispenser->gun_info.targetCurrent > ShmDispenser->gun_info.primaryMcuState.rating_current)?ShmDispenser->gun_info.primaryMcuState.rating_current:ShmDispenser->gun_info.targetCurrent);
					}

					// Debug information
					if(getDiffSecNow(startTime[TMR_IDX_REFRESH_CHARGING_INFO]) > TIMEOUT_SPEC_REFRESH_CHARGING_INFO)
					{
						DEBUG_INFO("==================================================\n");
						DEBUG_INFO("gun_info.primaryMcuCp_Pwn_Duty.max_current: %d\n", ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current);
						DEBUG_INFO("gun_info.targetCurrent: %d\n", ShmDispenser->gun_info.targetCurrent);
						DEBUG_INFO("==================================================\n");
						refreshStartTimer(&startTime[TMR_IDX_REFRESH_CHARGING_INFO]);

						getDateTimeString((char*)ShmDispenser->gun_info.StopDateTime);
					}
				}

				break;
			case SYS_MODE_TERMINATING:
				if(isModeChange())
				{
					getDateTimeString((char*)ShmDispenser->gun_info.StopDateTime);
				}

				refreshStartTimer(&endChargingTime);
				if(ShmDispenser->gun_info.PresentChargedDuration != 0)
				{
					ShmDispenser->gun_info.PresentChargedDuration = getDiffSecBetween(startChargingTime, endChargingTime);
				}

				// End authorize pass
				if(!ShmDispenser->gun_info.legacyRequest.isLegacyRequest ||
				   ((ShmDispenser->gun_info.chargingMode != CHARGING_MODE_SOCKETE) && (ShmDispenser->gun_info.primaryMcuState.cp_state == CP_STATE_A)) ||
				   ((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_SOCKETE) && !ShmDispenser->gun_info.primaryMcuState.socket_e.isSocketEPinOn) ||
				   ((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC) && (ShmDispenser->gun_info.acCcsInfo.EVChargeProgress == HLC_STOP_MODE)))
				{
					if(ShmDispenser->gun_info.chargingMode == CHARGING_MODE_BS)
					{
						ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_100;
						ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
						sleep(1);
						setChargerMode(SYS_MODE_COMPLETE);
					}
					else if(ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC)
					{
						//setChargerMode(gun_index, SYS_MODE_COMPLETE);
						//Cancel CCS task negotiating
						ShmDispenser->gun_info.acCcsInfo.ChargingPermission = OFF;
						ShmDispenser->gun_info.acCcsInfo.EVSENotification = NOTIFICATION_STOP;
						ShmDispenser->gun_info.isCCSWaitChangeDuty = ON;
					}
					else
					{
						setChargerMode(SYS_MODE_COMPLETE);
					}
				}
				else
				{
					// Debug information
					if(getDiffSecNow(startTime[TMR_IDX_REFRESH_CHARGING_INFO]) > TIMEOUT_SPEC_REFRESH_CHARGING_INFO)
					{
						DEBUG_INFO("==================================================\n");
						DEBUG_INFO("gun_info.primaryMcuCp_Pwn_Duty.max_current: %d\n", ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current);
						DEBUG_INFO("gun_info.targetCurrent: %d\n", ShmDispenser->gun_info.targetCurrent);
						DEBUG_INFO("==================================================\n");
						refreshStartTimer(&startTime[TMR_IDX_REFRESH_CHARGING_INFO]);
					}

					if(getDiffSecNow(startTime[TMR_IDX_PWN_CHANGE]) > TIMEOUT_SPEC_PWN_CHANGE)
					{
						ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = (ShmDispenser->gun_info.targetCurrent==0?CCS_PWM_DUTY_100:ShmDispenser->gun_info.targetCurrent);
						ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = YES;
						refreshStartTimer(&startTime[TMR_IDX_PWN_CHANGE]);
					}

					if(ShmDispenser->gun_info.legacyRequest.isLegacyRequest &&
					   (((ShmDispenser->gun_info.chargingMode != CHARGING_MODE_SOCKETE) && (ShmDispenser->gun_info.primaryMcuState.cp_state == CP_STATE_C)) || ((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_SOCKETE) && ShmDispenser->gun_info.primaryMcuState.socket_e.isSocketEPinOn)) &&
					   (((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC) && (ShmDispenser->gun_info.acCcsInfo.EVChargeProgress == HLC_START_MODE)) || (ShmDispenser->gun_info.chargingMode == CHARGING_MODE_BS))
					  )
					{
						setChargerMode(SYS_MODE_CHARGING);
					}
				}

				if((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_HLC) &&
				   (ShmDispenser->gun_info.isCCSWaitChangeDuty == ON) &&
				   (ShmDispenser->gun_info.acCcsInfo.CpSetPWMDuty == CCS_PWM_DUTY_100))
				{
					DEBUG_INFO("Set PWM duty 100%% go to SYS_MODE_TERMINATING.\n");

					ShmDispenser->gun_info.primaryMcuCp_Pwn_Duty.max_current = CCS_PWM_DUTY_100;
					ShmDispenser->gun_info.mcuFlag.isSetCpPwmDuty = ON;
					ShmDispenser->gun_info.isCCSWaitChangeDuty = OFF;
					setChargerMode( SYS_MODE_COMPLETE);
				}

				break;
			case SYS_MODE_COMPLETE:
				if(isModeChange())
				{}

				if(((ShmDispenser->gun_info.chargingMode != CHARGING_MODE_SOCKETE) && (ShmDispenser->gun_info.primaryMcuState.cp_state == CP_STATE_A)) ||
				   ((ShmDispenser->gun_info.chargingMode == CHARGING_MODE_SOCKETE) && (!ShmDispenser->gun_info.primaryMcuState.socket_e.isSocketEPinOn)))
				{
					setChargerMode(SYS_MODE_IDLE);
				}

				break;
			case SYS_MODE_ALARM:
				if((ShmDispenser->gun_info.systemAlarmCode.SystemAlarmCode == 0))
				{
					if((ShmDispenser->gun_info.PreviousSystemStatus == SYS_MODE_CHARGING) ||
					   (ShmDispenser->gun_info.PreviousSystemStatus == SYS_MODE_TERMINATING))
					{
						setChargerMode(ShmDispenser->gun_info.PreviousSystemStatus);
					}
					else
					{
						setChargerMode(SYS_MODE_IDLE);
					}
				}

				break;
			case SYS_MODE_FAULT:
				if(isModeChange())
				{}

				break;
			case SYS_MODE_MAINTAIN:
				if(isModeChange())
				{}

				if(ShmDispenser->gun_info.isOperactive)
				{
					DEBUG_INFO("Connector change to operactive.\n");
					setChargerMode(SYS_MODE_IDLE);
				}

				break;
			case SYS_MODE_UPDATE:
				if(isModeChange())
				{}

				break;
			case SYS_MODE_DEBUG:
				if(isModeChange())
				{}

				break;
		}

		usleep(100000);
	}

	return -1;
}