/*
 * Module_Cabinet.c
 *
 *  Created on: 2021/10/25
 *      Author: folus
 */

#include "Module_Cabinet.h"

//#define DEBUG
#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

struct SysConfigAndInfo					*ShmSysConfigAndInfo;
struct StatusCodeData 					*ShmStatusCodeData;
struct PrimaryMcuData					*ShmPrimaryMcuData;
struct Charger							*ShmCharger;
struct CABINET							*ShmCabinet;
struct DISPENSER						*ShmDispenser;

/**
 *
 * @param fmt
 * @return
 */
int StoreLogMsgServer(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]CabinetServerLog",
				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 fmt
 * @return
 */
int StoreLogMsgCient(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]CabinetClinetLog",
				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;
}

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

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

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

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

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

	//Create ShmCabinet
	if ((MeterSMId = shmget(ShmCabinetKey, sizeof(struct CABINET), IPC_CREAT | 0777)) < 0)
	{
		DEBUG_SERVER_ERROR("shmget ShmCabinet NG\n");

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

		result = FAIL;
	}
	memset(ShmCabinet,0,sizeof(struct CABINET));

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

    return result;
}

/**
 * Cabinet socket server
 * @param data
 * @param len
 * @param isRX
 */
void dM_Server(uint8_t *data, uint16_t len, uint8_t isRX)
{
#ifdef DEBUG
	uint8_t output[16384];

	if(isRX)
	{
		DEBUG_SERVER_INFO("= RX ===========================================\n");
	}
	else
	{
		DEBUG_SERVER_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_SERVER_INFO("%s\n", output);
	DEBUG_SERVER_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_SERVER_INFO("%s\n", output);
			memset(output, 0x00, ARRAY_SIZE(output));
			sprintf((char*)output, "%s %02X", output, data[idx]);
		}
	}
	DEBUG_SERVER_INFO("%s\n", output);
	DEBUG_SERVER_INFO("------------------------------------------------\n");
#endif
}

/**
 * Cabinet socket server
 * @param data
 * @param len
 * @param isRX
 */
void dM_Client(uint8_t *data, uint16_t len, uint8_t isRX)
{
#ifdef DEBUG
	uint8_t output[16384];

	if(isRX)
	{
		DEBUG_CLIENT_INFO("= RX ===========================================\n");
	}
	else
	{
		DEBUG_CLIENT_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_CLIENT_INFO("%s\n", output);
	DEBUG_CLIENT_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_CLIENT_INFO("%s\n", output);
			memset(output, 0x00, ARRAY_SIZE(output));
			sprintf((char*)output, "%s %02X", output, data[idx]);
		}
	}
	DEBUG_CLIENT_INFO("%s\n", output);
	DEBUG_CLIENT_INFO("------------------------------------------------\n");
#endif
}

/**
 *
 * @param message
 * @return
 */
int isValidCheckSum(struct CABINET_Message *message)
{
	uint8_t chksum = 0x00;

	for(uint16_t idx=0;idx<((((message->buffer[1]<<8) | message->buffer[2])+4)>ARRAY_SIZE(message->buffer)?ARRAY_SIZE(message->buffer):(((message->buffer[1]<<8) | message->buffer[2])+4));idx++)
	{
		chksum ^= message->buffer[idx];
	}

	return ((chksum == message->buffer[((((message->buffer[1]<<8) | message->buffer[2])+4)>ARRAY_SIZE(message->buffer)?ARRAY_SIZE(message->buffer):(((message->buffer[1]<<8) | message->buffer[2])+4))]) ? PASS : FAIL);
}

/**
 *
 * @param message
 * @return
 */
uint8_t chksumCal(struct CABINET_Message *message)
{
	uint8_t chksum=0;

	for(uint16_t idx=0;idx<((((message->buffer[1]<<8) | message->buffer[2])+4)>ARRAY_SIZE(message->buffer)?ARRAY_SIZE(message->buffer):(((message->buffer[1]<<8) | message->buffer[2])+4));idx++)
	{
		chksum ^= message->buffer[idx];
	}

	return chksum & 0xff;
}

/**
 *
 * @return
 */
