/******************************************************************************
版权所有：	
文件名称：	net_104link.c
文件版本：	01.01
创建作者:	   sunxi
创建日期：	2014-06-12
功能说明：	网络级联接口
其它说明：
修改记录：
*/
#ifdef CPU_FUXI

/*------------------------------- 头文件 --------------------------------------
*/
#include "head.h"
#include <pthread.h> 
#include <sys/prctl.h>
/*------------------------------- 宏定义 --------------------------------------
*/
#define NET_104LINK_BUF_SIZE   16384

/*------------------------------ 类型结构 -------------------------------------
*/


/*------------------------------ 全局变量 -------------------------------------
*/
// static struct task_struct * g_ts_104link[CFG_LINK_104_NUM];
static pthread_t g_ts_104link[CFG_LINK_104_NUM] = {0x00};
static SOCKET g_104link_client[CFG_LINK_104_NUM];

static unsigned char g_net_104link_buf_frame[CFG_LINK_104_NUM][NET_RECV_MAX];  // 以太网接收缓冲区
static unsigned char g_net_104link_buf[CFG_LINK_104_NUM][NET_104LINK_BUF_SIZE];  // 以太网接收缓冲区
static struct rt_fifo	g_net_104link_fifo[CFG_LINK_104_NUM];

static int g_net_104link_close_flag[CFG_LINK_104_NUM];
static int g_net_104link_exit_flag[CFG_LINK_104_NUM];
// static struct completion g_exit_completion_net_104link[CFG_LINK_104_NUM];


/*------------------------------ 函数声明 -------------------------------------
*/
int _net_104link_thread(int net);


