/*
 * Module_PowerSharing.c
 *
 *  Created on: 2020/12/07
 *      Author: foluswen
 */
#include "Module_PowerSharing.h"

struct SysConfigAndInfo		*ShmSysConfigAndInfo;
struct StatusCodeData 		*ShmStatusCodeData;
struct OCPP16Data			*ShmOCPP16Data;
struct Charger				*ShmCharger;
struct POWER_SHARING		*ShmPowerSharing;

//==========================================
// Common routine
//==========================================
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((time_t*)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]Module_PowerSharingLog",
				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;
}

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

void dM(uint8_t *data, uint16_t len, uint8_t isRX)
{
#ifdef DEBUG
	uint8_t output[8192];

	if(isRX)
	{
		DEBUG_INFO("- RX --------------------------------------------\n");
	}
	else
	{
		DEBUG_INFO("- TX --------------------------------------------\n");
	}

	memset(output, 0x00, ARRAY_SIZE(output));
	for(uint16_t idx=0;idx<16;idx++)
		sprintf((char*)output, "%s %02X", output, idx);
	DEBUG_INFO("%s\n", output);
	DEBUG_INFO("-------------------------------------------------\n");

	for(uint16_t idx = 0;idx<len;idx++)
	{
		if((idx%16)>0)
		{
			sprintf((char*)output, "%s %02X", output, data[idx]);
		}
		else
		{
			if(idx != 0)
				DEBUG_INFO("%s\n", output);
			memset(output, 0x00, ARRAY_SIZE(output));
			sprintf((char*)output, "%s %02X", output, data[idx]);
		}
	}
	DEBUG_INFO("%s\n", output);
	DEBUG_INFO("-------------------------------------------------\n");
#endif
}

int isValidCheckSum(struct Message *message)
{
	uint8_t chksum = 0x00;

	for(int idx=0;idx<((message->buffer[1]+3)>ARRAY_SIZE(message->buffer)?ARRAY_SIZE(message->buffer):(message->buffer[1]+3));idx++)
	{
		chksum ^= message->buffer[idx];
	}

	return ((chksum == message->buffer[((message->buffer[1]+3)>ARRAY_SIZE(message->buffer)?ARRAY_SIZE(message->buffer):(message->buffer[1]+3))]) ? PASS : FAIL);
}

uint8_t chksumCal(struct Message *message)
{
	uint8_t chksum=0;

	for(int idx=0;idx<((message->buffer[1]+3)>ARRAY_SIZE(message->buffer)?ARRAY_SIZE(message->buffer):(message->buffer[1]+3));idx++)
	{
		chksum ^= message->buffer[idx];
	}

	return chksum & 0xff;
}

//==========================================
// Init all share memory
//==========================================
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 ShmOCPP16Data
	if ((MeterSMId = shmget(ShmOcppModuleKey, sizeof(struct OCPP16Data),  0777)) < 0)
	{
		DEBUG_ERROR("shmget ShmOCPP16Data NG");
		result = FAIL;
	}
	else if ((ShmOCPP16Data = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		DEBUG_ERROR("shmat ShmOCPP16Data NG");
		result = FAIL;
	}
	else
	{}

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

	//Create ShmPowerSharing
	if ((MeterSMId = shmget(ShmPowerShargingKey, sizeof(struct POWER_SHARING), IPC_CREAT | 0777)) < 0)
	{
		DEBUG_ERROR("shmget ShmPowerShargingKey NG\n");

		result = FAIL;
	}
	else if ((ShmPowerSharing = shmat(MeterSMId, NULL, 0)) == (void *) -1)
	{
		DEBUG_ERROR("shmat ShmPowerShargingKey NG\n");

		result = FAIL;
	}
	memset(ShmPowerSharing,0,sizeof(struct POWER_SHARING));

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
		ShmPowerSharing->Connection_Info[idx].socketFd = (idx+1);

    return result;
}

//==========================================
// TCP socket server routine
//==========================================
int conn_getDupFd(void)
{
	int result = 0;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(!ShmPowerSharing->Connection_Info[idx].isSocketConnected)
		{
			result = ShmPowerSharing->Connection_Info[idx].socketFd;
			break;
		}
	}

	return result;
}

