ZeroOne AI
← 返回文章列表

TRIO-Basic 从入门到精通(十):UDP通讯实现网络串口化测试与应用

👁 3
分类:机器人自动化

本教程讲解TRIO控制器通过UDP通讯实现网络串口化的完整方案:使用OPEN#20 AS "dgram:IP(端口)"指令发送UDP数据包,WAIT UNTIL实现20ms定时播报,再用Wireshark与WinPcap上位机程序抓包验证。实测数据包准时稳定、无丢包,适用于控制器与第三方设备实时数据交互。

TRIO-Basic 从入门到精通(十):UDP通讯实现网络串口化测试与应用

一、教程背景

在工业控制场合,经常需要向第三方硬件或电脑快速、实时地发送数据,并且往往无法预先确认连接是否成功建立。传统做法是使用串口,但在没有串口网络环境的情况下,UDP 通讯是很好的替代方案。UDP(用户数据报协议)是一种无连接的传输层协议(IETF RFC 768,IP 报文协议号 17),提供面向事务的简单不可靠信息传送服务;它虽然在大数据分割发送时不能保证顺序,但对工业控制领域的数据播报场景已足够满足要求。本讲在 TRIO 控制器上用 TRIO-Basic 实现 UDP 数据定时发送(网络串口化),并用 Wireshark 与 WinPcap 上位机程序完成收包测试,适用于控制器与第三方设备/上位机的实时数据交互场景。

二、技术要点/技术方案

  1. UDP 通讯指令:在 TRIO-Basic 中通过 OPEN #n AS "dgram:目标IP(端口)" FOR READ_WRITE 打开 UDP 数据报通道,用 PRINT #n,字符串 发送数据。
  2. 定时发送:使用 WAIT UNTIL cal_timept-TICKS >= send_timept 实现阻塞计时,按固定时间间隔(本实验 20ms)周期发送数据包。
  3. 报文格式:示例报文以 "XG" 开头 + 6 位序号(STR(cal_allnum,0,6))+ 回车换行(CHR(13)+CHR(10)),方便接收端识别与校验。
  4. 网络抓包验证:电脑端安装 Wireshark(示例版本 Wireshark-win32-2.0.5.exe)抓取 UDP 数据包,验证数据是否实时、稳定、不丢包。
  5. 上位机收包:使用 WinPcap 开发包(WinPcap_4_1_3.exe 驱动 + WpdPack_4_1_2.zip 库,开发环境 Microsoft Visual Studio 2010),用 C 语言解析 IP/UDP 帧头,识别 "XG" 标记的数据并打印来源 IP、端口与报文内容。

三、实施步骤

  1. 编写并下载控制器发送程序:
    • CLOSE#20 关闭端口并延时等待;
    • OPEN#20 AS "dgram:192.168.0.10(8080)" FOR READ_WRITE 打开 UDP 通道,向 192.168.0.10 的 8080 端口广播数据;
    • 初始化发送计数,进入循环:用阻塞计时实现 20ms 定时,构造 "XG+6位序号+回车换行" 报文,PRINT #20,str_array 发送。
  2. 在电脑端安装 Wireshark 并启动抓包,确认实时收到来自控制器的数据包,收包时间稳定、无丢包。
  3. 搭建上位机收包程序(VS2010 + WinPcap):
    • 定义 IP 帧头结构(ip_header,24 字节)与 UDP 帧头结构(udp_header,8 字节);
    • pcap_findalldevs_ex 获取网卡列表,选择连接控制器的网卡;
    • pcap_open 以混杂模式打开网卡,pcap_compile + pcap_setfilter 设置 "ip and udp" 过滤条件;
    • pcap_loop 启动循环捕获,在回调函数 packet_handler 中解析以太网帧(偏移 14 字节定位 IP 头)、IP 头(ver_ihl&0xf)*4 定位 UDP 头,读取 UDP 数据前两位判断是否为 "XG" 标记,命中则打印时间戳、源/目标 IP、端口及报文内容。
  4. 运行上位机程序,选择实际连接控制器的网卡,观察收包结果。
  5. 结论验证:数据包准时收到,未发生丢包和数据错误。

