/*
 * Module_4g.c
 *
 *  Created on: 2019年11月29日
 *      Author: Eason Yang
 */

#include    <sys/types.h>
#include    <sys/stat.h>
#include 	<sys/time.h>
#include 	<sys/timeb.h>
#include 	<sys/ipc.h>
#include 	<sys/shm.h>
#include 	<sys/mman.h>

#include 	<unistd.h>
#include 	<stdarg.h>
#include    <stdio.h>		/*標準輸入輸出定義*/
#include    <stdlib.h>		/*標準函數庫定義*/
#include    <unistd.h>		/*Unix 標準函數定義*/
#include    <fcntl.h>		/*檔控制定義*/
#include    <termios.h>		/*PPSIX 終端控制定義*/
#include    <errno.h>		/*錯誤號定義*/
#include 	<errno.h>
#include 	<string.h>
#include	<time.h>
#include	<ctype.h>
#include	"define.h"

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

#define ARRAY_SIZE(A)		(sizeof(A) / sizeof(A[0]))
#define PASS	1
#define FAIL	-1
#define DONGLE_QUECTEL	1
#define DONGLE_UBLOX	2

// Define Module mode
#define NO_SERVICE		0
#define CDMA			1
#define GMS_GPRS		2
#define WCDMA			3
#define GMS_WCDMA		4
#define TD_SCDMA		5
#define UNKNOW			6

// Define interval
#define SystemInterval				30	// Seconds
#define CheckModemInterval			30	// Seconds
#define CheckSimInterval			30 	// Seconds
#define CheckModemInfoInterval		30	// Seconds
#define CheckConnectionInterval		30 	// Seconds
#define CheckInternetInterval		30	// Seconds
#define DisconnInterval				60 	// Seconds

int Check4GModem(void);
int isPppUp(void);
int isReadInfo(void);
int isReadSimInfo(void);
int isReachableInternet(void);
int isModuleUnbind(void);
int isModuleBind(void);
int rstModule(void);
int Load4gConfiguration(void);
int CheckSignalRssi(void);
int at_command(int uart, char* cmd, char* rx);
int openPort(char *tty);
int set_interface_attribs (int fd, int speed, int parity);
int set_blocking (int fd, int should_block);
void trim_s(char *s, unsigned char len);
void substr(char *dest, const char* src, unsigned int start, unsigned int cnt);

char *portName[3] 			= {"/dev/ttyUSB2", "/dev/ttyUSB2", "/dev/ttyACM2"};
char *valid_Internet[2] 	= {"8.8.8.8", "www.baidu.com"};
pid_t	pid;

struct dongle_info
{
	int Model;
	char ICCID[20];
	char IMSI[16];
	char IMEI[16];
	char MANUFACTURER[8];
	char MODELNAME[10];
	char REVISION[18];
	unsigned char MODE;
	int CSQ;
	int cnt_InternetFail;
	int cnt_ReadInfoFail;
	int cnt_pppFail;
	int cnt_SearchModuleFail;
	int cnt_ReadSimInfoFail;
}Dongle;

struct SysConfigAndInfo			*ShmSysConfigAndInfo;
struct StatusCodeData 			*ShmStatusCodeData;
struct FanModuleData			*ShmFanModuleData;

int StoreLogMsg(const char *fmt, ...)
{
	char Buf[4096+256];
	char buffer[4096];
	time_t CurrentTime;
	struct tm *tm;
	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);
	sprintf(Buf,"echo -n \"[%04d.%02d.%02d %02d:%02d:%02d] - %s\" >> /Storage/SystemLog/[%04d.%02d]4g_SystemLog",
			tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,
			buffer,
			tm->tm_year+1900,tm->tm_mon+1);
#ifdef SystemLogMessage
	system(Buf);
#endif

#ifdef ConsloePrintLog
	printf("[%04d.%02d.%02d %02d:%02d:%02d] - %s", tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec, 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;
}

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

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

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

    return result;
}