int conn_register(int socketFd)
{
	int result = FAIL;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(!ShmPowerSharing->Connection_Info[idx].isSocketConnected)
		{
			DEBUG_INFO("Dupfd-%d register to conn-%d.\n", socketFd, idx);
			ShmPowerSharing->Connection_Info[idx].isSocketConnected = TRUE;
			ShmPowerSharing->Connection_Info[idx].socketFd = socketFd;
			ShmPowerSharing->Connection_Info[idx].lastHeartBeatTime = time((time_t*)NULL);
			result = PASS;
			break;
		}
	}

	return result;
}

int conn_reject(int socketFd)
{
	int result = FAIL;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].socketFd == socketFd)
		{
			DEBUG_INFO("Dupfd-%d register from conn_info-%d.\n", socketFd, idx);
			ShmPowerSharing->Connection_Info[idx].isSocketConnected = FALSE;
			ShmPowerSharing->Connection_Info[idx].isGunConnected = FALSE;
			result = PASS;
			break;
		}
	}

	return result;

}

int conn_getConectedQuantity(void)
{
	int result = 0;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].isSocketConnected)
		{
			result += 1;
		}
	}

	DEBUG_INFO("Connection quantity: %d\n", result);
	ShmPowerSharing->connectedQty = result;
	return result;
}

int conn_updateHeartBeatTime(int socketFd)
{
	int result = FAIL;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].socketFd == socketFd)
		{
			//DEBUG_INFO("Dupfd-%d register from conn_info-%d update heart beat time.\n", socketFd, idx);
			ShmPowerSharing->Connection_Info[idx].lastHeartBeatTime = time((time_t*)NULL);
			result = PASS;
			break;
		}
	}

	return result;
}

int conn_getStatusStarttime(int socketFd)
{
	int result = 0;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].socketFd == socketFd)
		{
			result = ShmPowerSharing->Connection_Info[idx].lastGetStatusTime;
			break;
		}
	}

	return result;
}

int conn_getStatusStarttimeUpdate(int socketFd)
{
	int result = FAIL;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].socketFd == socketFd)
		{
			ShmPowerSharing->Connection_Info[idx].lastGetStatusTime = time((time_t*)NULL);;
			result = PASS;
			break;
		}
	}

	return result;
}

int conn_setCapacityStarttime(int socketFd)
{
	int result = 0;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].socketFd == socketFd)
		{
			result = ShmPowerSharing->Connection_Info[idx].lastSetCapacityTime;
			break;
		}
	}

	return result;
}

int conn_setCapacityStarttimeUpdate(int socketFd)
{
	int result = FAIL;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].socketFd == socketFd)
		{
			ShmPowerSharing->Connection_Info[idx].lastSetCapacityTime = time((time_t*)NULL);;
			result = PASS;
			break;
		}
	}

	return result;
}

int conn_update_status(int socketFd, uint8_t pilotState, uint8_t availableCurrent, uint8_t presentCurrent, uint16_t acPhase)
{
	int result = FAIL;
	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].socketFd == socketFd)
		{
			if(!ShmPowerSharing->Connection_Info[idx].isGunConnected &&
			   (2<=pilotState) &&
			   (pilotState<=7))
			{
				ShmPowerSharing->hasNewConn = YES;
			}

			if((ShmPowerSharing->Connection_Info[idx].isGunConnected != (2<=pilotState)&&(pilotState<=7)?YES:NO) ||
			   (ShmPowerSharing->Connection_Info[idx].presentOutputCurrent != presentCurrent) ||
			   (ShmPowerSharing->Connection_Info[idx].acPhase != acPhase))
			{
				DEBUG_INFO("Conn-%d pilot state: %d\n", idx, pilotState);
				DEBUG_INFO("Conn-%d available current: %d\n", idx, availableCurrent);
				DEBUG_INFO("Conn-%d preset output current: %d\n", idx, presentCurrent);
				DEBUG_INFO("Conn-%d ac power phase: %d\n", idx, acPhase);
				DEBUG_INFO("==================================\n");
			}

			ShmPowerSharing->Connection_Info[idx].isGunConnected = (2<=pilotState)&&(pilotState<=7)?YES:NO;
			ShmPowerSharing->Connection_Info[idx].presentOutputCurrent = presentCurrent;
			ShmPowerSharing->Connection_Info[idx].acPhase = acPhase;
			result = PASS;
		}
	}

	return result;
}