四、代码示例

控制器发送端(TRIO-Basic):

CLOSE#20
WA(100)
OPEN#20 AS "dgram:192.168.0.10(8080)" FOR READ_WRITE
WA(50)
DIM send_timept,cal_timept,cal_allnum AS INTEGER
DIM str_array AS STRING(20)
str_array="XG"
str_array=str_array+"000000"
str_array=str_array+CHR(13)+CHR(10)
PRINT #0,str_array
cal_allnum=0

TICKS=0
cal_timept=TICKS
send_timept=20

WHILE TRUE

    WAIT UNTIL cal_timept-TICKS >=send_timept
    cal_timept=cal_timept-send_timept
    cal_allnum=cal_allnum+1
    str_array="XG"
    str_array=str_array+STR(cal_allnum,0,6)
'    str_array=str_array+"000000"
    str_array=str_array+CHR(13)+CHR(10)
'    PRINT #0,str_array
    PRINT #20,str_array
WEND

CLOSE#20

上位机收包端(C + WinPcap):

//=================================================================
//测试标题:UDP测试
//测试时间:2017-11-10
//测试人  :桂志微
//公司	  :上海摩习恩自动化工程有限公司
//=================================================================
#include "pcap.h"

//IP地址
typedef struct ip_address{
	u_char byte1;
	u_char byte2;
	u_char byte3;
	u_char byte4;
}ip_address;			//4字节

//IPV4帧头结构
typedef struct ip_header{
	u_char	ver_ihl;		// 版本信息4BIT 信息长度4BIT	1字节
	u_char	tos;			// 服务类型						1字节 
	u_short tlen;			// 总长度						2字节 
	u_short identification; // 数据包标识					2字节
	u_short flags_fo;		// 标记3BIT 帧偏移13BIT			2字节
	u_char	ttl;			// 活动时间						1字节
	u_char	proto;			// 协议类型						1字节
	u_short crc;			// 校验							2字节
	ip_address	saddr;		// 源IP地址						4字节
	ip_address	daddr;		// 目标地址						4字节
	u_int	op_pad;			// 填充选项						4字节
}ip_header;													//24字节

//UDP帧头
typedef struct udp_header{
	u_short sport;			// 源端口			2字节
	u_short dport;			// 目标端口			2字节
	u_short len;			// 数据帧长度		2字节
	u_short crc;			// 校验				2字节
}udp_header;									//8字节

//响应数据包句柄
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