//==========================================
// Check dongle model
//==========================================
int Check4GModem(void)
{
    int result = FAIL;

    if((access("/dev/ttyUSB0", F_OK) != -1) &&
       (access("/dev/ttyUSB1", F_OK) != -1) &&
	   (access("/dev/ttyUSB2", F_OK) != -1) &&
	   (access("/dev/ttyUSB3", F_OK) != -1))
    {
    	result = DONGLE_QUECTEL;
    }
    else if((access("/dev/ttyACM0", F_OK) != -1) &&
    		(access("/dev/ttyACM1", F_OK) != -1) &&
        	(access("/dev/ttyACM2", F_OK) != -1) &&
			(access("/dev/ttyACM3", F_OK) != -1) &&
			(access("/dev/ttyACM4", F_OK) != -1) &&
			(access("/dev/ttyACM5", F_OK) != -1))
    {
    	result = DONGLE_UBLOX;
    }
    else
    {}

    if(result == DONGLE_QUECTEL)
    {
		#ifdef SystemLogMessage
    	DEBUG_WARN("Quectel 4G modem be found\n");
		#endif
    }
    else if(result == DONGLE_UBLOX)
    {
		#ifdef SystemLogMessage
    	DEBUG_WARN("Ublox 4G modem be found\n");
		#endif
    }
    else
    {
		#ifdef SystemLogMessage
    	DEBUG_WARN("No 4G modem be found\n");
		#endif
    }

    return result;
}

//==========================================
// Check ppp interface status
//==========================================
int isPppUp(void)
{
	int result = FAIL;

	FILE *fp;
	char cmd[256];
	char buf[512];
	char tmp[512];

	strcpy(cmd, "ifconfig");;
	fp = popen(cmd, "r");
	if(fp != NULL)
	{
		while(fgets(buf, sizeof(buf), fp) != NULL)
		{
			if(strstr(buf, "ppp") > 0)
			{
				result = PASS;
			}

			if(strstr(buf, "addr:") > 0)
			{
				sscanf(buf, "%*s%s", tmp);
				substr((char*)ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomIpAddress, tmp, strspn(tmp, "addr:"), strlen(buf)-strspn(tmp, "addr:"));
			}
		}
	}
	pclose(fp);

	return result;
}

//==========================================
// Check dongle info read status
//==========================================
int isReadInfo(void)
{
	int result = PASS;
	int uart;
	char rx[512];
	char tmp[512];
	char Lenght;
	int modeStatus;

	if((uart = openPort(portName[Dongle.Model])) != FAIL)
	{
		switch(Dongle.Model)
		{
			case DONGLE_QUECTEL:
				default:
				//==============================
				// Set don't echo command
				//==============================
				if(at_command(uart, "ate0\r", rx) <= 0)
					result = FAIL;

				//==============================
				// Read Manufacturer
				//==============================
				Lenght = at_command(uart, "at+gmi\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx,"OK"));
					memset(rx, 0, sizeof rx);
					trim_s(tmp,Lenght);
					strcpy(Dongle.MANUFACTURER, tmp);
				}
				else
					result = FAIL;

				//==============================
				// Read Model
				//==============================
				Lenght = at_command(uart, "at+gmm\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx,"OK"));
					memset(rx, 0, sizeof rx);
					trim_s(tmp,Lenght);
					strncpy(Dongle.MODELNAME, tmp, strlen(tmp));
				}
				else
					result = FAIL;

				//==============================
				// Read Revision
				//==============================
				Lenght = at_command(uart, "at+gmr\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx, "OK"));
					memset(rx, 0, sizeof rx);
					trim_s(tmp,Lenght);
					strncpy(Dongle.REVISION, tmp, strlen(tmp));
				}
				else
					result = FAIL;

				//==============================
				// Read IMEI
				//==============================
				Lenght = at_command(uart, "at+gsn\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx, "OK"));
					memset(rx, 0, sizeof rx);
					trim_s(tmp,Lenght);
					strncpy(Dongle.IMEI, tmp, strlen(tmp));
				}
				else
					result = FAIL;

				//==============================
				// Read CSQ
				//==============================
				Lenght = at_command(uart, "at+csq\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx,","));
					strncpy(rx, tmp + strcspn(tmp,":")+1, 10);
					Dongle.CSQ = atoi(rx);
				}
				else
					result = FAIL;

				//==============================
				// Read Mode
				//==============================
				Lenght = at_command(uart, "at+qcfg= \"nwscanmode\"\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx,"OK"));
					strncpy(rx, tmp + strcspn(tmp,",")+1, 1);

					modeStatus = atoi(rx);

					if(modeStatus == 0)
						Dongle.MODE = UNKNOW;
					else if(modeStatus == 1)
						Dongle.MODE = GMS_GPRS;
					else if(modeStatus == 2)
						Dongle.MODE = WCDMA;
					else if(modeStatus == 3)
						Dongle.MODE = UNKNOW;
					else if(modeStatus == 4)
						Dongle.MODE = TD_SCDMA;
					else if(modeStatus == 5)
						Dongle.MODE = UNKNOW;
					else if(modeStatus == 6)
						Dongle.MODE = CDMA;
					else
						Dongle.MODE = UNKNOW;
				}
				else
					result = FAIL;

				break;
			case DONGLE_UBLOX:
				//==============================
				// Set don't echo command
				//==============================
				if(at_command(uart, "ate0\r", rx) <= 0)
					result = FAIL;

				//==============================
				// Read Manufacturer
				//==============================
				Lenght = at_command(uart, "at+cgmi\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx,"OK"));
					memset(rx, 0, sizeof rx);
					strncpy(Dongle.MANUFACTURER, tmp+2, 6);
				}
				else
					result = FAIL;

				//==============================
				// Read Model
				//==============================
				Lenght = at_command(uart, "at+cgmm\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx,"OK"));
					memset(rx, 0, sizeof rx);
					strncpy(Dongle.MODELNAME , tmp+2, 9);
				}
				else
					result = FAIL;

				//==============================
				// Read Revision
				//==============================
				Lenght = at_command(uart, "at+cgmr\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx, "OK"));
					memset(rx, 0, sizeof rx);
					strncpy(Dongle.REVISION, tmp+2, 5);
				}
				else
					result = FAIL;

				//==============================
				// Read IMEI
				//==============================
				Lenght = at_command(uart, "at+cgsn\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx, "OK"));
					memset(rx, 0, sizeof rx);
					trim_s(tmp,Lenght);
					strncpy(Dongle.IMEI, tmp, strlen(tmp));
				}
				else
					result = FAIL;

				//==============================
				// Read CSQ
				//==============================
				Lenght = at_command(uart, "at+csq\r", rx);

				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx,","));
					strncpy(rx, tmp + strcspn(tmp,":")+1, 10);
					Dongle.CSQ = atoi(rx);
				}
				else
					result = FAIL;

				break;
		}
	}
	else
	{
		#ifdef SystemLogMessage
		DEBUG_ERROR("%s open fail.\n", portName[Dongle.Model]);
		#endif
		result = FAIL;
	}

	close(uart);

	return result;
}