int conn_getOnHandCurrent(void)
{
	int result = 0;

	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].isSocketConnected)
		{
			result += ShmPowerSharing->Connection_Info[idx].availableSharingCurrent;
		}
	}

	result = ShmCharger->gun_info[0].primaryMcuState.rating_current - result;

	DEBUG_INFO("Total available current: %d\n", result);
	return result;
}

void create_cmd_getStatus(struct Message *out)
{
	memset(out->buffer, 0, ARRAY_SIZE(out->buffer));

	out->size = 4;
	out->buffer[0] = 0x55;
	out->buffer[1] = 0x00;
	out->buffer[2] = SHARING_CMD_GET_STATUS;
	out->buffer[3] = chksumCal(out);

	dM(out->buffer, out->size, FALSE);
}

void create_cmd_SetAvailableCurrent(struct Message *out, int socketFd)
{
	memset(out->buffer, 0, ARRAY_SIZE(out->buffer));

	out->size = 5;
	out->buffer[0] = 0x55;
	out->buffer[1] = 0x01;
	out->buffer[2] = SHARING_CMD_SET_CAPACITY;
	for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
	{
		if(ShmPowerSharing->Connection_Info[idx].socketFd == socketFd)
		{
			out->buffer[3] = ShmPowerSharing->Connection_Info[idx].availableSharingCurrent;
		}
	}
	out->buffer[4] = chksumCal(out);
	dM(out->buffer, out->size, FALSE);
}