int main()
{
	//网卡驱动
	pcap_if_t *alldevs;
	pcap_if_t *d;
	
	int inum;
	int i=0;

	pcap_t *adhandle;

	char errbuf[PCAP_ERRBUF_SIZE];
	u_int netmask;
	char packet_filter[] = "ip and udp";
	struct bpf_program fcode;

	//获取所有网卡驱动链表
	if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
	{
		fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
		exit(1);
	}
	
	//打印所有网卡驱动链表
	for(d=alldevs; d; d=d->next)
	{
		printf("%d. %s", ++i, d->name);
		if (d->description)
			printf(" (%s)\n", d->description);
		else
			printf(" (No description available)\n");
	}

	if(i==0)
	{
		printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
		return -1;
	}
	
	//选择驱动网卡
	printf("Enter the interface number (1-%d):",i);
	scanf_s("%d", &inum);
	
	if(inum < 1 || inum > i)
	{
		printf("\nInterface number out of range.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return -1;
	}

	//定位选择网卡驱动
	for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
	
	//打开网卡驱动
	if ( (adhandle= pcap_open(d->name,							// 驱动名称
							 65536,								// 捕获数据包 
																// 存放数据包长度65536
							 PCAP_OPENFLAG_PROMISCUOUS,			// 混合模式
							 1000,								// 读取超时
							 NULL,								// 远程身份验证
							 errbuf								// 错误缓冲
							 ) ) == NULL)
	{
		fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");
		//释放驱动链表
		pcap_freealldevs(alldevs);
		return -1;
	}
	
	//链路层检查,只支持以太网链路层
	if(pcap_datalink(adhandle) != DLT_EN10MB)
	{
		fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
		//释放驱动链表
		pcap_freealldevs(alldevs);
		return -1;
	}
	
	if(d->addresses != NULL)
		//获取接口首地址掩码
		netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
	else
		//如果接口没有地址,我们提供C类网络掩码
		netmask=0xffffff; 

	//编译过滤器
	if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
	{
		fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
		//释放驱动链表
		pcap_freealldevs(alldevs);
		return -1;
	}
	
	//设置过滤器
	if (pcap_setfilter(adhandle, &fcode)<0)
	{
		fprintf(stderr,"\nError setting the filter.\n");
		//释放驱动链表
		pcap_freealldevs(alldevs);
		return -1;
	}
	
	printf("\nlistening on %s...\n", d->description);
	
	//释放驱动链表
	pcap_freealldevs(alldevs);
	
	//启动循环捕获
	pcap_loop(adhandle, 0, packet_handler, NULL);
	
	return 0;
}

//响应数据包句柄-每个数据包进行回调
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
	struct tm ltime;
	char timestr[16];
	char Bmark[20];
	ip_header *ih;
	udp_header *uh;
	u_int ip_len;
	u_short sport,dport;
	time_t local_tv_sec;
	int indexP=0;

	//将时间戳转换为可读格式
	local_tv_sec = header->ts.tv_sec;
	localtime_s(<ime, &local_tv_sec);
	strftime( timestr, sizeof timestr, "%H:%M:%S", <ime);

	//打印时间戳和包的长度
	printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);

	//pkt_data数据帧
	//定位以太网IP帧位置
	ih = (ip_header *) (pkt_data +14);

	//定位UDP数据帧位置
	ip_len = (ih->ver_ihl & 0xf) * 4;
	uh = (udp_header *) ((u_char*)ih + ip_len);

	//从网络字节顺序转换为主机字节顺序
	sport = ntohs( uh->sport );
	dport = ntohs( uh->dport );
	//定位UDP数据帧
	Bmark[0]=*((u_char*)uh + 8+0);
	Bmark[1]=*((u_char*)uh + 8+1);
	if(Bmark[0]=='X' && Bmark[1]=='G')
	{
		for(indexP=0;indexP<8;indexP++)
		{
			Bmark[indexP]=*((u_char*)uh + 8+indexP);
		}
		Bmark[indexP]='\0';

		//打印数据包消息
		//打印ip地址和udp端口
		printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d:%s\n",
			ih->saddr.byte1,
			ih->saddr.byte2,
			ih->saddr.byte3,
			ih->saddr.byte4,
			sport,
			ih->daddr.byte1,
			ih->daddr.byte2,
			ih->daddr.byte3,
			ih->daddr.byte4,
			dport,
			Bmark);
	}

}

五、应用价值

  1. UDP 无连接特性适合向未知连接状态下的第三方设备稳定播报数据,实现"网络串口化"。
  2. 定时发送机制(20ms 周期)保证数据实时性,实测收包稳定、无丢包、无数据错误。
  3. 报文带标记(XG)与序号,接收端可方便识别与校验数据完整性。
  4. 上位机基于 WinPcap 抓包解析,可精确获取数据来源 IP、端口与内容,便于系统集成与排障。
  5. 全程实测验证(Wireshark + 上位机程序双通道确认),方案可直接复用到工业现场。

六、SEO关键词

TRIO运动控制器,TRIO-Basic,UDP通讯,网络串口化,数据报协议,WinPcap,Wireshark,定时发送,上位机抓包,OPEN指令

本文内容整理自 guimotion 的《TRIO-Basic从入门到精通教程》系列(CSDN,CC BY-SA 4.0),由 ZeroOne AI 重新组织编写。

评论(0