//==========================================
// Read sim card information
//==========================================
int isReadSimInfo(void)
{
	int result = PASS;
	int uart;
	char rx[512];
	char tmp[512];
	char Lenght;

	if((uart = openPort(portName[Dongle.Model])) != FAIL)
	{
		switch(Dongle.Model)
		{
			case DONGLE_QUECTEL:
				default:
				//==============================
				// Set don't echo command
				//==============================
				if(at_command(uart, "ate0\r", rx) <= 0)
					result = FAIL;

				//==============================
				// Read IMSI
				//==============================
				Lenght = at_command(uart, "at+cimi\r", rx);
				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);

					if (strstr(rx, "ERROR"))
					{
						memset(Dongle.IMSI, 0, sizeof Dongle.IMSI);
						result = FAIL;
					}
					else
					{
						memcpy(tmp, rx, strcspn(rx, "OK"));
						trim_s(tmp, Lenght);
						memset(rx, 0, sizeof rx);
						strncpy(Dongle.IMSI, tmp, strlen(tmp));
					}
				}
				else
					result = FAIL;

				//==============================
				// Read CCID
				//==============================
				Lenght = at_command(uart, "at+qccid\r", rx);
				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);

					if (strstr(rx, "ERROR"))
					{
						memset(Dongle.ICCID, 0, sizeof Dongle.ICCID);
						result = FAIL;
					}
					else
					{
						memcpy(tmp, rx, strcspn(rx, "OK"));
						memset(rx, 0, sizeof rx);
						strncpy(Dongle.ICCID, tmp + strcspn(tmp, ":") + 2, 20);
					}
				}
				else
					result = FAIL;

				break;
			case DONGLE_UBLOX:
				//==============================
				// Set don't echo command
				//==============================
				if (at_command(uart, "ate0\r", rx) <= 0)
					result = FAIL;

				//==============================
				// Read IMSI
				//==============================
				Lenght = at_command(uart, "at+cimi\r", rx);
				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);

					if(strstr(rx, "ERROR"))
					{
						memset(Dongle.IMSI, 0, sizeof Dongle.IMSI);
						result = FAIL;
					}
					else
					{
						memcpy(tmp, rx, strcspn(rx,"OK"));
						memset(rx, 0, sizeof rx);
						trim_s(tmp,Lenght);
						strncpy(Dongle.IMSI , tmp, strlen(tmp));
					}
				}
				else
					result = FAIL;

				//==============================
				// Read CCID
				//==============================
				Lenght = at_command(uart, "at+ccid\r", rx);
				if(Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);

					if (strstr(rx, "ERROR"))
					{
						memset(Dongle.ICCID, 0, sizeof Dongle.ICCID);
						result = FAIL;
					}
					else
					{
						memcpy(tmp, rx, strcspn(rx, "OK"));
						memset(rx, 0, sizeof rx);
						strncpy(Dongle.ICCID, tmp + strcspn(tmp, ":") + 2, 20);
					}
				}
				else
					result = FAIL;

				break;
		}
	}
	else
	{
		#ifdef SystemLogMessage
		DEBUG_ERROR("%s open fail.\n", portName[Dongle.Model]);
		#endif
		result = FAIL;
	}

	close(uart);

	return result;
}