/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/
//	 TODO: 需要确认是否需要重新初始化
int net_104link_init(void)
{
#if 1
//	char buf[32];
//	struct sched_param sp;
//	pid_t pid;
	int net;
	int ret;
	
	// 运行应用104link线程
	for(net=0; net<CFG_LINK_104_NUM; net++)
	{
		g_104link_client[net] = RT_SOCKET_ERR;
#if 0
		init_completion(&g_exit_completion_net_104link[net]);
		
		sprintf(buf,"net_104link_%d",net);
		pid = kernel_thread((int (*)(void *))_net_104link_thread, (void *)net,CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
		g_ts_104link[net] = find_task_by_vpid(pid);
		
		if(IS_ERR(g_ts_104link[net]))
		{
			rt_printf("ts=%p\r\n",g_ts_104link[net]);
			g_ts_104link[net] = NULL;
			return -1;
		}

		// 设置调度策略和优先级
		sp.sched_priority = 8;
		sched_setscheduler(g_ts_104link[net],SCHED_FIFO,&sp);
		// 设置进程名字
		set_task_comm(g_ts_104link[net], buf);
#else

		ret = pthread_create(&g_ts_104link[net], NULL, (void *)_net_104link_thread, (void *)net);
		if(ret)
		{
			rt_printf("net = %d, ret = %d, err = %s\r\n", net, ret, strerror(ret));
		}
#endif
	}
	
#endif
	return 0;
}

 
int net_104link_exit(void)
{
	int net;
	
	for(net=0; net<CFG_LINK_104_NUM; net++)
	{
		if(g_ts_104link[net])
		{
			//kthread_stop(g_ts_104link);
			g_net_104link_exit_flag[net] = 1;
			pthread_join(g_ts_104link[net], NULL);
			g_ts_104link[net] = 0;
			// wait_for_completion(&g_exit_completion_net_104link[net]);
		}
	}

	return 0;
}
 

int net_104link_send(int net,unsigned char *buf,int len)
{
	if(g_104link_client[net] == RT_SOCKET_ERR)
	{
		return 0;
	}
	return	rt_send(g_104link_client[net],buf,len,0);
}

void net_104link_close(int net)
{
	if(g_104link_client[net] != RT_SOCKET_ERR)	
	{
		g_net_104link_close_flag[net] = 1;
	}
}

int net_104link_is_connect(int net)
{
	if((g_104link_client[net] == RT_SOCKET_ERR) || g_net_104link_close_flag[net])
	{
		return 0;
	}

	return 1;
}


/*------------------------------ 内部函数 -------------------------------------
内部函数以下划线‘_’开头，不需要检查参数的合法性.
*/
int _net_104link_thread(int net)
{
	struct sockaddr_in server_addr;

	struct timeval timeout;
	struct linger  ling;

	SOCKET s; 

	int len,ret;
	unsigned int ip_server,link_count;
	int recv_timeout;
	int recv_timeout_1;
	unsigned char buf;
	unsigned char *ip;
	unsigned int tmp_ip;
	unsigned char tmp_tnet[32];
//	int net = *pnet;

	sprintf(tmp_tnet,"net_104link_%d",net);
	prctl(PR_SET_NAME, tmp_tnet);
	while(1)
	{
BEGIN:
		link_count = 0;
		// 检查级联IP是否设置
		while(1)
		{
			ip_server = tRunPara.ip104Link[net];
			if(ip_server)
			{
				break;
			}
			
			// 检查是否退出
			if(g_net_104link_exit_flag[net])
			{
//				rt_printf_time("net_104link_thread exit.\r\n");
				// complete_and_exit(&g_exit_completion_net_104link[net], 0);
				return 0;
			}
		
			// 1秒钟检查一次
			// sunxi 20191025 这里有八个级联线程，为了不让退出时等那么久，把每一次的休眠时间改为200毫秒 msleep(1000);
			msleep(200);
		}
		tmp_ip = ip_server; 	// modify xxxxxx 解决am335x打印的ip地址错误的问题
		swap32((void*)&tmp_ip);
		ip = (BYTE*)&tmp_ip;			
		rt_printf_time("级联服务器开始(%d):ip=%d.%d.%d.%d!\r\n",net,ip[0],ip[1],ip[2],ip[3]);


		// 创建socket
		s = rt_socket(AF_INET, SOCK_STREAM, 0);
		if (s == RT_SOCKET_ERR)
		{
			RT_PRINTF_POSITION();
			return -1;
		}

		// 服务器端填充sockaddr结构
		memset (&server_addr, 0, sizeof (struct sockaddr_in));
		server_addr.sin_family = AF_INET;
		server_addr.sin_addr.s_addr = htonl (ip_server);
		server_addr.sin_port = htons (NET_104_PORT);

		// 连接服务器，没有连接成功不退出
		while(1)
		{
			ret = rt_connect(s,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in));

			// 检查是否退出
			if(g_net_104link_exit_flag[net])
			{
				// 关闭客户端
				g_104link_client[net] = RT_SOCKET_ERR;
				rt_socket_close (s);
				
				rt_printf_time("net_104link_thread exit(rt_recv).\r\n");
				// complete_and_exit(&g_exit_completion_net_104link[net], 0);
				return 0;
			}

			// 检查服务器是否变更
			if(ip_server != tRunPara.ip104Link[net])
			{
				// 关闭客户端
				g_104link_client[net] = RT_SOCKET_ERR;
				rt_socket_close (s);

				tmp_ip = tRunPara.ip104Link[net];	// modify xxxxxx 解决am335x打印的ip地址错误的问题
				swap32((void*)&tmp_ip);
				ip = (BYTE*)&tmp_ip;
				rt_printf_time("级联服务器变更(%d):ip=%d.%d.%d.%d!\r\n",net,ip[0],ip[1],ip[2],ip[3]);
			
				goto BEGIN;
			}

			if (ret == 0)
			{
				g_net_104link_close_flag[net] = 0;
				g_link104_init_ok[net] = 0; 	// 新连接，重新初始化
				g_104link_client[net] = s;

				link_count++;
				rt_printf_time("级联服务器成功(通道=%d,连接计数=%d):ip=%d.%d.%d.%d!\r\n",net,link_count,ip[0],ip[1],ip[2],ip[3]);
				break;
			}

			// 1秒钟连接一次
			msleep(1000);
		}

		// 设置接收超时
		timeout.tv_sec	= 0;
		timeout.tv_usec = 100000;
		rt_setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout ,sizeof(struct timeval));
		
		// 设置关闭延时
		ling.l_onoff = 1;
		ling.l_linger = 0;
		rt_setsockopt(s,SOL_SOCKET,SO_LINGER,(char *)&ling ,sizeof(struct linger));

		// 初始化FIFO
		rt_fifo_init(&g_net_104link_fifo[net],g_net_104link_buf[net],NET_104LINK_BUF_SIZE);

		// 处理接收
		recv_timeout = 0;
		recv_timeout_1 = 0;
		while(1)
		{
			len = rt_recv (s, g_net_104link_buf_frame[net], NET_RECV_MAX, 0);

			// 检查是否退出
			if(g_net_104link_exit_flag[net]) 
			{
				// 关闭客户端
				g_104link_client[net] = RT_SOCKET_ERR;
				rt_socket_close (s);
				
				rt_printf_time("net_104link_thread exit(rt_recv).\r\n");
				// complete_and_exit(&g_exit_completion_net_104link[net], 0);
				return 0;
			}
			
			// 检查服务器是否变更
			if(ip_server != tRunPara.ip104Link[net])
			{
				// 关闭客户端
				g_104link_client[net] = RT_SOCKET_ERR;
				rt_socket_close (s);
				
				rt_printf_time("级联服务器变更(%d):ip=%d.%d.%d.%d!\r\n",net,ip[0],ip[1],ip[2],ip[3]);
				goto BEGIN;
			}

			// 应用程序主动关闭
			if(g_net_104link_close_flag[net])
			{
				break;
			
			}

			//if(len == -EAGAIN)
			if((len == -EAGAIN)||(len==-1))//modify by sunxi: 20220906 接收不到数据也会返回-1
			{
				// 超时
				recv_timeout++;
				if(recv_timeout>300)  // 超时30s，端口未收到数据，关闭端口
				{
					rt_printf_time("104级联30S超时关闭(net=%d)\r\n",net);
					break;
				}
			}
			else if (len <= 0)
			{
				recv_timeout_1++;//sunxi for test
				if(recv_timeout_1>30)
				{
					//调用recv时，如果返回0，则表示对端关闭；返回负数，表示出错
					rt_printf_time ("104级联关闭(net=%d,ret=%d)\r\n",net,len);
					msleep(100);
					break;
				}
			}
			else
			{
				rt_fifo_put(&g_net_104link_fifo[net],g_net_104link_buf_frame[net],len);
				recv_timeout=0; 	//超时计数器清零
				recv_timeout_1 = 0;
				if(net<CFG_ETH_NUM_MAX)
					g_run_stu.led[CFG_UART_NUM_MAX*2+net]=1;
			}
			
			// 接收数据
			while(g_net_104link_data[net] == 0)
			{
				if(rt_fifo_get(&g_net_104link_fifo[net],&buf,1))
				{
					lnk_104_recv(net,buf);
				}
				else
				{
					break;
				}
			}
		}

		// 关闭客户端socket
		g_104link_client[net] = RT_SOCKET_ERR;
		rt_socket_close (s);
	//	rt_printf_time ("net_104link_thread:rt_recv(ret=%d)\r\n",len);
	}

	// 理论上，程序不会运行到这儿
	return 0;
}