int conn_getConectedQuantity(void)
{
	int result = 0;

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

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

/**
 *
 * @return
 */
int conn_getDupFd(void)
{
	int result = 0;

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

	return result;
}

/**
 *
 * @param socketFd
 * @return
 */
int conn_register(int socketFd)
{
	int result = FAIL;

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

	return result;
}

/**
 *
 * @param socketFd
 * @return
 */
int conn_reject(int socketFd)
{
	int result = FAIL;

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

	return result;

}

/**
 *
 * @param socketFd
 * @return
 */
int conn_updateHeartBeatTime(int socketFd)
{
	int result = FAIL;

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

	return result;
}

/**
 *
 * @param socketFd
 * @param gun_index
 * @param infoData
 * @return
 */
int conn_updateGunInfo(int socketFd, int gun_index, Gun_Info *infoData)
{
	int result = FAIL;
	Gun_Info gunInfo;

	for(uint8_t idx=0;idx<CABINET_CONNECTION_LIMIT;idx++)
	{
		if(ShmCabinet->Connection_Info[idx].socketFd == socketFd)
		{
			memcpy(&gunInfo, infoData, sizeof(Gun_Info));

			ShmCabinet->Connection_Info[idx].gun_index = gun_index;

			memcpy(&ShmCharger->gun_info[gun_index].primaryMcuLed, &gunInfo.primaryMcuLed, sizeof(Ac_Primary_Mcu_Led));
			memcpy(&ShmCharger->gun_info[gun_index].systemAlarmCode, &gunInfo.systemAlarmCode, sizeof(System_Alarm_Code));
			memcpy(&ShmCharger->gun_info[gun_index].primaryMcuAlarm, &gunInfo.primaryMcuAlarm, sizeof(Ac_Primary_Mcu_Alarm));
			memcpy(&ShmCharger->gun_info[gun_index].GPIO_Input, &gunInfo.GPIO_Input, sizeof(Gpio_in));
			memcpy(&ShmCharger->gun_info[gun_index].powerConsumptionTotal, &gunInfo.powerConsumptionTotal, sizeof(Power_Consumption));
			memcpy(&ShmCharger->gun_info[gun_index].powerConsumption[0], &gunInfo.powerConsumption[0], ARRAY_SIZE(gunInfo.powerConsumption)*sizeof(Power_Consumption));
			memcpy(&ShmCharger->gun_info[gun_index].inputVoltage, &gunInfo.inputVoltage, sizeof(PresentInputVoltage));
			memcpy(&ShmCharger->gun_info[gun_index].outputCurrent, &gunInfo.outputCurrent, sizeof(Presentoutputcurrent));
			memcpy(&ShmCharger->gun_info[gun_index].gunPluginTimes, &gunInfo.gunPluginTimes, sizeof(Gun_Plugin_Times));
			memcpy(&ShmCharger->gun_info[gun_index].temperature, &gunInfo.temperature, sizeof(Temperature));
			memcpy(&ShmCharger->gun_info[gun_index].rtc, &gunInfo.rtc, sizeof(Rtc));
			memcpy(&ShmCharger->gun_info[gun_index].acCcsInfo, &gunInfo.acCcsInfo, sizeof(Ac_Ccs_Info));
			memcpy(&ShmCharger->gun_info[gun_index].primaryMcuState.socket_e, &gunInfo.primaryMcuState.socket_e, sizeof(Socket_E));

			ShmCharger->gun_info[gun_index].primaryMcuState.cp_state = gunInfo.primaryMcuState.cp_state;
			ShmCharger->gun_info[gun_index].primaryMcuState.cp_voltage_negtive = gunInfo.primaryMcuState.cp_voltage_negtive;
			ShmCharger->gun_info[gun_index].primaryMcuState.cp_voltage_positive = gunInfo.primaryMcuState.cp_voltage_positive;
			ShmCharger->gun_info[gun_index].primaryMcuState.current_limit = gunInfo.primaryMcuState.current_limit;
			ShmCharger->gun_info[gun_index].primaryMcuState.locker_state = gunInfo.primaryMcuState.locker_state;
			ShmCharger->gun_info[gun_index].primaryMcuState.meter_state = gunInfo.primaryMcuState.meter_state;
			ShmCharger->gun_info[gun_index].primaryMcuState.pp_state = gunInfo.primaryMcuState.pp_state;
			ShmCharger->gun_info[gun_index].primaryMcuState.rating_current = gunInfo.primaryMcuState.rating_current;
			ShmCharger->gun_info[gun_index].primaryMcuState.relay_state = gunInfo.primaryMcuState.relay_state;
			ShmCharger->gun_info[gun_index].primaryMcuState.rotatory_switch = gunInfo.primaryMcuState.rotatory_switch;
			ShmCharger->gun_info[gun_index].primaryMcuState.shutter_state = gunInfo.primaryMcuState.shutter_state;

			ShmCharger->gun_info[gun_index].mcuFlag.isReadFwVerPass = gunInfo.mcuFlag.isReadFwVerPass;
			ShmCharger->gun_info[gun_index].mcuFlag.isReadMeterIcCorrectionStatus = gunInfo.mcuFlag.isReadMeterIcCorrectionStatus;
			ShmCharger->gun_info[gun_index].mcuFlag.isSetModelNamePass = gunInfo.mcuFlag.isSetModelNamePass;
			ShmCharger->gun_info[gun_index].mcuFlag.isSetSerialNumberPass = gunInfo.mcuFlag.isSetSerialNumberPass;
			ShmCharger->gun_info[gun_index].ccsHandshakeState = gunInfo.ccsHandshakeState;
			ShmCharger->gun_info[gun_index].isUpgradeEnd = gunInfo.isUpgradeEnd;
			ShmCharger->gun_info[gun_index].isUpgradePASS = gunInfo.isUpgradePASS;
			ShmCharger->gun_info[gun_index].isGunPlugged = gunInfo.isGunPlugged;
			memcpy(&ShmCharger->gun_info[gun_index].ver, &gunInfo.ver, sizeof(Ver));


			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PilotState = gunInfo.primaryMcuState.cp_state;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PilotDuty = (gunInfo.primaryMcuState.current_limit>51?(unsigned char)((gunInfo.primaryMcuState.current_limit/2.5)+64):(unsigned char)(gunInfo.primaryMcuState.current_limit/0.6));
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PilotVoltage = gunInfo.primaryMcuState.cp_voltage_positive;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].RelayK1K2Status = gunInfo.primaryMcuState.relay_state;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingVoltage = (float)gunInfo.inputVoltage.L1N_L12;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingVoltageL2 = (float)gunInfo.inputVoltage.L2N_L23;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingVoltageL3 = (float)gunInfo.inputVoltage.L3N_L31;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingCurrent = (float)gunInfo.outputCurrent.L1N_L12[0];
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingCurrentL2 = (float)gunInfo.outputCurrent.L2N_L23[0];
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingCurrentL3 = (float)gunInfo.outputCurrent.L3N_L31[0];
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].ConnectorTemp = gunInfo.temperature.point[0];

			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedVaGain = gunInfo.meterIcCorrectionStatus.bits.isCalibratedVaGain;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedVbGain = gunInfo.meterIcCorrectionStatus.bits.isCalibratedVbGain;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedVcGain = gunInfo.meterIcCorrectionStatus.bits.isCalibratedVcGain;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedVaOffset = gunInfo.meterIcCorrectionStatus.bits.isCalibratedVaOffset;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedVbOffset = gunInfo.meterIcCorrectionStatus.bits.isCalibratedVbOffset;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedVcOffset = gunInfo.meterIcCorrectionStatus.bits.isCalibratedVcOffset;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedCaGain = gunInfo.meterIcCorrectionStatus.bits.isCalibratedCaGain;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedCbGain = gunInfo.meterIcCorrectionStatus.bits.isCalibratedCbGain;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedCcGain = gunInfo.meterIcCorrectionStatus.bits.isCalibratedCcGain;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedCaOffset = gunInfo.meterIcCorrectionStatus.bits.isCalibratedCaOffset;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedCbOffset = gunInfo.meterIcCorrectionStatus.bits.isCalibratedCbOffset;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedCcOffset = gunInfo.meterIcCorrectionStatus.bits.isCalibratedCcOffset;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedPa = gunInfo.meterIcCorrectionStatus.bits.isCalibratedPa;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedPb = gunInfo.meterIcCorrectionStatus.bits.isCalibratedPb;
			ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].meterIcCalInfo.isCalibratedPc = gunInfo.meterIcCorrectionStatus.bits.isCalibratedPc;

			result = PASS;
			break;
		}
	}

	return result;
}