//==========================================
// Read signal information
//==========================================
int CheckSignalRssi(void)
{
	int result = PASS;
	int uart;
	char rx[512];
	char tmp[512];
	char Lenght;

	if((uart = openPort(portName[Dongle.Model])) != FAIL)
	{
		switch (Dongle.Model)
		{
			case DONGLE_UBLOX:
				//==============================
				// Set don't echo command
				//==============================
				if (at_command(uart, "ate0\r", rx) <= 0)
					result = FAIL;

				//==============================
				// Read CSQ
				//==============================
				Lenght = at_command(uart, "at+csq\r", rx);

				if (Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx, ","));
					strncpy(rx, tmp + strcspn(tmp, ":") + 1, 10);
					Dongle.CSQ = atoi(rx);
				}
				else
					result = FAIL;

				break;

			case DONGLE_QUECTEL:
			default:
				//==============================
				// Set don't echo command
				//==============================
				if (at_command(uart, "ate0\r", rx) <= 0)
					result = FAIL;

				//==============================
				// Read CSQ
				//==============================
				Lenght = at_command(uart, "at+csq\r", rx);

				if (Lenght > 0)
				{
					memset(tmp, 0, sizeof tmp);
					memcpy(tmp, rx, strcspn(rx, ","));
					strncpy(rx, tmp + strcspn(tmp, ":") + 1, 10);
					Dongle.CSQ = atoi(rx);
				}
				else
					result = FAIL;

				break;
		}
	}
	else
	{
		#ifdef SystemLogMessage
		DEBUG_ERROR("%s open fail.\n", portName[Dongle.Model]);
		#endif
		result = FAIL;
	}

	close(uart);

	return result;
}

//==========================================
// Init 4G dongle configuration
//==========================================
int  Load4gConfiguration()
{
	int result = FAIL;
	unsigned char CopyTmp[1024];

	if(strlen((char*)ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomApn) > 0)
	{
		system("cat /dev/null > /root/ppp/auto-apn.conf");
		system("chmod 600 /root/ppp/auto-apn.conf");
		system("cat /dev/null > /etc/ppp/pap-secrets");
		system("chmod 600 /etc/ppp/pap-secrets");
		system("cat /dev/null > /etc/ppp/chap-secrets");
		system("chmod 600 /etc/ppp/chap-secrets");
		system("cat /dev/null > /etc/ppp/auth");
		system("chmod 600 /etc/ppp/auth");
		memset(CopyTmp,0,sizeof(CopyTmp));
		sprintf((char*)CopyTmp,"echo APN=\"%s\" > /root/ppp/auto-apn.conf",ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomApn);
		system((char*)CopyTmp);
		memset(CopyTmp,0,sizeof(CopyTmp));
		sprintf((char*)CopyTmp,"echo ACCOUNT=\"%s\" >> /root/ppp/auto-apn.conf",ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapId);
		system((char*)CopyTmp);
		memset(CopyTmp,0,sizeof(CopyTmp));
		sprintf((char*)CopyTmp,"echo PASSWORD=\"%s\" >> /root/ppp/auto-apn.conf",ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapPwd);
		system((char*)CopyTmp);
		memset(CopyTmp,0,sizeof(CopyTmp));
		sprintf((char*)CopyTmp,"echo %s > /etc/ppp/auth",ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapId);
		system((char*)CopyTmp);
		sprintf((char*)CopyTmp,"echo %s >> /etc/ppp/auth",ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapPwd);
		system((char*)CopyTmp);

		if(strlen((char*)ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapId)>0)
		{
			memset(CopyTmp,0,sizeof(CopyTmp));
			if(strlen((char*)ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapPwd)>0)
				sprintf((char*)CopyTmp,"echo \"%s * %s \" > /etc/ppp/pap-secrets",	ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapId, ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapPwd);
			else
				sprintf((char*)CopyTmp,"echo \"%s *  \\<Your\\ Password\\> \" > /etc/ppp/pap-secrets",	ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapId);
			system((char*)CopyTmp);

			memset(CopyTmp,0,sizeof(CopyTmp));
			if(strlen((char*)ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapPwd)>0)
				sprintf((char*)CopyTmp,"echo \"%s * %s \" > /etc/ppp/chap-secrets",	ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapId, ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapPwd);
			else
				sprintf((char*)CopyTmp,"echo \"%s *  \\<Your\\ Password\\> \" > /etc/ppp/chap-secrets",	ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomChapPapId);
			system((char*)CopyTmp);
		}

		result = PASS;
	}

	return result;
}