#else

/*------------------------------- 头文件 --------------------------------------
*/
#include "linux/delay.h"
#include "head.h"
 
/*------------------------------- 宏定义 --------------------------------------
*/
#define NET_104LINK_BUF_SIZE   16384

/*------------------------------ 类型结构 -------------------------------------
*/


/*------------------------------ 全局变量 -------------------------------------
*/
static struct task_struct * g_ts_104link[CFG_LINK_104_NUM];
static SOCKET g_104link_client[CFG_LINK_104_NUM];

static unsigned char g_net_104link_buf_frame[CFG_LINK_104_NUM][NET_RECV_MAX];  // 以太网接收缓冲区
static unsigned char g_net_104link_buf[CFG_LINK_104_NUM][NET_104LINK_BUF_SIZE];  // 以太网接收缓冲区
static struct rt_fifo	g_net_104link_fifo[CFG_LINK_104_NUM];

static int g_net_104link_close_flag[CFG_LINK_104_NUM];
static int g_net_104link_exit_flag[CFG_LINK_104_NUM];
static struct completion g_exit_completion_net_104link[CFG_LINK_104_NUM];


/*------------------------------ 函数声明 -------------------------------------
*/
int _net_104link_thread(int net);


/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/
//   TODO: 需要确认是否需要重新初始化
int net_104link_init(void)
{
	char buf[32];
	struct sched_param sp;
	pid_t pid;
	int net;
	
	// 运行应用104link线程
	for(net=0; net<CFG_LINK_104_NUM; net++)
	{
		g_104link_client[net] = RT_SOCKET_ERR;
		init_completion(&g_exit_completion_net_104link[net]);	
		
		sprintf(buf,"net_104link_%d",net);
		pid = kernel_thread((int (*)(void *))_net_104link_thread, (void *)net,CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
		g_ts_104link[net] = find_task_by_vpid(pid);
		
		if(IS_ERR(g_ts_104link[net]))
		{
			printk("ts=%p\r\n",g_ts_104link[net]);
			g_ts_104link[net] = NULL;
			return -1;
		}

		// 设置调度策略和优先级
		sp.sched_priority = 8;
		sched_setscheduler(g_ts_104link[net],SCHED_FIFO,&sp);
		// 设置进程名字
		set_task_comm(g_ts_104link[net], buf);
	}
	
	return 0;
}

 
int net_104link_exit(void)
{
	int net;
	
	for(net=0; net<CFG_LINK_104_NUM; net++)
	{
		if(g_ts_104link[net] != NULL)
		{
			//kthread_stop(g_ts_104link);
			g_net_104link_exit_flag[net] = 1;
			wait_for_completion(&g_exit_completion_net_104link[net]);
		}
	}

	return 0;
}
 

int net_104link_send(int net,unsigned char *buf,int len)
{
	if(g_104link_client[net] == RT_SOCKET_ERR)
	{
		return 0;
	}
	return	rt_send(g_104link_client[net],buf,len,0);
}

void net_104link_close(int net)
{
	if(g_104link_client[net] != RT_SOCKET_ERR)	
	{
		g_net_104link_close_flag[net] = 1;
	}
}

int net_104link_is_connect(int net)
{
	if((g_104link_client[net] == RT_SOCKET_ERR) || g_net_104link_close_flag[net])
	{
		return 0;
	}

	return 1;
}


/*------------------------------ 内部函数 -------------------------------------
内部函数以下划线‘_’开头，不需要检查参数的合法性.
*/
int _net_104link_thread(int net)
{
	struct sockaddr_in server_addr;

	struct linger  ling;

	SOCKET s; 

	int len,ret;
	unsigned int ip_server,link_count;
	unsigned int recv_timeout;
	unsigned char buf;
	unsigned char *ip;
	unsigned int tmp_ip;
//	int net = *pnet;

	while(1)
	{
BEGIN:
		link_count = 0;
		// 检查级联IP是否设置
		while(1)
		{
			ip_server = tRunPara.ip104Link[net];
			if(ip_server)
			{
				break;
			}
			
			// 检查是否退出
			if(g_net_104link_exit_flag[net])
			{
//				rt_printf_time("net_104link_thread exit.\r\n");
				complete_and_exit(&g_exit_completion_net_104link[net], 0);	
				return 0;
			}
		
			// 1秒钟检查一次
			// sunxi 20191025 这里有八个级联线程，为了不让退出时等那么久，把每一次的休眠时间改为200毫秒 msleep(1000);
			msleep(200);
		}
		tmp_ip = ip_server;		// modify xxxxxx 解决am335x打印的ip地址错误的问题
		Swap_32((void*)&tmp_ip);
		ip = (BYTE*)&tmp_ip;	
		rt_printf_time("级联服务器开始(%d):ip=%d.%d.%d.%d!\r\n",net,ip[0],ip[1],ip[2],ip[3]);


		// 创建socket
		s = rt_socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0);
		if (s == RT_SOCKET_ERR)
		{
			RT_PRINTF_POSITION();
			return -1;
		}

		// 服务器端填充sockaddr结构
		memset (&server_addr, 0, sizeof (struct sockaddr_in));
		server_addr.sin_family = AF_INET;
		server_addr.sin_addr.s_addr = htonl (ip_server);
		server_addr.sin_port = htons (NET_104_PORT);

		// 连接服务器，没有连接成功不退出
		while(1)
		{
			ret = rt_connect(s,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in));
			
			// 检查是否退出
			if(g_net_104link_exit_flag[net])
			{
				// 关闭客户端
				g_104link_client[net] = RT_SOCKET_ERR;
				rt_socket_close (s);
				
				rt_printf_time("net_104link_thread exit(rt_recv).\r\n");
				complete_and_exit(&g_exit_completion_net_104link[net], 0);	
				return 0;
			}

			// 检查服务器是否变更
			if(ip_server != tRunPara.ip104Link[net])
			{
				// 关闭客户端
				g_104link_client[net] = RT_SOCKET_ERR;
				rt_socket_close (s);
				tmp_ip = tRunPara.ip104Link[net];	// modify xxxxxx 解决am335x打印的ip地址错误的问题
				Swap_32((void*)&tmp_ip);
				ip = (BYTE*)&tmp_ip;
				rt_printf_time("级联服务器变更(%d):ip=%d.%d.%d.%d!\r\n",net,ip[0],ip[1],ip[2],ip[3]);
			
				goto BEGIN;
			}

			if (ret == 0)
			{
				g_net_104link_close_flag[net] = 0;
				g_link104_init_ok[net] = 0;		// 新连接，重新初始化
				g_104link_client[net] = s;

				link_count++;
				rt_printf_time("级联服务器成功(通道=%d,连接计数=%d):ip=%d.%d.%d.%d!\r\n",net,link_count,ip[0],ip[1],ip[2],ip[3]);
				break;
			}

			// 1秒钟连接一次
			msleep(1000);
		}
	
		// 设置关闭延时
		ling.l_onoff = 1;
		ling.l_linger = 0;
		rt_setsockopt(s,SOL_SOCKET,SO_LINGER,(char *)&ling ,sizeof(struct linger));

		// 初始化FIFO
		rt_fifo_init(&g_net_104link_fifo[net],g_net_104link_buf[net],NET_104LINK_BUF_SIZE);

		// 处理接收
		recv_timeout = dTCounter;
		while(1)
		{
			msleep(2);//noted by sunxi: 20221212 设为非阻塞型
		
			len = rt_recv (s, g_net_104link_buf_frame[net], NET_RECV_MAX, MSG_DONTWAIT);

			// 检查是否退出
			if(g_net_104link_exit_flag[net]) 
			{
				// 关闭客户端
				g_104link_client[net] = RT_SOCKET_ERR;
				rt_socket_close (s);
				
				rt_printf_time("net_104link_thread exit(rt_recv).\r\n");
				complete_and_exit(&g_exit_completion_net_104link[net], 0);	
				return 0;
			}
			
			// 检查服务器是否变更
			if(ip_server != tRunPara.ip104Link[net])
			{
				// 关闭客户端
				g_104link_client[net] = RT_SOCKET_ERR;
				rt_socket_close (s);
				
				rt_printf_time("级联服务器变更(%d):ip=%d.%d.%d.%d!\r\n",net,ip[0],ip[1],ip[2],ip[3]);
				goto BEGIN;
			}

			// 应用程序主动关闭
			if(g_net_104link_close_flag[net])
			{
				break;
			}

			if(len == -EAGAIN)
			{
				// 超时
				if((dTCounter -  recv_timeout)>30*T_1s)  // 超时30s，端口未收到数据，关闭端口
				{
					rt_printf_time("104级联30S超时关闭(net=%d)\r\n",net);
					break;
				}
			}
			else if (len <= 0)
			{
				//调用recv时，如果返回0，则表示对端关闭；返回负数，表示出错
				rt_printf_time ("104级联关闭(net=%d,ret=%d)\r\n",net,len);
				msleep(100);
				break;
			}
			else
			{
				rt_fifo_put(&g_net_104link_fifo[net],g_net_104link_buf_frame[net],len);
				recv_timeout=dTCounter; 	//超时计数器清零
				if(net<CFG_ETH_NUM_MAX)
					g_run_stu.led[CFG_UART_NUM_MAX*2+net]=1;
			}
			
			// 接收数据
			while(g_net_104link_data[net] == 0)
			{
				if(rt_fifo_get(&g_net_104link_fifo[net],&buf,1))
				{
					lnk_104_recv(net,buf);
				}
				else
				{
					break;
				}
			}
		}

		// 关闭客户端socket
		g_104link_client[net] = RT_SOCKET_ERR;
		rt_socket_close (s);
	//	rt_printf_time ("net_104link_thread:rt_recv(ret=%d)\r\n",len);
	}

	// 理论上，程序不会运行到这儿
	return 0;
}


#endif

/*------------------------------ 测试函数 -------------------------------------
一个实体文件必须带一个本模块的测试函数来进行单元测试，如果的确不方便在本模块中
进行单元测试，必须在此注明实际的测试位置（例如在哪个实体文件中使用哪个测试函数）.
*/


/*------------------------------ 文件结束 -------------------------------------
*/
