/**
  ******************************************************************************
  * @file    tcp_echoclient.c
  * @author  MCD Application Team
  * @version V1.0.0
  * @date    31-October-2011 
  * @brief   tcp echoclient application using LwIP RAW API
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/tcp.h"
#include "stm32f10x.h"
#include "memp.h"
#include <stdio.h>
#include <string.h>
#include "stm32f10x.h"
#include "netconf.h"
#include "stm32_config.h"

static u8  TcpClient_buff[200]="";
static u32 TcpClient_len = 0;
u8  TcpClient_Link = 0; //连接状态标识

#if LWIP_TCP

__IO uint32_t message_count=0;
struct tcp_pcb *echoclient_pcb = NULL;
struct tcp_pcb *Socket_Client = NULL;
struct echoclient *rjes = NULL;
/* ECHO protocol states */
enum echoclient_states
{
	ES_NOT_CONNECTED = 0,
	ES_CONNECTED,
	ES_RECEIVED,
	ES_CLOSING,
};

/* structure to be passed as argument to the tcp callbacks */
struct echoclient
{
  enum echoclient_states state; /* connection status */
  struct tcp_pcb *pcb;          /* pointer on the current tcp_pcb */
  struct pbuf *p_tx;            /* pointer on pbuf to be transmitted */
};

/* Private function prototypes -----------------------------------------------*/
static err_t tcp_echoclient_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
static void tcp_echoclient_connection_close(struct tcp_pcb *tpcb, struct echoclient * es);
static err_t tcp_echoclient_poll(void *arg, struct tcp_pcb *tpcb);
static err_t tcp_echoclient_connected(void *arg, struct tcp_pcb *tpcb, err_t err);
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);
static void tcp_client_error(void *arg,err_t err);

/*********************************************************
  * @function  tcp_echoclient_connect
  * @role      Connects to the TCP echo server
  * @input     None
  * @output    None
  * @return    None
  ********************************************************/
void tcp_echoclient_connect(void)
{
	struct ip_addr DestIPaddr;
	/* 建立新的TCP连接,正常建立了连接标志，返回建立的 pcb  */
	echoclient_pcb = tcp_new(); 
	if (echoclient_pcb != NULL)
	{
		IP4_ADDR( &DestIPaddr, 192, 168, 1, 112 );
		/*
		 *连接到远程主机，并发送打开连接的最初的SYN段,如果当连接正确建立，
		 *那么它会直接调用第四个参数指定的函数(connected参数)。 
		 *相反地，如果连接不能够被正确建立，这原因可能是远程主机拒绝连接，
		 *也可能是远程主机不应答，无论是什么原因，都会调用connected函数来设置相应的参数err
		 */
		DEBUG();
		tcp_connect(echoclient_pcb,&DestIPaddr,8080,tcp_echoclient_connected);
	}
	else DEBUG();
	DEBUG();
}

/*********************************************************
  * @brief Function called when TCP connection established
  * @param tpcb: pointer on the connection contol block
  * @param err: when connection correctly established err should be ERR_OK 
  * @retval err_t: returned error 
  ********************************************************/
static err_t tcp_echoclient_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
	struct echoclient *es = NULL;
	if (err == ERR_OK)   
	{
		/* allocate structure es to maintain tcp connection informations */
		es = (struct echoclient *)mem_malloc(sizeof(struct echoclient));
		if (es != NULL)
		{
			es->state = ES_CONNECTED;
			es->pcb = tpcb;
			es->p_tx=NULL; 
			TcpClient_Link = 1;

			tcp_arg(tpcb, es);                     //程序状态，将状态传递给所有回调函数，es为需要传递给回调函数的参数
			tcp_recv(tpcb, tcp_echoclient_recv);   //初始化接收回调函数
			tcp_err(tpcb,tcp_client_error); 	   //初始化tcp_err()回调函数
			tcp_sent(tpcb,tcp_client_sent);		   //初始化LwIP的tcp_sent回调功能
			tcp_poll(tpcb, tcp_echoclient_poll, 1);//指定轮询的时间间隔以及轮询应用程序时应该调用的回调函数 （单位：秒）
			Socket_Client = tpcb;
			rjes = es;				
			return ERR_OK;
		}
		else
		{
			tcp_echoclient_connection_close(tpcb, es); //关闭连接
			return ERR_MEM;  //返回内存分配错误信息
		}
	}
	else
	{
		tcp_echoclient_connection_close(tpcb, es); //关闭连接
	}
	return err;
}
    
//lwIP tcp_sent的回调函数(当我发送数据时，会调用到这个函数)
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
	DEBUG();
	return ERR_OK;
}

//lwIP tcp_err函数的回调函数
static void tcp_client_error(void *arg,err_t err)
{  
	struct echoclient *es;
	LWIP_UNUSED_ARG(err);
	es = (struct echoclient *)arg;
	if (es != NULL)
	{
		Socket_Client->state = CLOSE_WAIT;
		rjes->state = ES_CLOSING; /* 关闭远程连接 */
		tcp_echoclient_connection_close(Socket_Client, es);
	}
	DEBUG();
} 