//==========================================
// Check internet access status
//==========================================
int isReachableInternet(void)
{
	int result = FAIL;
	FILE *fp;
	char cmd[256];
	char buf[512];
	char tmp[512];

	for(int idx=0;idx<ARRAY_SIZE(valid_Internet);idx++)
	{
		strcpy(cmd, "ping -c 1 -w 3 ");
		strcat(cmd, valid_Internet[idx]);
		fp = popen(cmd, "r");
		if(fp != NULL)
		{
			while(fgets(buf, sizeof(buf), fp) != NULL)
			{
				if(strstr(buf, "transmitted") > 0)
				{
					sscanf(buf, "%*s%*s%*s%*s%*s%s", tmp);

					if(strcmp(tmp,"100%") != 0)
					{
						result = PASS;
					}
				}
			}
		}
		pclose(fp);
	}

	return result;
}

//==========================================
// Unbind USB port
//==========================================
int isModuleUnbind(void)
{
	int result = FAIL;
	FILE *fp;
	char cmd[256];

	strcpy(cmd, "echo '1-1'> /sys/bus/usb/drivers/usb/unbind");
	fp = popen(cmd, "r");

	if(fp == NULL)
	{
		result = FAIL;
	}
	else
	{
		result = PASS;

		#ifdef SystemLogMessage
    	DEBUG_INFO("Unbind USB for dongle.\n");
		#endif
	}

	return result;
}

//==========================================
// Bind USB port
//==========================================
int isModuleBind(void)
{
	int result = FAIL;
	FILE *fp;
	char cmd[256];

	strcpy(cmd, "echo '1-1'> /sys/bus/usb/drivers/usb/bind");
	fp = popen(cmd, "r");

	if(fp == NULL)
	{
		result = FAIL;
	}
	else
	{
		#ifdef SystemLogMessage
    	DEBUG_INFO("Bind USB for dongle.\n");
		#endif

		result = PASS;
	}

	return result;
}

//==========================================
// Dongle reset process
//==========================================
int rstModule(void)
{
	int result = PASS;
	int uart;
	char rx[512];

	if((uart = openPort(portName[Dongle.Model])) != FAIL)
	{
		//==============================
		// Reset module
		//==============================
		switch(Dongle.Model)
		{
			case DONGLE_QUECTEL:
			default:
				if(at_command(uart, "at+cfun=1,1\r", rx) <= 0)
				{
					result = FAIL;
				}

				break;
			case DONGLE_UBLOX:
				if(at_command(uart, "at+cfun=1,1\r", rx) <= 0)
				{
					result = FAIL;
				}

				break;
		}
	}
	else
	{
		#ifdef SystemLogMessage
		DEBUG_ERROR("%s open fail.\n", portName[Dongle.Model]);
		#endif
		result = FAIL;
	}
	close(uart);
	sleep(40);

	return result;
}

//==========================================
// AT command send/receive
//==========================================
int at_command(int uart, char* cmd, char* rx)
{
	int len;
	//sleep(2); //required to make flush work, for some reason
	tcflush(uart,TCIOFLUSH);
	if(write(uart, cmd, strlen(cmd)) >= sizeof(cmd))
	{
		usleep(500000);
		len = read(uart, rx, 512);
	}
	else
	{
		#ifdef SystemLogMessage
		DEBUG_ERROR("AT command %s response fail.\n", cmd);
		#endif
	}

	return len;
}

