// ==========================================================================
// modem.c	
// (c) 2020, Aurel Dumitru
//
// Description:
// SIM5XXX, SIM7XXX modems communication
// =========================================================================

#include <stdlib.h>
#include <string.h>
#include "modem.h"
#include "file.h"
#include "flash.h"
#include "rtc.h"
#include "rgbled.h"
#include "sensor_nfc.h"
#include "devstatus.h"
#include "utils.h"
#include "control.h"
#include "config.h"
#include "calibrations.h"

#define CHECK_RN_OFF                 0
#define CHECK_RN_ON                  1

#define MODEM_CMD_RUNNING            0x01
#define MODEM_CMD_FINISHED_OK        0xA0
#define MODEM_CMD_SKIP_OK            0xA1
#define MODEM_CMD_FINISHED_NOK       0xB1
#define MODEM_CMD_FINISHED_TIMEOUT   0xB2

#define MODEM_RX_BUFFER_LEN          2024
#define MODEM_TX_BUFFER_LEN          2048

#define MODEM_CMD_QUEUE_SIZE         32
#define MODEM_DOUBLE_BUFFER_SIZE     (64*1024)
#define MODEM_CFTPSPUTFILE_TIMEOUT   20000

#define MODEM_FIRMWARE_FOUND_ON_FTP_SERVER		1
#define MODEM_CNF_FOUND_ON_FTP_SERVER			2

#define MODEM_UPDATE_FIRMWARE_IDLE_STATE       	0
#define MODEM_UPDATE_CNF_WRITE_STATE			1
#define MODEM_UPDATE_FIRMWARE_CHECKSUM_STATE   	2
#define MODEM_UPDATE_FIRMWARE_ERASE_FLASH_STATE	3
#define MODEM_UPDATE_FIRMWARE_PROG_FLASH_STATE	4

#define MODEM_POWER_OFF_STATE          0
#define MODEM_POWER_WAIT_ON_STATE      1
#define MODEM_POWER_ON_STATE           2


struct Modem_CmdStruct
{
	char     CmdText[192];
	uint8_t (*Op)(struct Modem_CmdStruct* Cmd);
	uint32_t Timeout;
	uint32_t Param;
	uint32_t ParamSec;
	uint16_t State;

};

struct Modem_OpStruct
{
	char    CmdTypeText[20];
	uint8_t (*Op)(struct Modem_CmdStruct* Cmd);
	uint32_t Timeout;
};


// Global
void	Modem_SendCmd(char* Text);