void tcp_client_close(void)
{
	if (rjes != NULL)
	{
		TcpClient_Link = 0;
		Socket_Client->state = CLOSE_WAIT;
		rjes->state = ES_CLOSING; /* 关闭远程连接 */
		tcp_echoclient_connection_close(Socket_Client, rjes);
	}
}
	
/*********************************************************
  * @brief tcp_receiv callback
  * @param arg: argument to be passed to receive callback 
  * @param tpcb: tcp connection control block 
  * @param err: receive error code 
  * @retval err_t: retuned error  
  ********************************************************/
static err_t tcp_echoclient_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{ 
	u32 i = 0;
	struct echoclient *es;
	err_t ret_err;
	uint8_t *Data;
	LWIP_ASSERT("arg != NULL",arg != NULL);
	es = (struct echoclient *)arg;
	if (p == NULL)//如果从服务器接收到空的数据帧就关闭连接
	{
		TcpClient_Link = 0;
		es->state = ES_CLOSING; /* 关闭远程连接 */
		if(es->p_tx == NULL)
		{
			//服务器断开后，会跳到这里面
			tcp_echoclient_connection_close(tpcb, es); /* 发送完成，关闭连接 */
		}
		ret_err = ERR_ABRT;//连接终止
	}   
	/* else : a non empty frame was received from echo server but for some reason err != ERR_OK */
	else if(err != ERR_OK)
	{
		if (p != NULL) {
			pbuf_free(p);
		}
		ret_err = err;
	}
	else if(es->state == ES_CONNECTED)//当处于连接状态时
	{
		if(p)//当处于连接状态并且接收到的数据不为空时
		{
			message_count++;
			//printf("Recv:[%d][%s]\r\n",p->tot_len, p->payload);
			if(!TcpClient_len)
			{
				TcpClient_len = p->tot_len;
				Data = (uint8_t*)p->payload;
				for(i = 0;(i<sizeof(TcpClient_buff)-1) && (i < TcpClient_len) ;i++)
				{
					TcpClient_buff[i] = Data[i];
				}		
				TcpClient_buff[i] = '\0';
			}	
			tcp_recved(tpcb,p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
			pbuf_free(p);
			ret_err = ERR_OK;		
		}

	}
	else
	{
		tcp_recved(tpcb, p->tot_len);//用于获取接收数据,通知LWIP可以获取更多数据
		es->p_tx=NULL;
		pbuf_free(p);
		ret_err = ERR_OK;
	}
	return ret_err;
}

/**
  * @brief  This function implements the tcp_poll callback function
  * @param  arg: pointer on argument passed to callback
  * @param  tpcb: tcp connection control block
  * @retval err_t: error code
  */
static err_t tcp_echoclient_poll(void *arg, struct tcp_pcb *tpcb)
{
	err_t ret_err;
	struct echoclient *es;
	es = (struct echoclient*)arg;
	DEBUG();
	if (es != NULL)
	{
		DEBUG();
		if ((es->p_tx == NULL) && (es->state == ES_CLOSING))
		{
			/* close tcp connection */
			tcp_echoclient_connection_close(tpcb, es);
		}
		ret_err = ERR_OK;
	}
	else
	{
		DEBUG();
		tcp_abort(tpcb);//终止连接,删除pcb控制块
		ret_err = ERR_ABRT;
	}
	return ret_err;
}

/**
  * @brief This function is used to close the tcp connection with server
  * @param tpcb: tcp connection control block
  * @param es: pointer on echoclient structure
  * @retval None
  */
static void tcp_echoclient_connection_close(struct tcp_pcb *tpcb, struct echoclient * es )
{
	/* remove callbacks */
	//tcp_abort(tpcb);//终止连接,删除pcb控制块
	tcp_arg(tpcb,NULL); 
	tcp_recv(tpcb, NULL);
	tcp_sent(tpcb, NULL);
	tcp_err(tpcb,NULL);	
	tcp_poll(tpcb, NULL,0);
	if (es != NULL)
	{
		mem_free(es);
	}
	/* close tcp connection */
	tcp_close(tpcb);
}

#endif /* LWIP_TCP */

u32 TCP_Client_Recv(u8 *pOut, u32 len)
{
	u32 ret = 0;
	u32 i = 0;
	if(TcpClient_len)
	{
		for(i = 0;(i<TcpClient_len) && (i< len);i++)
		{
			pOut[i] = TcpClient_buff[i];
		}
		pOut[i] = '\0';	
	}
	ret = TcpClient_len;
	TcpClient_len = 0;
	return ret;
}


s8 TCP_Client_Send(u8 *data, u32 len)
{
	s8 ret = -1;
	err_t wr_err = ERR_OK;
	wr_err = tcp_write(Socket_Client, (char*)data, len, 1);
	if (wr_err == ERR_OK) ret = 0;
	tcp_output(Socket_Client); 
	return ret;
}


/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