//==========================================
// Dongle communication port open
//==========================================
int openPort(char *tty)
{
	int uart = open(tty, O_RDWR | O_NOCTTY | O_NDELAY);

	if(uart!=FAIL)
	{
		if((set_interface_attribs(uart, B115200, 0) != PASS) || (set_blocking(uart, 0) != PASS))
			uart = FAIL;
	}

	return uart;
}

//==========================================
// Port parameter set
//==========================================
int set_interface_attribs (int fd, int speed, int parity)
{
	int result = FAIL;
	struct termios tty;
    memset (&tty, 0, sizeof tty);

    if (tcgetattr (fd, &tty) == 0)
	{
    	cfsetospeed (&tty, speed);
		cfsetispeed (&tty, speed);

		tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
		// disable IGNBRK for mismatched speed tests; otherwise receive break
		// as \000 chars
		tty.c_iflag &= ~IGNBRK;         		// disable break processing
		tty.c_lflag = 0;                		// no signaling chars, no echo,
												// no canonical processing
		tty.c_oflag = 0;                		// no remapping, no delays
		tty.c_cc[VMIN]  = 0;            		// read doesn't block
		tty.c_cc[VTIME] = 10;            		// 1 seconds read timeout

		tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

		tty.c_cflag |= (CLOCAL | CREAD);		// ignore modem controls,
												// enable reading
		tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
		tty.c_cflag |= parity;
		tty.c_cflag &= ~CSTOPB;
		tty.c_cflag &= ~CRTSCTS;

		if (tcsetattr (fd, TCSANOW, &tty) == 0)
			result = PASS;
	}

	return result;
}

int set_blocking (int fd, int should_block)
{
	int result = FAIL;
	struct termios tty;
	memset (&tty, 0, sizeof tty);
	if (tcgetattr (fd, &tty) == 0)
	{
		tty.c_cc[VMIN]  = should_block ? 1 : 0;
		tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

		if (tcsetattr (fd, TCSANOW, &tty) == 0)
			result = PASS;
	}

	return result;
}

//==========================================
// Common routine
//==========================================
void trim_s(char *s, unsigned char len)
{
	for(unsigned char i = 0 ; i < len; i++)
	{
		if (!((s[i]>='a') && (s[i]<='z')) && !((s[i]>='A') && (s[i]<='Z'))&& !((s[i]>='0') && (s[i]<='9')))
		{
			s[i] = s[i + 1];
			strncpy(s + i, s + i + 1, len);
			i -= 1;
			len -= 1;
		}
	}
	s[len + 1] = '\0';
}

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

