/****************************************************************************** 版权所有: 文件名称: net_debug.c 文件版本: 01.01 创建作者: sunxi 创建日期: 2012-11-07 功能说明: 网络调试接口 其它说明: 修改记录: */ /*------------------------------- 头文件 -------------------------------------- */ #include "head.h" #ifdef LINUX_KERNEL_APP #include #include #include #else #include #include #endif /*------------------------------- 宏定义 -------------------------------------- */ /*------------------------------ 类型结构 ------------------------------------- */ /*------------------------------ 全局变量 ------------------------------------- */ #ifdef LINUX_KERNEL_APP static struct task_struct * g_ts_debug_send; static struct task_struct * g_ts_debug_recv; #else static pthread_t g_ts_debug_recv = 0; #endif SOCKET g_debug_client = RT_SOCKET_ERR; unsigned char g_net_debug_buf[NET_RECV_MAX]; // 以太网接收缓冲区 int g_print_to_net; int g_print_time; int g_print_comm_raw; int g_print_101; int g_print_104; int g_print_lnk; int g_print_hmi; int g_print_can; int g_print_can_monitor; int g_print_sys; int g_print_dd; int g_print_goose; u32 g_print_port= -1; // 允许打印的串口通道,用位表示 int g_test_on; extern struct semaphore g_print_sem; #ifdef LINUX_KERNEL_APP extern struct semaphore g_print_sem; static pid_t g_net_debug_thread_send_pid ; static pid_t g_net_debug_thread_recv_pid ; static int g_net_debug_send_exit_flag = 0; #endif static volatile int g_net_debug_sock_flag = NET_SOCKET_INIT;//关闭SOCKET标志 static int g_net_debug_recv_exit_flag = 0; DEBUG_COMM g_tDebug; //定义下载串口结构 /*------------------------------ 函数声明 ------------------------------------- */ void _net_debug_recv_reset(void); void _net_debug_print_reset(void); #ifdef LINUX_KERNEL_APP int _net_debug_thread_send(void * unused); #endif int _net_debug_thread_recv(void * unused); /*------------------------------ 外部函数 ------------------------------------- 外部函数供其它实体文件引用,必须仔细检查传入参数的合法性. */ //设置socket关闭标志并等待socket关闭完成 void net_debug_set_sock_closing(int no) { struct sockaddr_in local_addr; unsigned int len = sizeof(local_addr); RUN_PARA *pRunPara=&tRunPara; unsigned int ip=0; int bflag = 0; //如果有客户端连接 if(g_debug_client != RT_SOCKET_ERR) { //获取客户端连接的IP rt_getsockname(g_debug_client, (struct sockaddr *)&local_addr, &len); #if 1 if(pRunPara->tNetPara[no].old_index==0) { //获取网卡IP rt_if_ip_get(pRunPara->tNetPara[no].old_ethid, (unsigned char *)&ip);//sunxi: 20220825 335x } else { //获取虚拟网卡上的IP地址 rt_if_ip_get2(pRunPara->tNetPara[no].old_ethid, pRunPara->tNetPara[no].old_index, (unsigned char *)&ip);//sunxi: 20220825 335x } //如果是该网卡的IP并且该网卡要修改IP或MASK,则需要通知关闭socket if((htonl (ip)==local_addr.sin_addr.s_addr) && pRunPara->tNetPara[no].bInit)//sunxi: 20220825 335x { bflag = 1; } #endif //该网卡的IP和MASK没有修改,则不需要通知关闭socket if(!bflag) { return; } g_net_debug_sock_flag = NET_SOCKET_CLOSING; //置关闭标志 //等待关闭完成 while(g_net_debug_sock_flag != NET_SOCKET_CLOSED) { msleep(20); } } } //设置socket重新创建标志 void net_debug_set_sock_initflag(void) { if(g_net_debug_sock_flag == NET_SOCKET_CLOSED) { g_net_debug_sock_flag = NET_SOCKET_REINIT; } } // #ifdef LINUX_KERNEL_APP int net_debug_s_init(void) { struct sched_param sp; pid_t pid; #if 1 // 如果已经初始化,退出 if(g_ts_debug_send) { return 0; } // 运行应用调试线程 //g_ts_debug_send = kthread_run(_net_debug_thread_send,NULL,"net_debug_send"); pid = kernel_thread(_net_debug_thread_send, NULL,CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD); g_ts_debug_send = find_task_by_vpid(pid); if(IS_ERR(g_ts_debug_send)) { printk("ts=%p\r\n",g_ts_debug_send); g_ts_debug_send = NULL; return -1; } g_net_debug_thread_send_pid = pid; memset(&sp,0,sizeof(sp)); sp.sched_priority = 8; sched_setscheduler(g_ts_debug_send,SCHED_FIFO,&sp); // 设置进程名字 set_task_comm(g_ts_debug_send, "net_debug_send"); #endif return 0; } #else int net_debug_s_init(void) { return 0; } #endif #ifdef LINUX_KERNEL_APP int net_debug_s_exit(void) { int status; if(g_ts_debug_send != NULL) { up(&g_print_sem); g_net_debug_send_exit_flag = 1; sys_wait4(g_net_debug_thread_send_pid, (int __user *)&status, 0, NULL); g_ts_debug_send = NULL; } return 0; } #else int net_debug_s_exit(void) { return 0; } #endif // TODO: 需要确认是否需要重新初始化 int net_debug_init(void) { int ret; pthread_attr_t attr; //attr.__schedpolicy = SCHED_FIFO; //attr.__schedparam.sched_priority =8; // 设置调度策略和优先级 pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_FIFO); //调度 struct sched_param s_parm; //s_parm.sched_priority = sched_get_priority_max(SCHED_FIFO); s_parm.sched_priority = 8; pthread_attr_setschedparam(&attr, &s_parm); //优先级 ret = pthread_create(&g_ts_debug_recv, &attr, (void *)_net_debug_thread_recv, NULL); if(!ret) { rt_printf("ret = %d, err = %s\r\n", ret, strerror(ret)); //g_ts_rcd = 0; return ret; } return 0; } #ifdef LINUX_KERNEL_APP int net_debug_exit(void) { int status; #if 0 if(g_ts_debug_send != NULL) { up(&g_print_sem); g_net_debug_send_exit_flag = 1; sys_wait4(g_net_debug_thread_send_pid, (int __user *)&status, 0, NULL); g_ts_debug_send = NULL; } #endif if(g_ts_debug_recv != NULL) { g_net_debug_recv_exit_flag = 1; sys_wait4(g_net_debug_thread_recv_pid, (int __user *)&status, 0, NULL); g_ts_debug_recv = NULL; } return 0; } #else int net_debug_exit(void) { if(g_ts_debug_recv) { g_net_debug_recv_exit_flag = 1; // sys_wait4(g_net_debug_thread_recv_pid, (int __user *)&status, 0, NULL); pthread_join(g_ts_debug_recv, NULL); g_ts_debug_recv = 0; } return 0; } #endif // 此函数必须在线程中调用,不能在中断中调用。 int net_debug_send(unsigned char *buf,int len) { if(g_debug_client == RT_SOCKET_ERR) { return 0; } return rt_send(g_debug_client,buf,len,0); } #ifdef METERING_ENERGY const char main_Debug[]= "\r\n*********装置调试菜单*******\r\n \ 0:通讯原始数据监视[通道可选]\r\n \ 1:101报文监视[通道可选]\r\n \ 2:104报文监视[通道可选]\r\n \ 3:CAN报文监视\r\n \ 4:系统信息监视\r\n \ 5:报文时标开启\r\n \ 6:关闭所有监视\r\n \ 7:面板报文监视\r\n \ 8:CAN总线监视\r\n \ A:载入默认公钥\r\n \ B:加密测试\r\n \ C:SOE测试\r\n \ D:重启系统\r\n \ E:打印点表\r\n \ F: 级联报文监视[通道可选]\r\n \ G:goose报文监视\r\n \ H:文件召唤测试\r\n \ I:程序升级测试\r\n \ J:打印终端固有参数\r\n \ K:新生成soe.xml文件\r\n \ L:新生成co.xml文件\r\n \ M:清零电压合格率\r\n \ N:清零脉冲计数值\r\n \ P:61850信息打印\r\n \ Q:生成菜单显示文件\r\n \ R:生成icd文件\r\n \ S:计量板校准\r\n \ T:计量电能清零\r\n \ U:CAN子板ID打印\r\n \ 其它:关闭所有监视信息\r\n \ \r\n>:"; #else const char main_Debug[]= "\r\n*********装置调试菜单*******\r\n \ 0:通讯原始数据监视[通道可选]\r\n \ 1:101报文监视[通道可选]\r\n \ 2:104报文监视[通道可选]\r\n \ 3:CAN报文监视\r\n \ 4:系统信息监视\r\n \ 5:报文时标开启\r\n \ 6:关闭所有监视\r\n \ 7:面板报文监视\r\n \ 8:CAN总线监视\r\n \ A:载入默认公钥\r\n \ B:加密测试\r\n \ C:SOE测试\r\n \ D:重启系统\r\n \ E:打印点表\r\n \ F: 级联报文监视[通道可选]\r\n \ G:goose报文监视\r\n \ H:文件召唤测试\r\n \ I:程序升级测试\r\n \ J:打印终端固有参数\r\n \ K:新生成soe.xml文件\r\n \ L:新生成co.xml文件\r\n \ M:清零电压合格率\r\n \ N:清零脉冲计数值\r\n \ P:61850信息打印\r\n \ Q:生成菜单显示文件\r\n \ R:生成icd文件\r\n \ r:查看实时线程状态\r\n \ s:复位实时线程状态\r\n \ S:ADC采样状态\r\n \ 其它:关闭所有监视信息\r\n \ \r\n>:"; #endif BYTE arrApn[48]; extern int g_soe_test; int _net_debug_get_chn(void) { int chn; if(g_tDebug.cRecvCnt >= 3) { chn = g_tDebug.arrRecvBuf[2] - '0'; if(chn >=0 && chn < COMM_CHANNEL_NUM) { g_print_port = 1<18) { rt_printf(buf); strcpy(buf,"\r\n "); j=0; } } if(j>0) { rt_printf(buf); } rt_printf("\r\n"); } /*------------------------------ 内部函数 ------------------------------------- 内部函数以下划线‘_’开头,不需要检查参数的合法性. */ void _net_debug_recv_reset(void) { g_tDebug.cTypeCounter=0; g_tDebug.cRecvCnt=0; } void _net_debug_print_reset(void) { g_print_to_net = 0; g_print_time = 0; g_print_101 = 0; g_print_104 = 0; g_print_lnk = 0; g_print_hmi = 0; g_print_can = 0; g_print_can_monitor= 0; g_print_sys = 0; g_print_dd = 0; g_print_comm_raw = 0; g_print_port = -1; g_test_on = 0; g_print_goose=0; } void _net_debug_recv(BYTE cRcvData) { if(g_tDebug.bData) { return; } //xshell 发送的保活状态消息 if(cRcvData == 0xff || cRcvData == 0xf1) { return; } //rt_printf("cRcvData=%02x\r\n",cRcvData); g_tDebug.arrRecvBuf[g_tDebug.cRecvCnt++]=cRcvData; if(cRcvData == 0x0d) { g_tDebug.bData=true; } if(g_tDebug.cRecvCnt >= UART_FRAME_LEN) { _net_debug_recv_reset(); return; } return; } #ifdef LINUX_KERNEL_APP int _net_debug_thread_send(void * unused) { while(1) { msleep(100); // 检查是否退出 if(g_net_debug_send_exit_flag) { g_net_debug_send_exit_flag = 0; return 0; } //noted by sunxi: 20220701 统一冷火和335x,有信号量,才往下执行 if(down_interruptible(&g_print_sem)<0) { continue; } rt_printf_fifo_get(1); } return 0; } #endif /* noted by sunxi:20220825 335x 修复 6000端口超时或断开后,不能重连的问题 */ int _net_debug_thread_recv(void * unused) { struct sockaddr_in server_addr; struct sockaddr_in client_addr; struct linger ling; #ifndef LINUX_KERNEL_APP struct timeval timeout; #endif SOCKET sk_server,tmp_sock; int sin_size; int i,len,ret; unsigned char *ip; #ifdef LINUX_KERNEL_APP unsigned int recv_timeout; unsigned int link_timeout = dTCounter; #else int recv_timeout; int link_timeout = 0; #endif unsigned int ip_server; // while(1) //noted by sunxi:20220825 335x 修复 6000端口超时或断开后,不能重连的问题 // { NET_BEGIN: // 1、建立socket并侦听 // 服务器端开始建立socket描述符 #ifdef LINUX_KERNEL_APP sk_server = rt_socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0); #else sk_server = rt_socket(AF_INET, SOCK_STREAM, 0); #endif if (sk_server == RT_SOCKET_ERR) { RT_PRINTF_POSITION(); g_ts_debug_recv = NULL; return -1; } // 服务器端填充sockaddr结构 memset (&server_addr, 0, sizeof (struct sockaddr_in)); server_addr.sin_family = AF_INET; if(get_mt_port_proc()==NET_MT_PORT_PROC_2) { if(rt_if_ip_get2(0, 3, (unsigned char *)&ip_server)==0) { server_addr.sin_addr.s_addr = htonl (ip_server); } else { server_addr.sin_addr.s_addr = htonl (INADDR_ANY); } } else { server_addr.sin_addr.s_addr = htonl (INADDR_ANY); } server_addr.sin_port = htons (NET_DEBUG_PORT); // 捆绑sockfd描述符 ret = rt_bind (sk_server, (struct sockaddr *)(&server_addr), sizeof (struct sockaddr)); if (ret < 0) { RT_PRINTF_POSITION(); rt_socket_close (sk_server); g_ts_debug_recv = NULL; return -2; } // 监听sockfd描述符 ret = rt_listen (sk_server, 1); if (ret < 0) { RT_PRINTF_POSITION(); rt_socket_close (sk_server); g_ts_debug_recv = NULL; return -3; } #ifndef LINUX_KERNEL_APP // 设置接收超时 timeout.tv_sec = 0; timeout.tv_usec = 500000; rt_setsockopt(sk_server,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout ,sizeof(struct timeval)); #endif // 设置关闭延时 ling.l_onoff = 1; ling.l_linger = 0; rt_setsockopt(sk_server,SOL_SOCKET,SO_LINGER,(char *)&ling ,sizeof(struct linger)); prctl(PR_SET_NAME, "_net_debug_recv"); while(1) //noted by sunxi:20220825 335x 修复 6000端口超时或断开后,不能重连的问题 { g_debug_client = RT_SOCKET_ACCEPT_RST; // 2、接受外部链接 #ifdef RT_SOCKET_FD while(g_debug_client < 0) #else while(g_debug_client == RT_SOCKET_ACCEPT_RST) #endif { msleep(100); // 服务器阻塞,直到客户程序建立连接 sin_size = sizeof (struct sockaddr_in); g_debug_client = rt_accept (sk_server, (struct sockaddr*)(&client_addr), &sin_size); // 检查是否退出 //if(kthread_should_stop()) if(g_net_debug_recv_exit_flag) { // 关闭客户端 if(g_debug_client != RT_SOCKET_ERR) { tmp_sock = g_debug_client; g_debug_client = RT_SOCKET_ERR; rt_socket_close (tmp_sock); } // 关闭服务器 rt_socket_close (sk_server); g_net_debug_recv_exit_flag= 0; return 0; } //IP已经修改要关闭SOCKET if(g_net_debug_sock_flag == NET_SOCKET_CLOSING) { // 关闭客户端 if(g_debug_client != RT_SOCKET_ERR) { tmp_sock = g_debug_client; g_debug_client = RT_SOCKET_ERR; rt_socket_close (tmp_sock); } // 关闭服务器 rt_socket_close (sk_server); //置已经关闭标志 g_net_debug_sock_flag = NET_SOCKET_CLOSED; //等待IP修改完成 while(g_net_debug_sock_flag != NET_SOCKET_REINIT) { msleep(100); } //置为初始化 g_net_debug_sock_flag = NET_SOCKET_INIT; goto NET_BEGIN; } } // 3、关闭侦听socket,避免多个外部连接 //rt_shutdown(sk_server, SHUT_RDWR); //rt_socket_close(sk_server); //noted by sunxi:20220825 335x 修复 6000端口超时或断开后,不能重连的问题 ip = (unsigned char *)&client_addr.sin_addr.s_addr; rt_printf("debug client connect(ip=%d.%d.%d.%d)!\r\n",ip[0],ip[1],ip[2],ip[3]); { char buf[64]; char log_info[128]; sprintf(buf,"6000端口连接(%d#网络:IP=%d.%d.%d.%d)!\r\n", set_get_network_id_from_socket(g_debug_client)+1, ip[0],ip[1],ip[2],ip[3]); log_str_time(LOG_OPERATE,buf,0,1); sprintf(log_info,"6000 port connection(%d#net:IP=%d.%d.%d.%d)",set_get_network_id_from_socket(g_debug_client)+1, ip[0],ip[1],ip[2],ip[3]); load_hs_log_rcd(TYPE_CHNL_LINK,true,NULL,log_info,1); } // 4、客户端已连接,处理客户端命令 #ifdef LINUX_KERNEL_APP recv_timeout=dTCounter; #else recv_timeout=0; #endif while(1) { #ifdef LINUX_KERNEL_APP msleep(2);//noted by sunxi: 20221212 设为非阻塞型 len = rt_recv (g_debug_client, g_net_debug_buf, NET_RECV_MAX, MSG_DONTWAIT); #else len = rt_recv (g_debug_client, g_net_debug_buf, NET_RECV_MAX, 0); #endif // 检查是否退出 if(g_net_debug_recv_exit_flag) { // 关闭客户端 tmp_sock = g_debug_client; g_debug_client = RT_SOCKET_ERR; rt_socket_close (tmp_sock); // 关闭服务器 rt_socket_close (sk_server); //noted by sunxi:20220825 335x 修复 6000端口超时或断开后,不能重连的问题 g_net_debug_recv_exit_flag = 0; return 0; } //IP已经修改要关闭SOCKET if(g_net_debug_sock_flag == NET_SOCKET_CLOSING) { // 关闭客户端 tmp_sock = g_debug_client; g_debug_client = RT_SOCKET_ERR; rt_socket_close (tmp_sock); // 关闭服务器 rt_socket_close (sk_server); //noted by sunxi:20220825 335x 修复 6000端口超时或断开后,不能重连的问题 //置已经关闭标志 g_net_debug_sock_flag = NET_SOCKET_CLOSED; //等待IP修改完成 while(g_net_debug_sock_flag != NET_SOCKET_REINIT) { msleep(100); } //置为初始化 g_net_debug_sock_flag = NET_SOCKET_INIT; goto NET_BEGIN; } #ifdef LINUX_KERNEL_APP if(len == -EAGAIN) #else if((len == -EAGAIN)||(len==-1))//使用linux的recvfrom没有接收到数据时返回-1 jack.liu 20200916 #endif { if(rt_get_net_linkstatus(rt_get_netcard_id_from_socket(g_debug_client))==1) { #ifdef LINUX_KERNEL_APP if((dTCounter - link_timeout)>RT_LINK_TIMEOUT*T_1s) //500ms超时 #else link_timeout++; if(link_timeout>RT_LINK_TIMEOUT*2) //500ms超时 #endif { rt_printf("%s: link down\n",__FUNCTION__); #ifdef LINUX_KERNEL_APP link_timeout = dTCounter; #else link_timeout = 0; #endif break; } } else { #ifdef LINUX_KERNEL_APP link_timeout = dTCounter; #else link_timeout = 0; #endif } // 超时 #ifdef LINUX_KERNEL_APP if((dTCounter - recv_timeout)>120*T_1s) // 超时2分钟,端口未收到数据,关闭端口 #else recv_timeout++; if(recv_timeout>240) // 超时2分钟,端口未收到数据,关闭端口 #endif { rt_printf ("net_debug_thread:timeout\r\n"); break; } continue; } #ifdef LINUX_KERNEL_APP else if (len <= 0) #else else if ((len == 0)||(len<-1)) //使用linux的recvfrom没有接收到数据时返回-1 jack.liu 20200916 #endif { //调用recv时,如果返回0,则表示对端关闭;返回负数,表示出错 rt_printf ("net_debug_thread:peer close(ret=%d).\r\n",len); msleep(100); break; } // 接收数据 #ifdef LINUX_KERNEL_APP recv_timeout = dTCounter; link_timeout = dTCounter; #else recv_timeout = 0; link_timeout = 0; #endif for(i=0;i