int tcpSocketServerStart(void)
{
	int 				sockFd = 0;
	int 				clientSockFd = 0;
	int					dupFd = 0;
	struct Message		input;
	struct Message		output;
	struct sockaddr_in 	serverInfo, clientInfo;
	socklen_t 			addrlen = sizeof(clientInfo);

	sockFd = socket(AF_INET , SOCK_STREAM , 0);
	if(sockFd == -1)
	{
		DEBUG_ERROR("TCP service socket create fail.\n");
		sleep(5);
		return FAIL;
	}

	bzero(&serverInfo,sizeof(serverInfo));
	serverInfo.sin_family = PF_INET;
	serverInfo.sin_addr.s_addr = htonl(INADDR_ANY);
	serverInfo.sin_port = htons(LISTEN_PORT_TCP);

	if(bind(sockFd, (struct sockaddr *)&serverInfo, sizeof(serverInfo)) < 0)
		DEBUG_ERROR("TCP server socket bind fail.\n");

	if(listen(sockFd, CONNECTION_LIMIT) < 0)
		DEBUG_ERROR("TCP server socket listen fail.\n");
	else
		DEBUG_INFO("Power sharing TCP server initial listen on port %d.\n", LISTEN_PORT_TCP);

	// Main loop
	for(;;)
	{
		clientSockFd = accept(sockFd, (struct sockaddr*) &clientInfo, &addrlen);
		fcntl(clientSockFd, F_SETFD, FD_CLOEXEC);
		DEBUG_INFO("Client connect in.\n");
		DEBUG_INFO("clientSockFd : %d\n", clientSockFd);

		if(clientSockFd > 0)
		{
			if(conn_getConectedQuantity() < CONNECTION_LIMIT)
			{
				// Fork a child process to handle the new conn
				if(fork()==0)
				{
					uint8_t idxStep = 0;
					uint8_t socketEnable = YES;
					struct timeval	tv;
					tv.tv_sec = 0;
					tv.tv_usec = 500000;
					setsockopt(clientSockFd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
					// Assign socket handle as available handle in conn info pool
					dupFd = dup2(clientSockFd, conn_getDupFd());
					conn_register(dupFd);

					while(socketEnable)
					{
						if((input.size = recv(dupFd, input.buffer, sizeof(input.buffer), 0)) > 0)
						{
							dM(input.buffer, input.size, YES);

							if(isValidCheckSum(&input))
							{
								conn_updateHeartBeatTime(dupFd);

								memset(output.buffer, 0x00, ARRAY_SIZE(output.buffer));
								switch(input.buffer[2])
								{
									case SHARING_CMD_GET_STATUS:
										conn_update_status(dupFd, input.buffer[3], input.buffer[4], input.buffer[5], input.buffer[6]);

										break;

									case SHARING_CMD_SET_CAPACITY:
										if(!input.buffer[3])
											DEBUG_INFO("Set connection-%d available current fail \n");
										break;

									default:
										DEBUG_WARN("Receive unknown command.\n");
										break;
								}
							}
							else
							{
								DEBUG_WARN("Receive command check sum error.\n");
							}
						}
						else if(input.size == 0)
						{
							DEBUG_INFO("Client disSocketConnected.\n");
							conn_reject(dupFd);
							socketEnable = NO;
							close(dupFd);
							close(clientSockFd);
							fflush(stdout);
						}
						else if(input.size == -1)
						{
							// Server slave handler
							switch(idxStep)
							{
								case 0:
									if((difftime(time((time_t*)NULL), conn_getStatusStarttime(dupFd)) >= 3) || (difftime(time((time_t*)NULL), conn_getStatusStarttime(dupFd)) < 0))
									{
										create_cmd_getStatus(&output);
										conn_getStatusStarttimeUpdate(dupFd);
										send(clientSockFd, output.buffer, output.size, 0);
									}

									idxStep++;
									break;
								default:
									if((difftime(time((time_t*)NULL), conn_setCapacityStarttime(dupFd)) >= 1) || (difftime(time((time_t*)NULL), conn_setCapacityStarttime(dupFd)) < 0))
									{
										create_cmd_SetAvailableCurrent(&output, dupFd);
										conn_setCapacityStarttimeUpdate(dupFd);
										send(clientSockFd, output.buffer, output.size, 0);
									}

									idxStep = 0;
									break;
							}
						}
					}

					conn_getConectedQuantity();
					exit(0);
				}
				else
				{
					// if parent, close the socket and go back to listening new requests
					close(clientSockFd);
				}
			}
			else
			{
				DEBUG_WARN("Connection is over limit.\n");
				output.size = 4;
				output.buffer[0] = 0x55;
				output.buffer[1] = 0x00;
				output.buffer[2] = SHARING_CMD_CONNECTION_FULL;
				output.buffer[3] = chksumCal(&output);
				send(clientSockFd, output.buffer, output.size, 0);
				close(clientSockFd);
			}
		}

		sleep(1);
	}

	return FAIL;
}

//==========================================
// Client routine
//==========================================
int tcpSocketClientStart(void)
{
	int 				sockfd;
	struct sockaddr_in 	info;
	struct hostent 		*ghbn;
	struct timeval 		tv;
	uint8_t 			socketEnable;
	uint8_t				cntSocketErr;

	struct Message		input;
	struct Message		output;

	bzero(&info,sizeof(info));
	ghbn = gethostbyname((char*)"192.168.10.10");
	info.sin_family = PF_INET;
	info.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)ghbn->h_addr_list[0]));
	info.sin_port = htons(LISTEN_PORT_TCP);
	ShmSysConfigAndInfo->SysInfo.localSharingInfo.isConnectedSharingServer = OFF;
	DEBUG_INFO("Connect to %s:%d\n", inet_ntoa(*(struct in_addr *)ghbn->h_addr_list[0]), LISTEN_PORT_TCP);

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd == -1)
	{
		DEBUG_ERROR("Fail to create a socket.");
		return 0;
	}

	if(connect(sockfd, (struct sockaddr *)&info,sizeof(info)) ==-1)
	{
		DEBUG_ERROR("Connection error.\n");
		ShmSysConfigAndInfo->SysInfo.localSharingInfo.isConnectedSharingServer = OFF;
		socketEnable = OFF;
	}
	else
	{
		DEBUG_INFO("Connect success.\n");
		tv.tv_sec = 0;
		tv.tv_usec = 500000;
		setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
		socketEnable = ON;
		cntSocketErr = 0;
		ShmSysConfigAndInfo->SysInfo.localSharingInfo.isConnectedSharingServer = ON;
	}

	while(socketEnable)
	{
		memset(input.buffer, 0, ARRAY_SIZE(input.buffer));
		if((input.size = recv(sockfd, input.buffer, ARRAY_SIZE(input.buffer), 0)) > 0)
		{
			//DEBUG_INFO("Receive size: %d.\n", input.size);
			dM(input.buffer, input.size, YES);

			if(isValidCheckSum(&input))
			{
				switch(input.buffer[2])
				{
					case SHARING_CMD_GET_STATUS:
						output.size = 8;
						output.buffer[0] = 0x55;
						output.buffer[1] = 0x04;
						output.buffer[2] = input.buffer[2];
						output.buffer[3] = ShmSysConfigAndInfo->SysInfo.AcChargingData[0].PilotState;
						output.buffer[4] = ShmSysConfigAndInfo->SysInfo.localSharingInfo.AvailableShargingCurrent;
						output.buffer[5] = ShmSysConfigAndInfo->SysInfo.AcChargingData[0].PresentChargingCurrent;
						output.buffer[6] = ShmSysConfigAndInfo->SysConfig.AcPhaseCount;
						output.buffer[7] = chksumCal(&output);

						break;

					case SHARING_CMD_SET_CAPACITY:
						output.size = 5;
						output.buffer[0] = 0x55;
						output.buffer[1] = 0x01;
						output.buffer[2] = input.buffer[2];
						output.buffer[3] = 0x01;
						output.buffer[4] = chksumCal(&output);
						if(ShmSysConfigAndInfo->SysInfo.localSharingInfo.AvailableShargingCurrent != input.buffer[3])
						{
							ShmSysConfigAndInfo->SysInfo.localSharingInfo.AvailableShargingCurrent = input.buffer[3];
							DEBUG_INFO("Get available current from server: %d\n", ShmSysConfigAndInfo->SysInfo.localSharingInfo.AvailableShargingCurrent);
						}
						break;

					default:
						DEBUG_WARN("Receive unknown command.\n");
						output.size = 4;
						output.buffer[0] = 0x55;
						output.buffer[1] = 0x00;
						output.buffer[2] = SHARING_CMD_UNKNOWN;
						output.buffer[3] = chksumCal(&output);
						break;
				}
			}
			else
			{
				DEBUG_WARN("Receive command check sum error.\n");
				output.size = 4;
				output.buffer[0] = 0x55;
				output.buffer[1] = 0x00;
				output.buffer[2] = SHARING_CMD_CHKSUM_ERROR;
				output.buffer[3] = chksumCal(&output);
			}

			dM(output.buffer, output.size, NO);
			send(sockfd, output.buffer, output.size, 0);
		}
		else if(input.size == 0)
		{
			DEBUG_INFO("DisSocketConnected.\n");
			fflush(stdout);

			socketEnable = OFF;
			ShmSysConfigAndInfo->SysInfo.localSharingInfo.isConnectedSharingServer = OFF;
		}
		else if(input.size == -1)
		{
			if(cntSocketErr > 5)
			{
				socketEnable = OFF;
				DEBUG_ERROR("Socket error occur\n");
			}
			else
			{
				cntSocketErr++;
			}
		}
		usleep(1000000);
	}
	close(sockfd);

	return FAIL;
}