uint8_t Modem_OpWait(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpSimple(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAte0(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCREG_Q(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCIMI(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCGSOCKCONT(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCICCID(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCSQ(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtSIMCOMATI(struct Modem_CmdStruct* Cmd);

// Time
uint8_t Modem_OpAtCCLK_Q(struct Modem_CmdStruct* Cmd);

// Filesystem
uint8_t Modem_OpAtCFTRANRX(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtFSCD(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtFSDEL(struct Modem_CmdStruct* Cmd);

// Ftp
uint8_t Modem_OpAtCFTPSSTART(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCFTPSSTOP(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCFTPSLOGIN(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCFTPSLOGOUT(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCFTPSGET(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCFTPSPUTFILE(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCFTPSDELE(struct Modem_CmdStruct* Cmd);
uint8_t Modem_OpAtCFTPSLIST(struct Modem_CmdStruct* Cmd);

// GPS
uint8_t Modem_OpAtCGPSINFO(struct Modem_CmdStruct* Cmd);

/* SMS */
uint8_t Modem_OpAtCMGS(struct Modem_CmdStruct* Cmd);


const struct Modem_OpStruct Modem_OpList[]=
{
	// File system
	{"+CFTRANRX",		Modem_OpAtCFTRANRX,			   40000},
	{"+FSDEL",			Modem_OpSimple,			    	5000},
	{"+FSCD",			Modem_OpSimple,			    	5000},

	// FTP
	{"+CFTPSGET",		Modem_OpAtCFTPSGET,			   30000},
	{"+CFTPSPUTFILE",   Modem_OpAtCFTPSPUTFILE,    	   30000},
	{"+CFTPSLIST",		Modem_OpAtCFTPSLIST,    	   20000},
	{"+CFTPSSTART",		Modem_OpAtCFTPSSTART,		   10000},
	{"+CFTPSSTOP",		Modem_OpAtCFTPSSTOP,		   10000},
	{"+CFTPSLOGIN",		Modem_OpAtCFTPSLOGIN,		   30000},
	{"+CFTPSLOGOUT",	Modem_OpAtCFTPSLOGOUT,		   20000},
	{"+CFTPSSINGLEIP",	Modem_OpSimple,				    5000},
	{"+CFTPSDELE",	    Modem_OpAtCFTPSDELE,	       20000},

	// Time
	{"+CTZU",			Modem_OpSimple,					5000},
	{"+CCLK?",			Modem_OpAtCCLK_Q,				5000},

	// NET
	{"+CGSOCKCONT",		Modem_OpAtCGSOCKCONT,		   20000},
	{"+CSOCKSETPN",		Modem_OpSimple,					5000},

	// GPS
	{"+CGPS",			Modem_OpSimple,					5000},
	{"+CGPSINFO",		Modem_OpAtCGPSINFO,				5000},

	//Global
	{"+WAIT",			Modem_OpWait,				       0},
	{"E0",				Modem_OpAte0,					5000},
	{"+CREG?",			Modem_OpAtCREG_Q,			  180000},
	{"+CATR",			Modem_OpSimple,					5000},
	{"+IFC",			Modem_OpSimple,					5000},
	{"+IPREX",			Modem_OpSimple,					5000},
	{"+CIMI",           Modem_OpAtCIMI,					5000},
	{"+SIMCOMATI",      Modem_OpAtSIMCOMATI,			5000},
	{"+CICCID",      	Modem_OpAtCICCID,				5000},
	{"+CSQ",			Modem_OpAtCSQ,					5000},

	// SMS
	{"+CMGS",			Modem_OpAtCMGS,				   10000},
	{"+CMGF",			Modem_OpSimple,					5000},
};

const char Modem_CacheReadStr[] = "AT+CFTPSCACHERD\r";

const uint32_t Modem_OpListNumber = sizeof(Modem_OpList)/sizeof(struct Modem_OpStruct);

struct Modem_CmdStruct Modem_CmdQueue[MODEM_CMD_QUEUE_SIZE];
struct Modem_CmdStruct* Modem_CmdPtr;
uint8_t  Modem_CmdQueueHead;
uint8_t  Modem_CmdQueueTail;
uint32_t Modem_Timeout;
uint32_t Modem_WaitTimeStamp;
uint32_t Modem_CmdParam;
uint32_t Modem_CmdParamSec;

FIL*     Modem_FileToSend;
uint32_t Modem_FtpSizeToSend;
uint32_t Modem_CmdSize;
uint8_t  Modem_LastStatus;
int8_t   Modem_SentFileStatus;

uint8_t  Modem_DataBufferPutIndex;
uint8_t  Modem_DataBufferGetIndex;
uint8_t  Modem_DataBufferReqSend;
uint8_t  Modem_DataBufferFullFlag[2];
uint32_t Modem_DataBufferSize[2];

// Rx/Tx buffers
uint8_t  Modem_TxBuf[MODEM_TX_BUFFER_LEN];
uint8_t  Modem_RxBuf[MODEM_RX_BUFFER_LEN];
uint8_t* Modem_DataBuffer;
uint8_t* Modem_CircularBuffer;

uint8_t* Modem_UartRxAddr;
uint8_t* Modem_UartRxAddrEnd;

uint8_t  Modem_CmdUsesIsr;
uint8_t  Modem_UpdateFirmwareState;

uint32_t Modem_FirmwareSize;
uint8_t  Modem_FirmwareCks;
uint8_t* Modem_FirmwarePtr;
uint8_t  Modem_FirmwareUpdateType;

uint8_t  Modem_CopyUartToFirmwareState;
uint8_t  Modem_FtpGetDataMask;
uint8_t  Modem_FtpGet0ReceivedFlag;
uint32_t Modem_CacheReadSize;
uint8_t* Modem_CurrAddr;

uint8_t  Modem_PowerState;
uint32_t Modem_PowerTimestamp;

uint8_t	 Modem_Error;

char	 Modem_Apn[32];
char	 Modem_FtpsType;

uint32_t Modem_TimeStamp;
uint8_t  Modem_TestStatus = 0;
uint32_t Modem_FilesFoundOnFtpServer;
uint32_t Modem_GetFileNow;

char	 Modem_Iccid[21];
int8_t   Modem_RssiInt;
char	 Modem_RssiBer[64];
char	 Modem_GpsString[128];

extern UART_HandleTypeDef huart7;
extern DMA_HandleTypeDef  hdma_uart7_rx;
extern DMA_HandleTypeDef  hdma_uart7_tx;


void Modem_UartErrorIsr(UART_HandleTypeDef *huart)
{
	Modem_Error = HAL_UART_GetError(huart);
}


void Modem_PowerDownHwModule(void)
{
	// Set the power state to POWER OFF
	Modem_PowerState = MODEM_POWER_OFF_STATE;
	// Abort the uart communication
	HAL_UART_Abort(&huart7);
	// Stop power supply
	HAL_GPIO_WritePin(EN_3V3_GPIO_Port, EN_3V3_Pin, GPIO_PIN_RESET);

	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.Pin = MODEM_RXD_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
}


void Modem_PowerUpHwModule(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.Pin = MODEM_RXD_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_UART7;
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

	// Start power supply
	HAL_GPIO_WritePin(EN_3V3_GPIO_Port, EN_3V3_Pin, GPIO_PIN_SET);
	// Set the power state to WAIT_ON
	Modem_PowerState 	 = MODEM_POWER_WAIT_ON_STATE;
	Modem_SentFileStatus = 0;
	Modem_Error 		 = 0;
	Modem_CmdUsesIsr 	 = 0;
	// Get the start time of WAIT to start modem firmware
	Modem_PowerTimestamp = HAL_GetTick();
	Modem_TestStatus = MODEM_TESTSTATUS_FIRMWARE_BOOT;
}


uint8_t Modem_SearchText(char* TextOk, char* TextError, uint8_t CheckNR)
{
	uint8_t I;
	uint8_t* Ptr;
	uint32_t DataLength = MODEM_RX_BUFFER_LEN - ((DMA_Stream_TypeDef*)hdma_uart7_rx.Instance)->NDTR;
	if (DataLength < (3 + CheckNR*2))
		return MODEM_CMD_RUNNING;
	if (CheckNR && ((Modem_RxBuf[DataLength-2] != '\r') || (Modem_RxBuf[DataLength-1] != '\n')))
		return MODEM_CMD_RUNNING;

	char* TextOkPtr     = TextOk;
	char* TextErrorPtr  = TextError;

	while (*TextOkPtr != '\0')
		TextOkPtr++;
	uint8_t TextOkLength = TextOkPtr - TextOk;
	while (*TextErrorPtr != '\0')
		TextErrorPtr++;
	uint8_t TextErrorLength = TextErrorPtr - TextError;

	/* Search for ok text */
	Ptr = Modem_RxBuf + (DataLength - TextOkLength - 2*CheckNR);
	while (Ptr >= Modem_RxBuf)
	{
		for (I=0; I<TextOkLength; I++)
			if (Ptr[I] != TextOk[I]) break;
		if (I == TextOkLength)
			return MODEM_CMD_FINISHED_OK;
		else
			Ptr--;
	}
	
	/* Search for error text */
	Ptr = Modem_RxBuf + (DataLength - TextErrorLength - 2*CheckNR);
	while (Ptr >= Modem_RxBuf)
	{
		for (I=0; I<TextErrorLength; I++)
			if (Ptr[I] != TextError[I]) break;
		if (I == TextErrorLength)
			return MODEM_CMD_FINISHED_NOK;
		else
			Ptr--;
	}

	return MODEM_CMD_RUNNING;
}


uint8_t* Modem_CopyTxBuf(uint8_t* InBuf, uint8_t* OutBuf)
{
	while(*InBuf != 0)
		*OutBuf++ = *InBuf++;
	return OutBuf;
}


void Modem_SendUartCmd(char* CmdPtr)
{
	uint8_t* Ptr = Modem_CopyTxBuf((uint8_t*)CmdPtr, Modem_TxBuf);
	HAL_UART_Abort(&huart7);
	HAL_UART_Receive_DMA(&huart7, Modem_RxBuf, MODEM_RX_BUFFER_LEN);
	HAL_UART_Transmit_DMA(&huart7, Modem_TxBuf, (Ptr - Modem_TxBuf));
}


void Modem_SendUartFtpsGetCmd(char* CmdPtr)
{
	uint8_t* Ptr = Modem_CopyTxBuf((uint8_t*)CmdPtr, Modem_TxBuf);
	HAL_UART_Abort(&huart7);
	Modem_UartRxAddr    = Modem_CircularBuffer;
	Modem_UartRxAddrEnd = Modem_CircularBuffer + NC_UART_RX_CIRCULAR_BUFFER_LEN;
	Modem_FirmwarePtr   = Modem_DataBuffer;
	HAL_UART_Receive_DMA(&huart7, Modem_CircularBuffer, NC_UART_RX_CIRCULAR_BUFFER_LEN);
	HAL_UART_Transmit_DMA(&huart7, Modem_TxBuf, (Ptr - Modem_TxBuf));
}


void Modem_SendUartFtpFileIsr(UART_HandleTypeDef *huart)
{
	if (Modem_CmdUsesIsr)
	{
		Modem_DataBufferFullFlag[Modem_DataBufferGetIndex] = 0;
		Modem_DataBufferGetIndex ^= 1;
		if (Modem_DataBufferFullFlag[Modem_DataBufferGetIndex] == 1)
		{
			uint8_t* Src = Modem_DataBuffer + Modem_DataBufferGetIndex*(MODEM_DOUBLE_BUFFER_SIZE/2);
			HAL_UART_AbortTransmit(huart);
			HAL_UART_Transmit_DMA(huart, Src, Modem_DataBufferSize[Modem_DataBufferGetIndex]);
		}
		else
			Modem_DataBufferReqSend = 1;
	}
}


void Modem_SendUartFtpFile(UART_HandleTypeDef *huart)
{
	uint8_t* Src = Modem_DataBuffer + Modem_DataBufferGetIndex*(MODEM_DOUBLE_BUFFER_SIZE/2);
	HAL_UART_AbortTransmit(huart);
	HAL_UART_Transmit_DMA(huart, Src, Modem_DataBufferSize[Modem_DataBufferGetIndex]);
}


uint8_t Modem_CheckTimeout(void)
{
	uint8_t RetVal = 0;
	if ((int)(Modem_Timeout - HAL_GetTick()) <= 0)
	{
		HAL_UART_Abort(&huart7);
		RetVal = 1;
	}
	return RetVal;
}


uint8_t Modem_CheckResponseTextAndTimeout(char* OkText, char* ErrorText, uint8_t CheckNR)
{
	uint8_t RetVal = Modem_SearchText(OkText, ErrorText, CheckNR);
	if (RetVal == MODEM_CMD_RUNNING)
		if (Modem_CheckTimeout())
			RetVal = MODEM_CMD_FINISHED_TIMEOUT;
	return RetVal;
}


uint8_t* Modem_CheckResponseText(char* Text)
{
	uint8_t I;
	uint8_t* Ptr;
	uint32_t DataLength = MODEM_RX_BUFFER_LEN - ((DMA_Stream_TypeDef*)hdma_uart7_rx.Instance)->NDTR;
	char* TextPtr = Text;

	while (*TextPtr != '\0')
		TextPtr++;
	uint8_t TextLength = TextPtr - Text;
	
	/* Search for text */
	Ptr = Modem_RxBuf + (DataLength - TextLength);
	while (Ptr >= Modem_RxBuf)
	{
		for (I=0; I<TextLength; I++)
			if (Ptr[I] != Text[I]) break;
		if (I == TextLength)
			return Ptr;
		else
			Ptr--;
	}
	return NULL;
}

void Modem_FirmwareUpdateError(uint32_t Val)
{
	Modem_UpdateFirmwareState = MODEM_UPDATE_FIRMWARE_IDLE_STATE;
	Utils_FreeMem(Modem_DataBuffer);
	Utils_FreeMem(Modem_CircularBuffer);

	if (Val)
	{
		Modem_FilesFoundOnFtpServer = 0;
		if (Modem_FirmwareUpdateType != MODEM_UPDATE_CNF_STATUS)
			Modem_FirmwareUpdateType = MODEM_UPDATE_IDLE_STATUS;
	}
	else
	{
		Modem_FirmwareUpdateType = (Modem_FilesFoundOnFtpServer == MODEM_FIRMWARE_FOUND_ON_FTP_SERVER) ? MODEM_UPDATE_FIRMWARE_STATUS:MODEM_UPDATE_CNF_STATUS;
		Utils_ClearMsb(&Modem_FilesFoundOnFtpServer);
	}

}

uint32_t Modem_GetFirmwareUpdateStatus(void)
{
	if (Modem_FilesFoundOnFtpServer)
		return MODEM_UPDATE_CHECK_STATUS;
	else
		return Modem_FirmwareUpdateType;
}

void Modem_ResetFirmwareUpdateStatus(void)
{
	Modem_UpdateFirmwareState	= MODEM_UPDATE_FIRMWARE_IDLE_STATE;
	Modem_FilesFoundOnFtpServer = 0;
	Modem_FirmwareUpdateType	= MODEM_UPDATE_IDLE_STATUS;
}


uint8_t Modem_CopyUartRxToFirmware(void)
{
	uint8_t* Ptr = (uint8_t*)(((DMA_Stream_TypeDef*)hdma_uart7_rx.Instance)->M0AR) + (NC_UART_RX_CIRCULAR_BUFFER_LEN - (((DMA_Stream_TypeDef*)hdma_uart7_rx.Instance)->NDTR));
	uint8_t* Src;
	uint8_t* Dest;
	uint32_t Size;

	//copy data from DMA buffer in firmware buffer
	while (Modem_UartRxAddr != Ptr)
	{
		*Modem_FirmwarePtr++ = *Modem_UartRxAddr++;
		if (Modem_UartRxAddr == Modem_UartRxAddrEnd)
			Modem_UartRxAddr = Modem_CircularBuffer;
	}
	Size = Modem_FirmwarePtr - Modem_DataBuffer;

	switch (Modem_CopyUartToFirmwareState)
	{
	case 0:
		Modem_FtpGet0ReceivedFlag = 0;
		Modem_CurrAddr            = Modem_DataBuffer;
		if (Size >= 28)
				if (Utils_CheckMemPattern(Modem_CurrAddr, (uint8_t*)"\r\nOK\r\n\r\n+CFTPS: RECV EVENT\r\n"))
				{
					Src  = Modem_CurrAddr + 28;
					Dest = Modem_CurrAddr;
					while (Src < Modem_FirmwarePtr)
						*Dest++ = *Src++;
					Modem_FirmwarePtr = Dest;
					Modem_CurrAddr    = Modem_DataBuffer;
					Modem_CopyUartToFirmwareState = 1;
				}
		break;

	case 1:
		HAL_UART_AbortTransmit(&huart7);
		HAL_UART_Transmit_DMA(&huart7, (uint8_t*)Modem_CacheReadStr, 16);
		Modem_FtpGetDataMask = 0;
		Modem_CacheReadSize  = 0;
		Modem_CopyUartToFirmwareState = 2;
		break;

	case 2:
		Size = Modem_FirmwarePtr - Modem_CurrAddr;
		if (Modem_FtpGetDataMask == 0)
		{
			if (Size >= 16)
				if (Utils_CheckMemPattern(Modem_CurrAddr, (uint8_t*)"\r\n+CFTPSGET: 0\r\n"))
				{
					Src  = Modem_CurrAddr + 16;
					Dest = Modem_CurrAddr;
					while (Src < Modem_FirmwarePtr)
						*Dest++ = *Src++;
					Modem_FirmwarePtr = Dest;
					Modem_FtpGet0ReceivedFlag = 1;
					break;
				}
			if (Size >= 22)
				if (Utils_CheckMemPattern(Modem_CurrAddr, (uint8_t*)"\r\n+CFTPS: RECV EVENT\r\n"))
				{
					Src  = Modem_CurrAddr + 22;
					Dest = Modem_CurrAddr;
					while (Src < Modem_FirmwarePtr)
						*Dest++ = *Src++;
					Modem_FirmwarePtr = Dest;
					break;
				}
			if (Size >= 24)
				if (Utils_CheckMemPattern(Modem_CurrAddr, (uint8_t*)"\r\n+CFTPSGET: DATA,"))
				{
					Modem_FtpGetDataMask = 1;
					Src  = Modem_CurrAddr + 18;
					Modem_CacheReadSize = 0;
					while (*Src != '\r')
						Modem_CacheReadSize = Modem_CacheReadSize*10 + ((*Src++) - '0');
					Src += 2;
					Dest = Modem_CurrAddr;
					while (Src < Modem_FirmwarePtr)
						*Dest++ = *Src++;
					Modem_FirmwarePtr = Dest;
					break;
				}
		}

		if ((Modem_CacheReadSize > 0) || (Modem_FtpGetDataMask == 0))
			if (Size >= Modem_CacheReadSize+6)
			{
				if (Utils_CheckMemPattern(&Modem_CurrAddr[Modem_CacheReadSize], (uint8_t*)"\r\nOK\r\n"))
				{
					Src  = Modem_CurrAddr + Modem_CacheReadSize + 6;
					Dest = Modem_CurrAddr + Modem_CacheReadSize;
					Modem_CurrAddr = Dest;
					while (Src < Modem_FirmwarePtr)
						*Dest++ = *Src++;
					Modem_FirmwarePtr = Dest;
					if ((Modem_FtpGet0ReceivedFlag) && (Modem_CacheReadSize == 0))
					{
						Modem_FirmwareSize = Modem_FirmwarePtr - Modem_DataBuffer;
						return 1;
					}
					Modem_CopyUartToFirmwareState = 1;
				}
				else
					if (Modem_CacheReadSize > 0)
					{
						HAL_UART_Abort(&huart7);
						return 20;
					}

			}
		break;
	}
	return 0;
}


uint8_t Modem_OpWait(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	char* Ptr;

	if (Cmd->State == 0)
	{
		/* Get timeout from input string */
		Ptr = Cmd->CmdText + 8;
		Modem_WaitTimeStamp = 0;
		while (*Ptr != '\r')
		{
			Modem_WaitTimeStamp *= 10;
			Modem_WaitTimeStamp += (*Ptr++) - '0';
		}
		Modem_WaitTimeStamp += HAL_GetTick();
		Cmd->State = 1;
	}
	else
		if ((int)(Modem_WaitTimeStamp - HAL_GetTick()) <= 0)
			RetVal = MODEM_CMD_FINISHED_OK;
	return RetVal;
}


uint8_t Modem_OpSimple(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAte0(struct Modem_CmdStruct* Cmd)
{
	char IprexCmd[24];

	DevStatus[DEVSTATUS_MODEM_STATUS_IDX] = DEVSTATUS_MODEM_OK;
	Modem_TimeStamp = HAL_GetTick();
	Modem_SendUartCmd(Cmd->CmdText);
	while ((HAL_GetTick() - Modem_TimeStamp) < 1000)
		if (Modem_CheckResponseText("OK\r\n") != NULL)
			return MODEM_CMD_FINISHED_OK;

	for (int I=0; I<4; I++)
	{
		Utils_SetUartBaudrate(&huart7, C_ModemBauds[I]);
		Modem_TimeStamp = HAL_GetTick();
		Modem_SendUartCmd(Cmd->CmdText);
		while ((HAL_GetTick() - Modem_TimeStamp) < 1000)
			if (Modem_CheckResponseText("OK\r\n") != NULL)
			{
				strcpy(IprexCmd, "AT+IPREX=");
				(void)itoa(NC_MODEM_BAUDRATE, &IprexCmd[9], 10);
				char* Src = IprexCmd;
				while (*Src++ != '\0');
				*Src     = '\0';
				*(Src-1) = '\r';
				Modem_SendUartCmd(IprexCmd);
				HAL_Delay(2000);
				(void)HAL_IWDG_Refresh(&hiwdg1);
				Utils_SetUartBaudrate(&huart7, NC_MODEM_BAUDRATE);
				return MODEM_CMD_FINISHED_OK;
			}
		(void)HAL_IWDG_Refresh(&hiwdg1);
	}
	DevStatus[DEVSTATUS_MODEM_STATUS_IDX] = DEVSTATUS_MODEM_UART_FAIL;
	return MODEM_CMD_FINISHED_NOK;
}


uint8_t Modem_OpAtSIMCOMATI(struct Modem_CmdStruct* Cmd)
{
	char* Ptr;
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_FtpsType = '0';
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
				if ((Ptr = (char*)Modem_CheckResponseText("SIM7600M22_B")) != NULL)
				{
					Ptr[14] = '\0';	Ptr[17] = '\0';
					uint32_t FwVal = (atoi(&Ptr[12]) << 8) + atoi(&Ptr[15]);
					if (FwVal >= 0x0403)	Modem_FtpsType = '2';
				}
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCICCID(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Modem_Iccid[0] = 'N';
			Modem_Iccid[1] = 'A';
			Modem_Iccid[2] = '\0';
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal != MODEM_CMD_RUNNING)
			{
				if (RetVal == MODEM_CMD_FINISHED_OK)
				{
					char* Iccid = (char*)(Modem_RxBuf) + 10;
					uint32_t I = 0;
					while (*Iccid != '\r')
						Modem_Iccid[I++] = *Iccid++;
					Modem_Iccid[I] = '\0';
				}
				else
					DevStatus[DEVSTATUS_MODEM_STATUS_IDX] = DEVSTATUS_MODEM_SIM_FAIL;
			}
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCSQ(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	int32_t Rssi, Ber;
	char Tmp[8];
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
			{
				char* Src = (char*)(Modem_RxBuf);
				char* Dest = Modem_RssiBer;
				Dest = Utils_CopyString("RSSI", Dest);
				while (*Src++ != ' ');
				Rssi = 0;
				while (*Src != ',')
					Rssi = Rssi*10 + (uint32_t)((*Src++) - '0');
				if (Rssi < 100)
				{
					Rssi = Rssi*2 - 113;
					if (Rssi == -113)	*Dest++ = '<';
					if (Rssi == -51)	*Dest++ = '>';
				}
				else
				{
					Rssi = Rssi - 216;
					if (Rssi == -116)	*Dest++ = '<';
					if (Rssi == -25)	*Dest++ = '>';
				}
				Ber = (uint32_t)((*++Src) - '0');
				*Dest++ = '=';
				if (Rssi > -25)		Dest = Utils_CopyString("N/A", Dest);
				else
				{
					Dest = Utils_CopyString(itoa(Rssi, Tmp, 10), Dest);
					Dest = Utils_CopyString("dBm(", Dest);
					if (Rssi <= -95) 		{Dest = Utils_CopyString("poor)", Dest); DevStatus[DEVSTATUS_MODEM_STATUS_IDX] = DEVSTATUS_MODEM_SIGNAL_FAIL;}
					else if (Rssi <= -85)	 Dest = Utils_CopyString("fair)", Dest);
					else if (Rssi <= -75)	 Dest = Utils_CopyString("good)", Dest);
					else 					 Dest = Utils_CopyString("excellent)", Dest);
				}
				Modem_RssiInt = (int8_t)Rssi;
				Dest = Utils_CopyString(", BER", Dest);
				if (Ber == 0)	Dest = Utils_CopyString("<0.01%", Dest);
				if (Ber == 1)	Dest = Utils_CopyString("<0.1%", Dest);
				if (Ber == 2)	Dest = Utils_CopyString("<0.5%", Dest);
				if (Ber == 3)	Dest = Utils_CopyString("<1.0%", Dest);
				if (Ber == 4)	Dest = Utils_CopyString("<2.0%", Dest);
				if (Ber == 5)	Dest = Utils_CopyString("<4.0%", Dest);
				if (Ber == 6)	Dest = Utils_CopyString("<8.0%", Dest);
				if (Ber == 7)	Dest = Utils_CopyString(">=8.0%", Dest);
				if (Ber >  7)	Dest = Utils_CopyString("=N/A", Dest);
				*Dest = '\0';
			}
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCGPSINFO(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			strcpy(Modem_GpsString, "N/A");
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
			{
				uint8_t* ResEnd = Modem_CheckResponseText("\r\n\r\nOK");
				*ResEnd = 0;
				if (Modem_RxBuf[13] >= '0')
					strcpy(Modem_GpsString, (char*)(&(Modem_RxBuf[13])));
			}
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCGSOCKCONT(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			strcpy(Cmd->CmdText, "AT+CGSOCKCONT=1,\"IP\",\"");
			strcat(Cmd->CmdText, Modem_Apn);
			strcat(Cmd->CmdText, "\"\r");
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCIMI(struct Modem_CmdStruct* Cmd)
{
	uint32_t I;
	uint8_t RetVal = MODEM_CMD_RUNNING;
	char* ImsiPtr;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
		case 1:
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 2;
			break;
		case 2:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal != MODEM_CMD_RUNNING)
			{
				if (RetVal == MODEM_CMD_FINISHED_OK)
				{
					ImsiPtr = (char*)(Modem_RxBuf) + 2;
					ImsiPtr[5] = '\0';
					for (I=0; I < C_ImsiToApnNumber; I++)
					{
						if (strlen(C_ImsiToApn[I].Imsi) == 3)
							ImsiPtr[3] = '\0';
						if (strcmp(C_ImsiToApn[I].Imsi, ImsiPtr) == 0)
						{
							strcpy(Modem_Apn, C_ImsiToApn[I].Apn);
							break;
						}
					}
				}
				else
					DevStatus[DEVSTATUS_MODEM_STATUS_IDX] = DEVSTATUS_MODEM_SIM_FAIL;
			}
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCREG_Q(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
		case 1:
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 2;
			break;
		case 2:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
			{
				if ((Modem_CheckResponseText("+CREG: 0,1") == NULL) && (Modem_CheckResponseText("+CREG: 0,5") == NULL))
				{
					RetVal = MODEM_CMD_RUNNING;
					Cmd->State = 1;
				}
				else
					Modem_TestStatus = MODEM_TESTSTATUS_NETWORK_REGISTERED;
			}
			if ((RetVal != MODEM_CMD_FINISHED_OK) && (RetVal != MODEM_CMD_RUNNING))
				DevStatus[DEVSTATUS_MODEM_STATUS_IDX] = DEVSTATUS_MODEM_REG_FAIL;
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCMGS(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;

	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("> ", "ERROR", CHECK_RN_OFF);
			if (RetVal == MODEM_CMD_FINISHED_OK)
			{
				RetVal = MODEM_CMD_RUNNING;
				Modem_SendUartCmd((char*)(Cmd->Param));
				Cmd->State = 2;
			}
			break;
		case 2:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			break;
	}
	if ((RetVal != MODEM_CMD_FINISHED_OK) && (RetVal != MODEM_CMD_RUNNING))
		RetVal = MODEM_CMD_SKIP_OK;
	return RetVal;
}


uint8_t Modem_OpAtCCLK_Q(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
			{
				uint32_t TimeSet = Rtc_GetRtcTimeSetStatus();
				if (Rtc_UpdateTimeDate(Modem_CheckResponseText("+CCLK:") + 8) == 0)
					DevStatus[DEVSTATUS_MODEM_STATUS_IDX] = DEVSTATUS_MODEM_NITZ_FAIL;
				else
					Control_SetTimeOnNitz();
				if (Rtc_GetRtcTimeSetStatus() && (TimeSet == 0))
				{
					Rtc_CheckAndUpdateNextWakeUp();
					Rgbled_SetColor(0, 0x000000);
				}
			}
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCFTRANRX(struct Modem_CmdStruct* Cmd)
{
	uint32_t FileReadSize;
	uint8_t  RetVal  = MODEM_CMD_RUNNING;

	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_FtpSizeToSend = Modem_CmdPtr->ParamSec;
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("\r\n>", "ERROR", CHECK_RN_OFF);
			if (RetVal != MODEM_CMD_RUNNING)
			{
				if (RetVal == MODEM_CMD_FINISHED_OK)
				{
					RetVal = MODEM_CMD_RUNNING;
					Modem_CmdUsesIsr = 1;
					Modem_DataBufferFullFlag[0] = 0;
					Modem_DataBufferFullFlag[1] = 0;
					Modem_DataBufferReqSend     = 1;
					Modem_DataBufferPutIndex = 0;
					Modem_DataBufferGetIndex = 0;
					Modem_DataBuffer = Utils_AllocMem(MODEM_DOUBLE_BUFFER_SIZE);
					Cmd->State = 2;
				}
				else
					Modem_SentFileStatus = -1;
			}
			break;
		case 2:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal != MODEM_CMD_RUNNING)
			{
				Modem_CmdUsesIsr = 0;
				Utils_FreeMem(Modem_DataBuffer);
				if (RetVal != MODEM_CMD_FINISHED_OK)
					Modem_SentFileStatus = -1;
			}
			else
			{
				if ((Modem_FtpSizeToSend) && (Modem_DataBufferFullFlag[Modem_DataBufferPutIndex] == 0))
				{
					FileReadSize = File_ReadFileFtp((FIL*)(Modem_CmdPtr->Param),
													Modem_DataBuffer + (MODEM_DOUBLE_BUFFER_SIZE/2)*Modem_DataBufferPutIndex,
													MODEM_DOUBLE_BUFFER_SIZE/2);
					Modem_FtpSizeToSend -= FileReadSize;

					if (Modem_FtpSizeToSend == 0)
						File_CloseFileFtp((FIL*)(Modem_CmdPtr->Param));

					Modem_DataBufferSize[Modem_DataBufferPutIndex] = FileReadSize;
					Modem_DataBufferFullFlag[Modem_DataBufferPutIndex] = 1;
					Modem_DataBufferPutIndex ^= 1;

					if (Modem_DataBufferReqSend)
					{
						Modem_DataBufferReqSend = 0;
						Modem_SendUartFtpFile(&huart7);
					}
				}
			}
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCFTPSSTART(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
			{
				RetVal = MODEM_CMD_RUNNING;
				Cmd->State = 2;
			}
			break;
		case 2:
			RetVal = Modem_CheckResponseTextAndTimeout("+CFTPSSTART: 0", "ERROR", CHECK_RN_ON);
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCFTPSSTOP(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
			{
				RetVal = MODEM_CMD_RUNNING;
				Cmd->State = 2;
			}
			break;
		case 2:
			RetVal = Modem_CheckResponseTextAndTimeout("+CFTPSSTOP: 0", "ERROR", CHECK_RN_ON);
			if ((RetVal != MODEM_CMD_FINISHED_OK) && (RetVal != MODEM_CMD_RUNNING))
				RetVal = MODEM_CMD_SKIP_OK;
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCFTPSLOGIN(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Cmd->CmdText[strlen(Cmd->CmdText) - 2] = Modem_FtpsType;
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
			{
				RetVal = MODEM_CMD_RUNNING;
				Cmd->State = 2;
			}
			break;
		case 2:
			RetVal = Modem_CheckResponseTextAndTimeout("+CFTPSLOGIN: 0", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
				Modem_TestStatus = MODEM_TESTSTATUS_SERVER_LOGIN;
			break;
	}
	if ((RetVal != MODEM_CMD_RUNNING) && (RetVal != MODEM_CMD_FINISHED_OK))
		DevStatus[DEVSTATUS_MODEM_STATUS_IDX] = DEVSTATUS_MODEM_FTPS_FAIL;
	return RetVal;
}


uint8_t Modem_OpAtCFTPSLOGOUT(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK", "ERROR", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_FINISHED_OK)
			{
				RetVal = MODEM_CMD_RUNNING;
				Cmd->State = 2;
			}
			break;
		case 2:
			RetVal = Modem_CheckResponseTextAndTimeout("+CFTPSLOGOUT: 0", "ERROR", CHECK_RN_ON);
			if ((RetVal != MODEM_CMD_FINISHED_OK) && (RetVal != MODEM_CMD_RUNNING))
				RetVal = MODEM_CMD_SKIP_OK;
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCFTPSPUTFILE(struct Modem_CmdStruct* Cmd)
{
	uint8_t  RetVal  = MODEM_CMD_RUNNING;

	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_TimeStamp =  HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("OK\r\n\r\n+CFTPSPUTFILE: 0", "ERROR", CHECK_RN_ON);
			if (RetVal != MODEM_CMD_RUNNING)
			{
				if (RetVal == MODEM_CMD_FINISHED_OK)
					Modem_SentFileStatus = 1;
				else
					Modem_SentFileStatus = -1;
			}
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCFTPSGET(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_GetFileNow >>= 1;
			if (Modem_FilesFoundOnFtpServer & Modem_GetFileNow)
			{
				Modem_Timeout = Cmd->Timeout + HAL_GetTick();
				Modem_DataBuffer     = Utils_AllocMem(NC_MAX_FIRMWARE_SIZE);
				Modem_CircularBuffer = Utils_AllocMem(NC_UART_RX_CIRCULAR_BUFFER_LEN);
				Modem_SendUartFtpsGetCmd(Cmd->CmdText);
				Cmd->State = 1;
				Modem_CopyUartToFirmwareState = 0;
			}
			else
				RetVal = MODEM_CMD_FINISHED_OK;
			break;
		case 1:
			switch (Modem_CopyUartRxToFirmware())
			{
				case 1:
					Modem_UpdateFirmwareState = (Modem_FilesFoundOnFtpServer == MODEM_FIRMWARE_FOUND_ON_FTP_SERVER) ? MODEM_UPDATE_FIRMWARE_CHECKSUM_STATE:MODEM_UPDATE_CNF_WRITE_STATE;
					RetVal = MODEM_CMD_FINISHED_OK;
					break;
				case 20:
					Modem_FirmwareUpdateError(1);
					RetVal = MODEM_CMD_FINISHED_NOK;
					break;
				default:
					if (Modem_CheckTimeout())
					{
						Modem_FirmwareUpdateError(1);
						RetVal = MODEM_CMD_FINISHED_TIMEOUT;
					}
			}
			break;
	}
	return RetVal;
}


uint8_t Modem_OpAtCFTPSDELE(struct Modem_CmdStruct* Cmd)
{
	uint8_t RetVal = MODEM_CMD_RUNNING;
	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("+CFTPSDELE: 0", "N/A", CHECK_RN_ON);
			if (RetVal == MODEM_CMD_RUNNING)	RetVal = Modem_CheckResponseTextAndTimeout("+CFTPSDELE: 9", "N/A", CHECK_RN_ON);
			break;
	}
	return RetVal;
}

uint8_t Modem_OpAtCFTPSLIST(struct Modem_CmdStruct* Cmd)
{
	uint32_t I;
	uint8_t RetVal = MODEM_CMD_RUNNING;
	char FirmwareFileName[] = "fly.bin";
	char CnfFileName[] = "cnf.txt";

	switch (Cmd->State)
	{
		case 0:
			Modem_Timeout = Cmd->Timeout + HAL_GetTick();
			Modem_SendUartCmd(Cmd->CmdText);
			Modem_GetFileNow = 1<<NC_NUMBER_OF_FILES_TO_DOWNLOAD;
			Cmd->State = 1;
			break;
		case 1:
			RetVal = Modem_CheckResponseTextAndTimeout("+CFTPSLIST: 0", "ERROR", CHECK_RN_ON);
			if (RetVal != MODEM_CMD_RUNNING)
			{
				if (RetVal == MODEM_CMD_FINISHED_OK)
				{
					uint32_t DataLength = MODEM_RX_BUFFER_LEN - ((DMA_Stream_TypeDef*)hdma_uart7_rx.Instance)->NDTR;
					uint8_t* Ptr = Modem_RxBuf + (DataLength - 7/*file length*/ - 13/*+CFTPLIST: 0 length*/);
					while (Ptr >= Modem_RxBuf)
					{
						for (I=0; I<7; I++)
							if (Ptr[I] != FirmwareFileName[I]) break;
						if (I == 7)		Modem_FilesFoundOnFtpServer |= MODEM_FIRMWARE_FOUND_ON_FTP_SERVER;
						for (I=0; I<7; I++)
							if (Ptr[I] != CnfFileName[I]) break;
						if (I == 7)		Modem_FilesFoundOnFtpServer |= MODEM_CNF_FOUND_ON_FTP_SERVER;
						Ptr--;
					}
				}
				if (Modem_FilesFoundOnFtpServer == 0)
					Modem_ResetFirmwareUpdateStatus();
			}
			break;
	}
	return RetVal;
}



int8_t Modem_GetSentFileStatus(void)
{
	int8_t Flag = Modem_SentFileStatus;
	if (Flag != 0)	Modem_SentFileStatus = 0;
	return Flag;
}


void Modem_Init()
{
	HAL_UART_RegisterCallback(&huart7, HAL_UART_ERROR_CB_ID, Modem_UartErrorIsr);
	HAL_UART_RegisterCallback(&huart7, HAL_UART_TX_COMPLETE_CB_ID, Modem_SendUartFtpFileIsr);
	Modem_CmdQueueHead	= 0;
	Modem_CmdQueueTail	= 0;
}


void Modem_InitCommands(void)
{
	// Global
	Modem_SendCmd("ATE0");            //echo off
	Modem_SendCmd("AT+SIMCOMATI");    //get firmware version
//	Modem_SendCmd("AT+IFC=2,2");      //enable RTS/ disable CTS lines - default for this modem
	Modem_SendCmd("AT+CATR=1");       //select UART
	Modem_SendCmd("AT+CGPS=1");       //start GPS session
	Modem_SendCmd("AT+CIMI");         //retrieve IMSI from SIM card
	Modem_SendCmd("AT+CICCID");       //retrieve ICCID from SIM card
	Modem_SendCmd("AT+CREG?");        //check is registered in network
	// Network time
	Modem_SendCmd("AT+CTZU=1");       //automatic update clock from network
	Modem_SendCmd("AT+WAIT=5000");
	Modem_SendCmd("AT+CCLK?");        //get current time/date
	// SMS
//	Modem_SendCmd("AT+CMGF=1");       //set SMS in mode text
	// Filesystem
	Modem_SendCmd("AT+FSCD=E:/");     //set default path to E:/
	Modem_SendCmd("AT+FSDEL=*.*");    //delete everything from E:/ (safety measure)
	// GPS
	Modem_SendCmd("AT+CGPSINFO");     //get GPS position
	Modem_SendCmd("AT+CSQ");
}



void Modem_CommandsServer()
{
	uint8_t Status;

	if (Modem_CmdQueueHead != Modem_CmdQueueTail)
	{
		Modem_CmdPtr = &Modem_CmdQueue[Modem_CmdQueueHead];
		Status = Modem_CmdPtr->Op(Modem_CmdPtr);
		switch (Status)
		{
		case MODEM_CMD_FINISHED_OK:
		case MODEM_CMD_SKIP_OK:
			Modem_CmdUsesIsr = 0;
			Modem_CmdQueueHead = (Modem_CmdQueueHead+1) % MODEM_CMD_QUEUE_SIZE;
			break;
		case MODEM_CMD_FINISHED_NOK:
		case MODEM_CMD_FINISHED_TIMEOUT:
			Modem_CmdUsesIsr = 0;
			// implement error management
			Modem_CmdQueueHead = Modem_CmdQueueTail;
			Modem_Error = 1;
			Modem_TestStatus = MODEM_TESTSTATUS_ERROR;
			break;
		}
	}
}


uint8_t Modem_GetModemErrorStatus(void)
{	uint8_t Error = 0;
	if (Modem_Error)
	{
		Error = 1;
		Modem_Error = 0;
	}
	return Error;
}



uint8_t Modem_CommandsServerActivityStatus(void)
{
	return (Modem_CmdQueueHead == Modem_CmdQueueTail) ? 0:1;
}


void Modem_FirmwareUpdateServer()
{
	if (Modem_UpdateFirmwareState != MODEM_UPDATE_FIRMWARE_IDLE_STATE)
	{
		switch (Modem_UpdateFirmwareState)
		{
		case MODEM_UPDATE_CNF_WRITE_STATE:
			Modem_DataBuffer[Modem_FirmwareSize] = 0;
			if (Config_ParseConfigFileImmediateActions((char*)Modem_DataBuffer) == 0)
				File_WriteFileNwm("cnf.txt", (char*)Modem_DataBuffer, Modem_FirmwareSize);
			Modem_SendCmd("AT+WAIT=5000");
			Modem_SendCmd("AT+CFTPSDELE=\"firmware/cnf.txt\"");
			Modem_SendCmd("AT+WAIT=5000");
			Modem_FirmwareUpdateError(0);
			break;
		case MODEM_UPDATE_FIRMWARE_CHECKSUM_STATE:
			Modem_FirmwareCks = Utils_CheckSum8(Modem_DataBuffer, Modem_FirmwareSize-1);
			if (Modem_FirmwareCks == Modem_DataBuffer[Modem_FirmwareSize-1])
				Modem_UpdateFirmwareState = MODEM_UPDATE_FIRMWARE_ERASE_FLASH_STATE;
			else
				Modem_FirmwareUpdateError(1);
			break;
		case MODEM_UPDATE_FIRMWARE_ERASE_FLASH_STATE:
			if (Flash_EraseUnusedBank())
				Modem_UpdateFirmwareState = MODEM_UPDATE_FIRMWARE_PROG_FLASH_STATE;
			else
				Modem_FirmwareUpdateError(1);
			break;
		case MODEM_UPDATE_FIRMWARE_PROG_FLASH_STATE:
			if (Flash_ProgrammUnusedBank((uint32_t)Modem_DataBuffer, Modem_FirmwareSize))
			{
				// request to delete firmware on the ftp server
				Modem_SendCmd("AT+WAIT=5000");
				Modem_SendCmd("AT+CFTPSDELE=\"firmware/fly.bin\"");
				Modem_SendCmd("AT+WAIT=5000");
				// release used memory for firmware update
				Modem_FirmwareUpdateError(0);
			}
			else
				Modem_FirmwareUpdateError(1);
			break;
		}
	}
}


void Modem_PowerServer()
{
	switch (Modem_PowerState)
	{
	case MODEM_POWER_WAIT_ON_STATE:
		if ((HAL_GetTick() - Modem_PowerTimestamp) > NC_MODEM_POWERKEY_ON_TO_READY_TIME)
		{
			// Set the power state to ON
			Modem_PowerState = MODEM_POWER_ON_STATE;
			// Send initialization commands
			Modem_InitCommands();
			Modem_TestStatus = MODEM_TESTSTATUS_CONFIG_CMDS;
		}
		break;
	}
}

uint8_t Modem_GetPowerServerState(void)
{
	uint8_t Flag = 0xFF;
	if (Modem_PowerState == MODEM_POWER_OFF_STATE)
		Flag = 0;
	else
		if (Modem_PowerState == MODEM_POWER_ON_STATE)
			Flag = 1;
	return Flag;
}

void Modem_Server(void)
{
	Modem_PowerServer();
	Modem_CommandsServer();
	Modem_FirmwareUpdateServer();
}


void Modem_SendCmd(char* Text)
{
	uint32_t I, J, K;
	char* Ptr;
	const struct Modem_OpStruct* Modem_OpPtr;
	struct Modem_CmdStruct* CmdPtr;

	if (Modem_PowerState == MODEM_POWER_ON_STATE)
		for (I=0; I<Modem_OpListNumber; I++)
		{
			Modem_OpPtr = &Modem_OpList[I];
			for (J=0, K=2; ; J++, K++)
				if ((Text[K] != Modem_OpPtr->CmdTypeText[J]) || (Text[K] == '\0') || (Text[K] == '=') || (Modem_OpPtr->CmdTypeText[J] == '\0'))
					break;
			if ((Modem_OpPtr->CmdTypeText[J] == '\0') && ((Text[K] == '\0') || (Text[K] == '=')))
			{
				CmdPtr = &Modem_CmdQueue[Modem_CmdQueueTail];
				Ptr = CmdPtr->CmdText;
				/* Copy command in queue buffer */
				while (*Text != '\0')
					*Ptr++ = *Text++;
				*Ptr++ = '\r';
				*Ptr   = '\0';

				CmdPtr->Op       = Modem_OpPtr->Op;
				CmdPtr->Param    = Modem_CmdParam;
				CmdPtr->ParamSec = Modem_CmdParamSec;
				CmdPtr->Timeout  = Modem_OpPtr->Timeout;
				CmdPtr->State    = 0;

				Modem_CmdQueueTail = (Modem_CmdQueueTail+1) % MODEM_CMD_QUEUE_SIZE;
				break;
			}
		}
}


void Modem_SendSms(char* Src, char* PhoneNumber)
{
	char CmdText[32];

	Modem_SendCmd("AT+CMGF=1");       //set SMS in mode text
	strcpy(CmdText, "AT+CMGS=\"");
	strcat(CmdText, PhoneNumber);
	strcat(CmdText, "\"");
	Modem_CmdParam = (uint32_t)Src;
	Modem_SendCmd(CmdText);
}


void Modem_SendFileFtp(char* FileNameLocal, char* FileNameFtp, uint32_t Action)
{
	char CmdText[256];
	char Size[16];
	int32_t Pos;

	Modem_FileToSend = File_OpenReadFileFtp(FileNameLocal);
	Modem_CmdParam	 = (uint32_t)Modem_FileToSend;

	if (Modem_FileToSend == NULL)
	{
		if (Action)		 Modem_CmdParamSec = 20;
		else			{Modem_SentFileStatus = 3; return;}
	}
	else
	{
		Modem_CmdParamSec = (int32_t)f_size(Modem_FileToSend);
		if (Modem_CmdParamSec == 0)
		{
			if (Action)	{Modem_CmdParamSec = 20; Modem_FileToSend = NULL;}
			else		{Modem_SentFileStatus = 2; return;}
		}
	}

	Pos = (int32_t)strlen(FileNameFtp) - 1;
	while(Pos >= 0)
	{
		if (FileNameFtp[Pos] == '/')
			break;
		Pos--;
	};
	Pos++;

	strcpy(CmdText, "AT+CFTRANRX=\"e:/");
	strcat(CmdText, &FileNameFtp[Pos]);
	strcat(CmdText, "\",");
	strcat(CmdText, itoa (Modem_CmdParamSec, Size, 10));
	Modem_SendCmd(CmdText);

	strcpy(CmdText, "AT+CFTPSPUTFILE=\"");
	strcat(CmdText, FileNameFtp);
	strcat(CmdText, "\",3");
	Modem_SendCmd(CmdText);

	strcpy(CmdText, "AT+FSDEL=");
	strcat(CmdText, &FileNameFtp[Pos]);
	Modem_SendCmd(CmdText);
}


void Modem_OpenFtpConnection(void)
{
	// Set APN
	Modem_SendCmd("AT+CGSOCKCONT");			// command parameters + missing APN will be build at command execution
	Modem_SendCmd("AT+CSOCKSETPN=1");       // select this socket
	Modem_SendCmd("AT+CFTPSSTART");
	Modem_SendCmd("AT+CFTPSSINGLEIP=1");
	// Build LOGIN string
	char LoginString[128] = "AT+CFTPSLOGIN=\"";
	strcat(LoginString, C_FtpAddr);
	strcat(LoginString, "\",21,\"Dev");
	strcat(LoginString, Sensor_Nfc_GetDevNumber());
	strcat(LoginString, "@flytrap.ro\",\"");
	char FtpPass[16];
	Sensor_Nfc_PowerUp();
	Sensor_Nfc_GetFtpPass(FtpPass);
	Sensor_Nfc_PowerDown();
	FtpPass[12] = '\0';
	strcat(LoginString, FtpPass);
	strcat(LoginString, "\",0");
	Modem_SendCmd(LoginString);  //login to FTP server
}


void Modem_CloseFtpConnection(void)
{
	Modem_SendCmd("AT+WAIT=4000");
	Modem_SendCmd("AT+CFTPSLOGOUT");	// logout to FTP server
	Modem_SendCmd("AT+CFTPSSTOP");		// stop ftp server
}


void Modem_CheckFirmwareUpdate(void)
{
	Modem_FirmwareUpdateType	= MODEM_UPDATE_CHECK_STATUS;
	Modem_SendCmd("AT+CFTPSLIST=\"firmware/\"");
	Modem_SendCmd("AT+CFTPSGET=\"firmware/cnf.txt\",0,1");
	Modem_SendCmd("AT+WAIT=5000");
	Modem_SendCmd("AT+CFTPSGET=\"firmware/fly.bin\",0,1");
};


uint8_t Modem_GetTestStatus(void)
{
	return Modem_TestStatus;
}

char*   Modem_GetIccid(void)
{
	return Modem_Iccid;
}

char*   Modem_GetRssiBer(void)
{
	return Modem_RssiBer;
}
int8_t  Modem_GetRssiInt(void)
{
	return Modem_RssiInt;
}

char* Modem_GpsResult(void)
{
	if ((Modem_CommandsServerActivityStatus() == 0) && (Modem_GpsString[0] > '9'))
	{
		Modem_SendCmd("AT+WAIT=2000");
		Modem_SendCmd("AT+CGPSINFO");
	}
	return Modem_GpsString;
}

uint32_t Modem_GetFtpsType(void)
{
	return (Modem_FtpsType - '0');
}