/**
 *
 * @param socketFd
 * @return
 */
int conn_getGunIndex(int socketFd)
{
	int result = -1;

	for(uint8_t idx=0;idx<CABINET_CONNECTION_LIMIT;idx++)
	{
		if(ShmCabinet->Connection_Info[idx].socketFd == socketFd)
		{
			result = ShmCabinet->Connection_Info[idx].gun_index;
		}
	}

	return result;
}

/**
 *
 * @param out
 */
void create_cmd_getChargingData(struct CABINET_Message *out)
{
	memset(out->buffer, 0, ARRAY_SIZE(out->buffer));

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

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

/**
 *
 * @param out
 */
void create_cmd_setConfigData(struct CABINET_Message *out, int socketFd)
{
	memset(out->buffer, 0, ARRAY_SIZE(out->buffer));

	out->size = (5 + sizeof(struct SysConfigData));
	out->buffer[0] = 0x55;
	out->buffer[1] = (sizeof(struct SysConfigData) >> 8) & 0xff;
	out->buffer[2] = (sizeof(struct SysConfigData) >> 0) & 0xff;
	out->buffer[3] = CABINET_CMD_SET_CONFIG_DATA;

	for(uint8_t idx=0;idx<CABINET_CONNECTION_LIMIT;idx++)
	{
		if(ShmCabinet->Connection_Info[idx].socketFd == socketFd)
		{
			memcpy(&out->buffer[4], &ShmSysConfigAndInfo->SysConfig, sizeof(struct SysConfigData));
			break;
		}
	}
	out->buffer[out->size-1] = chksumCal(out);

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

/**
 *
 * @param out
 */
void create_cmd_setGunInfo(struct CABINET_Message *out, int socketFd)
{
	memset(out->buffer, 0, ARRAY_SIZE(out->buffer));

	out->size = (5 + sizeof(Gun_Info) + sizeof(Timeout_Spec));
	out->buffer[0] = 0x55;
	out->buffer[1] = ((sizeof(Gun_Info) + sizeof(Timeout_Spec)) >> 8) & 0xff;
	out->buffer[2] = ((sizeof(Gun_Info) + sizeof(Timeout_Spec)) >> 0) & 0xff;
	out->buffer[3] = CABINET_CMD_SET_GUNINFO;

	for(uint8_t idx=0;idx<CABINET_CONNECTION_LIMIT;idx++)
	{
		if(ShmCabinet->Connection_Info[idx].socketFd == socketFd)
		{
			memcpy(&out->buffer[4], &ShmCharger->gun_info[ShmCabinet->Connection_Info[idx].gun_index], sizeof(Gun_Info));
			memcpy(&out->buffer[4+sizeof(Gun_Info)], &ShmCharger->timeoutSpec, sizeof(Timeout_Spec));
			break;
		}
	}

	out->buffer[out->size-1] = chksumCal(out);

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

/**
 *
 * @return
 */
int tcpSocketServerStart(void)
{
	int 						sockFd = 0;
	int 						clientSockFd = 0;
	int							dupFd = 0;
	struct CABINET_Message		input;
	struct CABINET_Message		output;
	struct sockaddr_in 			serverInfo, clientInfo;
	socklen_t 					addrlen = sizeof(clientInfo);

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

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

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

	if(listen(sockFd, CABINET_CONNECTION_LIMIT) < 0)
		DEBUG_SERVER_ERROR("TCP cabinet server socket listen fail.\n");
	else
		DEBUG_SERVER_INFO("TCP cabinet server listen on port %d.\n", CABINET_LISTEN_PORT_TCP);

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

		if(clientSockFd > 0)
		{
			if(conn_getConectedQuantity() < CABINET_CONNECTION_LIMIT)
			{
				// Fork a child process to handle the new conn
				if(fork()==0)
				{
					uint8_t idxStep = 0;
					uint8_t socketEnable = YES;
					struct FLAG
					{
						uint8_t		isConfigured:1;
					}flag;
					struct timeval	tv;
					tv.tv_sec = 0;
					tv.tv_usec = TIMEOUT_SOCKET_RX;
					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_Server(input.buffer, input.size, YES);

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

								memset(output.buffer, 0x00, ARRAY_SIZE(output.buffer));
								switch(input.buffer[3])
								{
									case CABINET_CMD_GET_CHARGING_DATA:
										conn_updateGunInfo(dupFd, input.buffer[4], (Gun_Info*)&input.buffer[5]);
										//DEBUG_SERVER_INFO("Get charging data response.\n");
										break;

									case CABINET_CMD_SET_CONFIG_DATA:
										if(input.buffer[4])
										{
											flag.isConfigured = ON;
										}
										else
											DEBUG_SERVER_INFO("Get set configuration response: %d.\n", input.buffer[4]);
										break;

									case CABINET_CMD_SET_GUNINFO:
										if(input.buffer[4])
										{

										}
										else
											DEBUG_SERVER_INFO("Get set gun info response: %d.\n", input.buffer[4]);
										break;

									default:
										DEBUG_SERVER_WARN("Receive unknown response.\n");
										break;
								}
							}
							else
							{
								DEBUG_SERVER_WARN("Receive response check sum error.\n");
							}
						}
						else if(input.size == 0)
						{
							DEBUG_SERVER_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:
									create_cmd_getChargingData(&output);
									send(clientSockFd, output.buffer, output.size, 0);
									idxStep++;
									break;

								default:
									if(!flag.isConfigured)
									{
										create_cmd_setConfigData(&output, dupFd);
										send(clientSockFd, output.buffer, output.size, 0);
									}
									else
									{
										create_cmd_setGunInfo(&output, dupFd);
										send(clientSockFd, output.buffer, output.size, 0);

										ShmCharger->gun_info[conn_getGunIndex(dupFd)].mcuResetRequest.isMcuResetRequest = OFF;
									}
									idxStep = 0;

									break;
							}
						}
					}

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

		sleep(1);
	}

	return FAIL;
}

/**
 * Cabinet socket client
 * @return
 */
int tcpSocketClientStart(void)
{
	int 						sockfd;
	struct sockaddr_in 			info;
	struct hostent 				*ghbn;
	struct timeval 				tv;
	uint8_t 					socketEnable;
	uint8_t						cntSocketErr;

	struct CABINET_Message		input;
	struct CABINET_Message		output;

	bzero(&info,sizeof(info));
	ghbn = gethostbyname((char*)"192.168.201.201");
	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(CABINET_LISTEN_PORT_TCP);
	ShmSysConfigAndInfo->SysInfo.localSharingInfo.isConnectedSharingServer = OFF;
	DEBUG_CLIENT_INFO("Connect to %s:%d\n", inet_ntoa(*(struct in_addr *)ghbn->h_addr_list[0]), CABINET_LISTEN_PORT_TCP);

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

	if(connect(sockfd, (struct sockaddr *)&info,sizeof(info)) ==-1)
	{
		DEBUG_CLIENT_ERROR("Connection error.\n");
		ShmSysConfigAndInfo->SysInfo.localSharingInfo.isConnectedSharingServer = OFF;
		socketEnable = OFF;
	}
	else
	{
		DEBUG_CLIENT_INFO("Connect success.\n");
		tv.tv_sec = 0;
		tv.tv_usec = TIMEOUT_SOCKET_RX;
		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)
		{
			dM_Client(input.buffer, input.size, YES);

			if(isValidCheckSum(&input))
			{
				switch(input.buffer[3])
				{
					case CABINET_CMD_GET_CHARGING_DATA:
						output.size = 6 + sizeof(Gun_Info);
						output.buffer[0] = 0x55;
						output.buffer[1] = ((sizeof(Gun_Info)+1) >> 8) & 0xff;
						output.buffer[2] = ((sizeof(Gun_Info)+1) >> 0) & 0xff;
						output.buffer[3] = input.buffer[3];
						output.buffer[4] = 0; // TODO: gun_index assign
						memcpy(&output.buffer[5], &ShmDispenser->gun_info, sizeof(Gun_Info));
						output.buffer[output.size-1] = chksumCal(&output);
						break;

					case CABINET_CMD_SET_CONFIG_DATA:
						output.size = 6;
						output.buffer[0] = 0x55;
						output.buffer[1] = 0x00;
						output.buffer[2] = 0x01;
						output.buffer[3] = input.buffer[3];
						output.buffer[4] = 0x01;
						output.buffer[output.size-1] = chksumCal(&output);
						memcpy(&ShmDispenser->ConfigData, &input.buffer[4], sizeof(struct SysConfigData));
						ShmDispenser->isCcsEnable = OFF;
						for(uint8_t idx=0;idx<3;idx++)
						{
							if(ShmDispenser->ConfigData.ModelName[7+idx] == '7')
								ShmDispenser->isCcsEnable = ON;
						}
						break;

					case CABINET_CMD_SET_GUNINFO:
						output.size = 6;
						output.buffer[0] = 0x55;
						output.buffer[1] = 0x00;
						output.buffer[2] = 0x01;
						output.buffer[3] = input.buffer[3];
						output.buffer[4] = 0x01;
						output.buffer[output.size-1] = chksumCal(&output);

						Gun_Info rxGunInfo;
						memcpy(&rxGunInfo, &input.buffer[4], sizeof(Gun_Info));
						/*
						 *	TODO:
						 *		1. Variable set to share memory
						 */
						memcpy(&ShmDispenser->timeoutSpec, &input.buffer[4+sizeof(Gun_Info)], sizeof(Timeout_Spec));

						memcpy(&ShmDispenser->gun_info.primaryMcuState.relayState, &rxGunInfo.primaryMcuState.relayState, sizeof(Relay));
						ShmDispenser->gun_info.mcuResetRequest.isMcuResetRequest = rxGunInfo.mcuResetRequest.isMcuResetRequest;
						ShmDispenser->gun_info.primaryMcuLed.mode = rxGunInfo.primaryMcuLed.mode;
						ShmDispenser->gun_info.isOperactive = rxGunInfo.isOperactive;
						ShmDispenser->gun_info.legacyRequest.isLegacyRequest = rxGunInfo.legacyRequest.isLegacyRequest;
						ShmDispenser->gun_info.legacyRequest.isRelayOn = rxGunInfo.legacyRequest.isRelayOn;
						ShmDispenser->gun_info.targetCurrent = rxGunInfo.targetCurrent;
						ShmDispenser->gun_info.isResetSoftReq = rxGunInfo.isResetSoftReq;
						ShmDispenser->gun_info.isResetHardReq = rxGunInfo.isResetHardReq;
						ShmDispenser->gun_info.isUpgradeReq = rxGunInfo.isUpgradeReq;

						break;

					default:
						DEBUG_CLIENT_WARN("Receive unknown command.\n");
						output.size = 5;
						output.buffer[0] = 0x55;
						output.buffer[1] = 0x00;
						output.buffer[2] = 0x00;
						output.buffer[3] = CABINET_CMD_UNKNOWN;
						output.buffer[output.size-1] = chksumCal(&output);
						break;
				}
			}
			else
			{
				DEBUG_CLIENT_WARN("Receive command check sum error.\n");
				output.size = 5;
				output.buffer[0] = 0x55;
				output.buffer[1] = 0x00;
				output.buffer[2] = 0x00;
				output.buffer[3] = CABINET_CMD_CHKSUM_ERROR;
				output.buffer[output.size-1] = chksumCal(&output);
			}

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

			socketEnable = OFF;
		}
		else if(input.size == -1)
		{
			if(cntSocketErr > 5)
			{
				socketEnable = OFF;
				DEBUG_CLIENT_ERROR("Socket error occur.\n");
			}
			else
			{
				cntSocketErr++;
			}
		}
		usleep(100000);
	}
	close(sockfd);

	return FAIL;
}


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

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

	/*
	 * 	TODO:
	 * 		1. Rotary switch define change with system specification
	 */
	for(uint8_t idx=0;idx<10;idx++)
	{
		if(ShmDispenser->gun_info.primaryMcuState.cp_state == CP_STATE_UNKNOWN)
			break;

		DEBUG_SERVER_INFO("Wait CP state query...%d\n", idx);
		sleep(5);
	};

	if(ShmDispenser->gun_info.primaryMcuState.rotatory_switch == ROTARY_SWITCH_E_DISPENSER)
	{
		system("/sbin/ifconfig eth0:1 192.168.201.202 netmask 255.255.255.248 up &");
	}
	else
	{
		system("/sbin/ifconfig eth0:1 192.168.201.201 netmask 255.255.255.248 up &");
	}

	DEBUG_SERVER_INFO("Module_Cabinet initialized...\n");
	if(ShmDispenser->gun_info.primaryMcuState.rotatory_switch != ROTARY_SWITCH_E_DISPENSER)
	{
		// TCP socket server start
		if(fork() == 0)
		{
			if(tcpSocketServerStart() == FAIL)
			{
				DEBUG_SERVER_ERROR("TCP socket server down.\n");
				return 0;
			}
		}
		sleep(3);
	}

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

		usleep(100000);
	}

	return -1;
}