//==========================================
// Main process
//==========================================
int main(void)
{
	//==========================================
	//Initialization share memory
	//==========================================
	if(InitShareMemory() == FAIL)
	{
		#ifdef SystemLogMessage
		DEBUG_ERROR("InitShareMemory NG\n");
		#endif

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

		return 0;
	}

	for(;;)
	{
		if(((Dongle.Model = Check4GModem()) != FAIL))
		{
			system("pkill Module_Wifi");
			ShmStatusCodeData->FaultCode.FaultEvents.bits.Telecom4GModuleBroken = 0;

			Dongle.cnt_SearchModuleFail = 0;
			if(isReadInfo() == PASS)
			{
				memcpy(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModelName, Dongle.MODELNAME, sizeof Dongle.MODELNAME);
				memcpy(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSoftwareVer, Dongle.REVISION, sizeof Dongle.REVISION);
				memcpy(ShmSysConfigAndInfo->SysInfo.TelcomModemFwRev, Dongle.REVISION, sizeof Dongle.REVISION);
				memcpy(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemImei, Dongle.IMEI, sizeof Dongle.IMEI);
				ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomRssi = Dongle.CSQ;
				ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemMode = Dongle.MODE;

				#ifdef SystemLogMessage
				DEBUG_INFO("========================================\n");
				DEBUG_INFO("Status: Device info readable...\n");
				DEBUG_INFO("Device MANUFACTURER: %s\n", Dongle.MANUFACTURER);
				DEBUG_INFO("Device MODEL: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModelName);
				DEBUG_INFO("Device REVISION: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSoftwareVer);
				DEBUG_INFO("Device IMEI: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemImei);
				DEBUG_INFO("Device RSSI: %d\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomRssi);
				DEBUG_INFO("Device MODE: %d\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemMode);
				DEBUG_INFO("========================================\n");
				#endif

				Dongle.cnt_ReadInfoFail = 0;
				if(isReadSimInfo() == PASS)
				{
					ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimStatus = 1;
					memcpy(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimIccid, Dongle.ICCID, sizeof Dongle.ICCID);
					memcpy(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimImsi, Dongle.IMSI, sizeof Dongle.IMSI);

					#ifdef SystemLogMessage
					DEBUG_INFO("========================================\n");
					DEBUG_INFO("Status: SIM card info readable...\n");
					DEBUG_INFO("Device IMSI: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimImsi);
					DEBUG_INFO("Device ICCID: %.20s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimIccid);
					DEBUG_INFO("TelcomSimStatus: %d\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimStatus);
					DEBUG_INFO("========================================\n");
					#endif

					Dongle.cnt_ReadSimInfoFail = 0;
					if(isPppUp() == PASS)
					{
						#ifdef SystemLogMessage
						DEBUG_INFO("PPP IP: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomIpAddress);
						#endif

						Dongle.cnt_pppFail = 0;
						do
						{
							if(isReachableInternet() == PASS)
							{
								ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomNetworkConn = 1;
								ShmStatusCodeData->AlarmCode.AlarmEvents.bits.Telecom4GModuleCommFail = 0;
								ShmStatusCodeData->InfoCode.InfoEvents.bits.InternetDisconnectVia4Gi = 0;
								ShmStatusCodeData->InfoCode.InfoEvents.bits.ApnDisconnectVia4Gi = 0;

								if(CheckSignalRssi() != PASS)
								{
									#ifdef SystemLogMessage
									DEBUG_INFO("No RSSI\n");
									#endif
								}
								else
								{
									ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomRssi = Dongle.CSQ;
								}

								#ifdef SystemLogMessage
								DEBUG_INFO("================================\n");
								DEBUG_INFO("Status: 4G Device connecting.\n");
								DEBUG_INFO("================================\n");
								DEBUG_INFO("Device MANUFACTURER: %s\n", Dongle.MANUFACTURER);
								DEBUG_INFO("Device MODEL: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModelName);
								DEBUG_INFO("Device REVISION: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSoftwareVer);
								DEBUG_INFO("Device IMEI: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemImei);
								DEBUG_INFO("Device IMSI: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimImsi);
								DEBUG_INFO("Device RSSI: %d\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomRssi);
								DEBUG_INFO("Device ICCID: %.20s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimIccid);
								DEBUG_INFO("Device MODE: %d\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemMode);
								DEBUG_INFO("Network connection: %d\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomNetworkConn);
								DEBUG_INFO("================================\n");
								#endif

								#ifdef SystemLogMessage
								DEBUG_INFO("Dongle internet valid result: Pass\n");
								#endif

								Dongle.cnt_InternetFail = 0;
								sleep(CheckInternetInterval);
							}
							else
							{
								#ifdef SystemLogMessage
								DEBUG_INFO("Dongle internet valid result: Fail %d time\n", Dongle.cnt_InternetFail);
								#endif

								Dongle.cnt_InternetFail++;
								sleep(DisconnInterval);
							}
						}while(Dongle.cnt_InternetFail < 3);

						ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomNetworkConn = 0;
						ShmStatusCodeData->InfoCode.InfoEvents.bits.InternetDisconnectVia4Gi = 1;
						ShmStatusCodeData->InfoCode.InfoEvents.bits.ApnDisconnectVia4Gi = 1;
					}
					else
					{
						if(Load4gConfiguration() == FAIL)
						{
							DEBUG_ERROR("4G configuration value NG.\n");
							if(ShmStatusCodeData!=NULL)
							{
								ShmStatusCodeData->AlarmCode.AlarmEvents.bits.Telecom4GModuleCommFail=1;
							}
						}
						else
						{
							#ifdef SystemLogMessage
							DEBUG_WARN("PPP interface not found.\n");
							#endif

							memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomIpAddress, 0 , sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomIpAddress);

							Dongle.cnt_pppFail++;
							if(Dongle.cnt_pppFail > 5)
							{
								#ifdef SystemLogMessage
								DEBUG_INFO("Dongle hardware reset...\n");
								#endif

								rstModule();
							}

							system("killall 4GDetection");
							sleep(2);

							if(Dongle.Model == DONGLE_QUECTEL)
							{
								system("/root/ppp/4GDetection /dev/ttyUSB3 &");
								printf("4GDetection for primary device.\n");
							}
							else if(Dongle.Model == DONGLE_UBLOX)
							{
								system("/root/ppp/4GDetection /dev/ttyACM0 &");
								printf("4GDetection for second device.\n");
							}
							else
							{}
						}

						sleep(CheckConnectionInterval);
					}
				}
				else
				{
					#ifdef SystemLogMessage
					DEBUG_ERROR("SIM card info read error fail: %d\n", Dongle.cnt_ReadSimInfoFail);
					#endif

					ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimStatus = 0;
					memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimImsi, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimImsi);
					memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimIccid, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimIccid);
					memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomIpAddress, 0 , sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomIpAddress);

					#ifdef SystemLogMessage
					DEBUG_INFO("========================================\n");
					DEBUG_INFO("Status: Read Sim card info fail...\n");
					DEBUG_INFO("Device ICCID: %.20s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimIccid);
					DEBUG_INFO("Device IMSI: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimImsi);
					DEBUG_INFO("========================================\n");
					#endif

					Dongle.cnt_ReadSimInfoFail ++;
					if(Dongle.cnt_ReadSimInfoFail > 3)
					{
						#ifdef SystemLogMessage
						DEBUG_INFO("Dongle hardware reset...\n");
						#endif

						Dongle.cnt_ReadSimInfoFail = 0;
						rstModule();
					}

					sleep(CheckSimInterval);
				}
			}
			else
			{
				#ifdef SystemLogMessage
				DEBUG_ERROR("Device info read error fail: %d\n", Dongle.cnt_ReadInfoFail);
				#endif

				Dongle.MODE = NO_SERVICE;
				memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModelName, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModelName);
				memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSoftwareVer, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSoftwareVer);
				memset(ShmSysConfigAndInfo->SysInfo.TelcomModemFwRev, 0, sizeof ShmSysConfigAndInfo->SysInfo.TelcomModemFwRev);
				memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemImei, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemImei);
				ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomRssi = 0;
				ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemMode = 0;

				#ifdef SystemLogMessage
				DEBUG_INFO("========================================\n");
				DEBUG_INFO("Status: Read device info fail...\n");
				DEBUG_INFO("Device MANUFACTURER: %s\n", Dongle.MANUFACTURER);
				DEBUG_INFO("Device MODEL: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModelName);
				DEBUG_INFO("Device REVISION: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSoftwareVer);
				DEBUG_INFO("Device IMEI: %s\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemImei);
				DEBUG_INFO("Device RSSI: %d\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomRssi);
				DEBUG_INFO("Device MODE: %d\n", ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemMode);
				DEBUG_INFO("========================================\n");
				#endif

				Dongle.cnt_ReadInfoFail++;
				if(Dongle.cnt_ReadInfoFail > 3)
				{
					#ifdef SystemLogMessage
					DEBUG_INFO("Device hardware reset...\n");
					#endif

					Dongle.cnt_ReadInfoFail = 0;
					rstModule();
				}
				sleep(CheckModemInfoInterval);
			}
		}
		else
		{
			//==========================================
			// Module valid fail process
			//==========================================
			#ifdef SystemLogMessage
			DEBUG_ERROR("Device search error fail: %d\n", Dongle.cnt_SearchModuleFail);
			#endif

			ShmStatusCodeData->InfoCode.InfoEvents.bits.InternetDisconnectVia4Gi = 1;
			ShmStatusCodeData->InfoCode.InfoEvents.bits.ApnDisconnectVia4Gi = 1;
			ShmStatusCodeData->AlarmCode.AlarmEvents.bits.Telecom4GModuleCommFail = 0;
			ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomNetworkConn = 0;
			ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomRssi = 0;
			ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimStatus = 0;
			ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemMode = 0;
			memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemImei, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModemImei);
			memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimImsi, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimImsi);
			memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomIpAddress, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomIpAddress);
			memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimIccid, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSimIccid);
			memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSoftwareVer, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomSoftwareVer);
			memset(ShmSysConfigAndInfo->SysInfo.TelcomModemFwRev, 0, sizeof ShmSysConfigAndInfo->SysInfo.TelcomModemFwRev);
			memset(ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModelName, 0, sizeof ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomModelName);

			Dongle.cnt_SearchModuleFail++;
			if(Dongle.cnt_SearchModuleFail > 3)
			{
				#ifdef SystemLogMessage
				DEBUG_ERROR("4G Module was broken.\n");
				#endif

				if(isModuleUnbind() == PASS)
				{
					isModuleBind();
				}

				ShmStatusCodeData->FaultCode.FaultEvents.bits.Telecom4GModuleBroken = 1;

				Dongle.cnt_SearchModuleFail = 0;
			}

			sleep(CheckModemInterval);
		}
		//sleep(SystemInterval);
	}

	return 0;
}