//==========================================
// Local loading balance check
//==========================================
int balance_check_loop(void)
{
	for(;;)
	{
		// Check conn heart beat
		for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
		{
			if(ShmPowerSharing->Connection_Info[idx].isSocketConnected &&
			   ((difftime(time((time_t*)NULL), ShmPowerSharing->Connection_Info[idx].lastHeartBeatTime) > TIMEOUT_SPEC_HEARTBEAT) || (difftime(time((time_t*)NULL), ShmPowerSharing->Connection_Info[idx].lastHeartBeatTime) < 0)))
			{
				DEBUG_INFO("SocketFd-%d heart beat is over %d seconds.\n", ShmPowerSharing->Connection_Info[idx].socketFd, TIMEOUT_SPEC_HEARTBEAT);
				ShmPowerSharing->Connection_Info[idx].isGunConnected = FALSE;
				ShmPowerSharing->Connection_Info[idx].isSocketConnected = FALSE;
			}
		}

		// Check available power
		if(ShmPowerSharing->hasNewConn)
		{
			DEBUG_INFO("Detect gun connected re-allocate available current to each connection.\n");

			for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
			{
				if(ShmPowerSharing->Connection_Info[idx].isSocketConnected &&
				   ShmPowerSharing->Connection_Info[idx].isGunConnected)
				{
					ShmPowerSharing->Connection_Info[idx].availableSharingCurrent = 6;
				}
				else
				{
					ShmPowerSharing->Connection_Info[idx].availableSharingCurrent = 0;
				}
			}

			ShmPowerSharing->hasNewConn = NO;
		}

		for(uint8_t idx=0;idx<CONNECTION_LIMIT;idx++)
		{
			if(ShmPowerSharing->Connection_Info[idx].isSocketConnected &&
			   ShmPowerSharing->Connection_Info[idx].isGunConnected)
			{
				if((difftime(time((time_t*)NULL), ShmPowerSharing->Connection_Info[idx].lastCheckCapacityTime) > TIMEOUT_SPEC_CHECK_CAPACITY) || (difftime(time((time_t*)NULL), ShmPowerSharing->Connection_Info[idx].lastCheckCapacityTime) < 0))
				{
					if(ShmPowerSharing->Connection_Info[idx].availableSharingCurrent >= (ShmPowerSharing->Connection_Info[idx].presentOutputCurrent+2))
					{
						if(ShmPowerSharing->Connection_Info[idx].availableSharingCurrent >= 8)
							ShmPowerSharing->Connection_Info[idx].availableSharingCurrent -= 2;
					}
					else if(((ShmPowerSharing->Connection_Info[idx].presentOutputCurrent-1) < ShmPowerSharing->Connection_Info[idx].availableSharingCurrent) &&
							(ShmPowerSharing->Connection_Info[idx].availableSharingCurrent < (ShmPowerSharing->Connection_Info[idx].presentOutputCurrent+1)) &&
							(conn_getOnHandCurrent() >= 2))
					{
						ShmPowerSharing->Connection_Info[idx].availableSharingCurrent += 2;
					}
					else
					{}
					ShmPowerSharing->Connection_Info[idx].lastCheckCapacityTime = time((time_t*)NULL);
				}
			}
			else
			{
				if(ShmPowerSharing->Connection_Info[idx].availableSharingCurrent != 0)
				{
					DEBUG_INFO("Dupfd-%d on conn_info-%d update sharing current(A): 0\n", ShmPowerSharing->Connection_Info[idx].socketFd, idx);
				}
				ShmPowerSharing->Connection_Info[idx].availableSharingCurrent = 0;
			}
		}

		sleep(1);
	}

	return FAIL;
}

//==========================================
// Main process
//==========================================
int main(void)
{
	signal(SIGCHLD,SIG_IGN);

	// Initial share memory
	if(InitShareMemory() == FAIL)
	{
		DEBUG_ERROR("InitShareMemory NG\n");

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

	// Enable server if rotary switch not slave mode
	if((ShmCharger->gun_info[0].primaryMcuState.rotatory_switch != SWITCH_F_SLAVE) &&
	   (AC_QUANTITY==1?TRUE:(ShmCharger->gun_info[1].primaryMcuState.rotatory_switch != SWITCH_F_SLAVE)))
	{
		// TCP socket server start
		if(fork() == 0)
		{
			if(tcpSocketServerStart() == FAIL)
			{
				DEBUG_ERROR("TCP socket server down.\n");
				return 0;
			}
		}

		// Connection check loop
		if(fork() == 0)
		{
			if(balance_check_loop() == FAIL)
			{
				DEBUG_ERROR("Local loading balance check loop fail.\n");
				return 0;
			}
		}
	}

	sleep(10);
	for(;;)
	{
		// Slave logic
		tcpSocketClientStart();

		usleep(100000);
	}

	return FAIL;
}