本文主要介绍如何通讯C#编程上位机语言和美国Rockwell Allen-Bradley(AB)PLC进行在以太网物理网络上实现通讯,实现数据的读取和写入控制。本文参考资料有官方文档EIP-CIP-V2-1.0.pdf,和网上下载的一个关于协议格式的文档。同时和现场的PLC实际通讯互相验证才形成此文,以和大家互相交流打破技术壁垒共同提高进步。
本文分为四大部分
美国Rockwell Allen-Bradley(AB)PLC的通讯写采用的是EIP和CIP嵌套的通讯协议,在EIP的数据部分嵌入CIP。以面向对象的方式通讯每次以请求应答的形式实现通讯。通讯的内容以类、对象、服务、属性四大类信息组成。
CIP是一种为工业应用开发的应用层协议,被deviceNet、ControINet、EtherNet/IP三种网络所采用,因此这三种网络相应地统称为CIP网络。为了在人们普遍偏好的工业以太网市场上也占据一席之地,ODVA/CI联合了另外一个国际组织——工业以太网协会(IndustrialEthemet Association.IEA)在2000年推出了EtherNet/IP。注意,EtherNet/IP名称时,的“IP”是工业协议( Industrial Protocol)的缩写。EtherNet/IP在以太网技术和TCP/IP技术的基础上开发的一种工业以太网,其应用层协议也使用CIP。三种CIP网络都已经成为国际标准。现场总线的标准化工作由国际电工委员会(IEC)负责,有关的标准有两个:一个是设备级现场总线标准(IEC62026);另一个是现场级现场总线标准(IEC 61158)。DeviceNet被纳入IEC62026之中,ControINet和EtherNet/IP均被纳入IEC 61158中。Allen- Bradley部门是CIP网络的原始开发者,并且且推出了许多CIP网络产品,因此其网站上与CIP网络有关的信息还是很多的。
ABPLC的通讯的完成过程如下图
打开请求分为:标准打开请求和扩展打开请求。二者的区别为标准的打开请求中设置的数据服务请求数据包的大小为500字节。扩展打开请求设置的数据服务请求数据包大小范围为500~4000个字节。
我们读取PLC的数据通常是使用标签名称的方式,简要的来说就是在请求中输入标签的名称,PLC根据标签名称将标签对应的数据返回给上位机软件。由于在每次通讯时最大的数据包长度有限制所以就需要在读取多个标签时将标签进行分组。每组内的所有标签构成的数据请求包要在指定的大小范围之内。这就会涉及到使用动态决策算法求出最优的组合。当然如果不使用最优组合也行就是会在通讯时产生额外的流量。
ABPLC的通讯采用小端模式。一般情况实现注册、请求、读写数据即可。不实现关闭请求、卸载注册在实际测试过程中也不会出现问题。在断开连接时不提交关闭请求和卸载注册指令。再重新连接plc执行注册、打开请求、读写数据还是一样可以成功的。
65 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
字段 |
字节数 |
说明 |
命令(0x0065) |
2 |
注册请求 |
长度(0x0004) |
2 |
命令数据长度(单位字节) |
会话句柄 |
4 |
由会话句柄,初始值为0x00000000 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
协议版本(0x0001) |
2 |
默认为0x0001 |
选项标记(0x0000) |
2 |
默认为0x0000 |
65 00 04 00 57 01 56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
字段 |
字节数 |
说明 |
命令(0x0065) |
2 |
注册请求 |
长度(0x0004) |
2 |
命令数据长度(单位字节) |
会话句柄 |
4 |
由AB PLC生成 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
协议版本(0x0001) |
2 |
默认为0x0001 |
选项标记(0x0000) |
2 |
默认为0x0000 |
6f 00 40 00 57 01 56 00 00 00 00 00 8d 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 b2 00 30 00 54 02 20 06 24 01 05 f7 02 00 00 80 01 00 fe 80 02 00 1b 05 c0 80 a7 02 02 00 00 00 80 84 1e 00 f4 43 80 84 1e 00 f4 43 a3 03 01 00 20 02 24 01
字段 |
字节数 |
说明 |
命令(0x006F) |
2 |
命令类型(0x6f=打开请求) |
长度(0x0040) |
2 |
EIP命令数据长度,固定为64字节(0x0040) |
会话句柄 |
4 |
注册应答中返回的数值 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
接口句柄(0x00000000) |
4 |
默认为0x00000000(CIP) |
超时(0x0001) |
2 |
默认为0x0001 |
项数(0x0002) |
2 |
默认为0x0002 |
空地址项(0x0000) |
2 |
默认为0x0000 |
长度(0x0000) |
2 |
默认为0x0000 |
未连接数据项(0x00b2) |
2 |
默认为(0x00b2) |
长度(0x0030) |
2 |
后面数据包的长度(48个字节) |
以下是CIP协议的内容 |
||
服务 |
1 |
服务类型,固定为0x54 |
请求路径大小 |
1 |
固定为0x02 |
请求路径(0x0620) |
2 |
类逻辑段 20中第7,6,5位表示段类型,第4,3,2位表示逻辑数据类型,例如(classID,InstanceID,MemberID,ServiceID等等),第0,1位表示逻辑格式(8位、16位、32位),06表示具体ID值 |
请求路径(0x0124) |
2 |
实例逻辑段 |
Priority/time_tick |
1 |
固定为0xF7 |
Time-out_ticks |
1 |
固定为0x05 |
O-T NetWork Connection ID |
4 |
默认0x80000002,可以修改成自己的值 |
T-O NetWork Connection ID |
4 |
默认0x80FE0001,可以修改成自己的值 |
Connection Serial Number |
2 |
默认0x0002,可以修改成自己的值 |
Verder ID |
2 |
默认0x0101,可以修改成自己的值 |
Originator Serial Number |
4 |
和T-O NetWork Connection ID相同 |
连接超时倍数 |
1 |
0x02 |
保留数据 |
3 |
0x000000 |
O-T RPI(包间隔) |
4 |
固定0x001e8480 |
O-T 网络连接参数 |
2 |
固定0x43F4 |
T-O RPI(包间隔) |
4 |
固定0x001e8480 |
T-O 网络连接参数 |
2 |
固定0x43F4 |
传输类型 |
1 |
固定0xA3 |
连接路径大小 |
1 |
固定0x03 |
连接路径、机架号 |
1 |
0x01 |
连接路径、插槽号 |
1 |
0x00 |
连接路径、类和实例 |
4 |
0x01240220 |
6f 00 2e 00 57 01 56 00 00 00 00 00 8e 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 b2 00 1e 00 d4 00 00 00 4d 7c 9c ff 02 00 fe 80 03 00 1b 05 c0 80 a7 02 80 84 1e 00 80 84 1e 00 00 00
字段 |
字节数 |
说明 |
命令(0x006F) |
2 |
打开请求 |
长度(0x002E) |
2 |
正常情况为46字节(0x002E) |
会话句柄 |
4 |
注册应答中返回的数值 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
接口句柄(0x00000000) |
4 |
默认为0x00000000(CIP) |
超时(0x0001) |
2 |
默认为0x0001 |
项数(0x0002) |
2 |
默认为0x0002 |
空地址项(0x0000) |
2 |
默认为0x0000 |
长度(0x0000) |
2 |
默认为0x0000 |
未连接数据项(0x00b2) |
2 |
默认为(0x00b2) |
长度(0x001E) |
2 |
后面数据包的长度(30个字节) |
以下是CIP协议的内容 |
||
服务 |
1 |
固定为0xD4 |
保留数据 |
1 |
固定为0x00 |
状态 |
2 |
状态好为0x0000 |
O-T NetWork Connection ID |
4 |
由PLC产生 |
T-O NetWork Connection ID |
4 |
和打开请求帧中的相同 |
Connection Serial Number |
2 |
0x0000 |
Verder ID |
2 |
0x0101 |
Originator Serial Number |
4 |
和T-O NetWork Connection ID相同 |
O-T API |
4 |
0x00 1e 84 80 |
T-O RPI |
4 |
0x00 1e 84 80 |
应用应答大小 |
1 |
0x00 |
保留数据 |
1 |
0x00 |
6f 00 44 00 57 01 31 00 00 00 00 00 41 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 b2 00 34 00 5b 02 20 06 24 01 05 f7 02 00 00 80 01 00 fe 80 02 00 1b 05 c0 80 a7 02 02 00 00 00 80 84 1e 00 a0 0f 00 42 80 84 1e 00 a0 0f 00 42 a3 03 01 00 20 02 24 01
字段 |
字节数 |
说明 |
命令(0x006F) |
2 |
打开请求 |
长度(0x0044) |
2 |
固定为68字节(0x0044) |
会话句柄 |
4 |
注册应答中返回的数值 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
接口句柄(0x00000000) |
4 |
默认为0x00000000(CIP) |
超时(0x0001) |
2 |
默认为0x0001 |
项数(0x0002) |
2 |
默认为0x0002 |
空地址项(0x0000) |
2 |
默认为0x0000 |
长度(0x0000) |
2 |
默认为0x0000 |
未连接数据项(0x00b2) |
2 |
默认为(0x00b2) |
长度(0x0030) |
2 |
后面数据包的长度(48个字节) |
以下是CIP协议的内容 |
||
服务 |
1 |
固定为0x5b |
请求路径大小 |
1 |
固定为0x02 |
请求路径(0x0620) |
2 |
类逻辑段 20中第7,6,5位表示段类型,第4,3,2位表示逻辑数据类型,例如(classID,InstanceID,MemberID,ServiceID等等),第0,1位表示逻辑格式(8位、16位、32位),06表示具体ID值 |
请求路径(0x0124) |
2 |
实例逻辑段 |
Priority/time_tick |
1 |
固定为0xF7 |
Time-out_ticks |
1 |
固定为0x05 |
O-T NetWork Connection ID |
4 |
默认0x80000002,可以修改成自己的值 |
T-O NetWork Connection ID |
4 |
默认0x80FE0001,可以修改成自己的值 |
Connection Serial Number |
2 |
0x0000 |
Verder ID |
2 |
0x0101 |
Originator Serial Number |
4 |
和T-O NetWork Connection ID相同 |
连接超时倍数 |
1 |
0x02 |
保留数据 |
3 |
0x000000 |
O-T RPI(包间隔) |
4 |
固定0x001e8480 |
数据包大小 |
2 |
请单次数据包大小 |
网络连接参数 |
2 |
固定0x4200,表示意思不清楚 |
T-O RPI(包间隔) |
4 |
固定0x001e8480 |
数据包大小 |
2 |
请单次数据包大小 |
网络连接参数 |
2 |
固定0x4200,表示意思不清楚 |
传输类型 |
1 |
0xA3 |
连接路径大小 |
1 |
0x03 |
连接路径、机架号 |
1 |
0x01 |
连接路径、插槽号 |
1 |
0x00 |
连接路径、类和实例 |
4 |
0x01240220 |
6f 00 2e 00 57 01 31 00 00 00 00 00 41 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 b2 00 1e 00 db 00 00 00 48 7c 9c ff 01 00 fe 80 02 00 1b 05 c0 80 a7 02 80 84 1e 00 80 84 1e 00 00 00
扩展请求应答的格式和标准请求应答的格式一致不再列表详述。
读取属性和标签PC_REALDATA_S0_INT.S0_PLC_HOUR,属性可以不读取
70 00 5c 00 57 01 56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 a1 00 04 00 4d 7c 9c ff b1 00 48 00 8f 0c 0a 02 20 02 24 01 02 00 06 00 12 00 03 02 20 ac 24 01 02 00 01 00 03 00 52 13 91 0d 50 43 5f 43 4f 4e 54 52 4f 4c 5f 53 31 00 91 14 53 31 5f 53 54 41 52 54 5f 43 49 52 43 4c 45 50 55 4d 50 31 01 00 00 00 00 00
字段 |
字节数 |
说明 |
命令(0x0070) |
2 |
打开请求,固定为0x 00 70 |
长度 |
2 |
命令指定数据的大小 |
会话句柄 |
4 |
注册应答中返回的数值 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
接口句柄(0x00000000) |
4 |
默认为0x00000000(CIP) |
超时(0x0001) |
2 |
默认为0x0001 |
项数(0x0002) |
2 |
默认为0x0002 |
连接的地址项 |
2 |
默认为0x00 A1 |
长度 |
2 |
默认为0x00 04 |
连接标识 |
4 |
和打开应答中的 O-T NetWork Connection ID相同 |
连接的数据项 |
2 |
默认为(0x00b1) |
长度 |
2 |
后面数据包的长度 |
序号 |
2 |
数据服务请求帧的序号(从1开始) |
以下是CIP协议的内容 |
||
服务 |
1 |
固定为0x0A |
请求路径大小 |
1 |
固定为0x02 |
请求路径 |
4 |
固定为0x01240220 |
服务数(即测点数) |
2 |
请求数据点的个数 |
偏移量(和服务数相同) |
2*(服务数) |
从服务数第一个字节算起,每个服务的偏移量 |
服务1(即测点1) |
||
服务标识 |
1 |
0x03请求服务列表,0x52请求标签数据 |
请求路径大小 |
1 |
目前总结的公式是size = (len +1)/2 +1; 其中size为请求路径大小,len为请求侧点名的长度 |
扩展符号 |
1 |
固定为0x91 |
数据大小 |
1 |
该服务所对应的PLC中的侧点名大小 |
数据内容 |
|
该服务所对应的PLC中的侧点名 |
目前,发现的规律是侧点名的长度是奇数时,有一个填充字节,偶数时不填充 另外,当侧点名中有“.”时,需以点为分割线分为两部分进行传输 |
||
服务命令指定数据 |
2 |
当前请求服务位属性时 固定为0x00 02.当请求服务为标签数据是,固定为0x00 001 |
预留 |
4 |
当前请求服务为属性时固定 0x00030001,当请求服务为标签数据是固定0x00000000 |
服务2(即测点2) |
||
……. |
70 00 3b 00 57 01 56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 a1 00 04 00 02 00 fe 80 b1 00 27 00 8f 0c 8a 00 00 00 02 00 06 00 1a 00 83 00 00 00 02 00 01 00 00 00 00 00 03 00 00 00 2e 97 ba b8 d2 00 00 00 c1 00 00
字段 |
字节数 |
说明 |
命令(0x0070) |
2 |
打开请求,固定为0x 00 70 |
长度 |
2 |
命令指定数据的大小 |
会话句柄 |
4 |
注册应答中返回的数值 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
固定为0x00 00 00 00 00 00 00 00 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
接口句柄(0x00000000) |
4 |
默认为0x00000000(CIP) |
超时(0x0000) |
2 |
默认为0x0000 |
项数(0x0002) |
2 |
默认为0x0002 |
连接的地址项 |
2 |
默认为0x00 A1 |
长度 |
2 |
默认为0x00 04 |
连接标识 |
4 |
和打开应答中的 T-O NetWork Connection ID相同 |
连接的数据项 |
2 |
默认为(0x00b1) |
长度 |
2 |
后面数据包的长度 |
序号 |
2 |
和数据服务请求帧中的序号相同 |
以下是CIP协议的内容 |
||
服务 |
1 |
固定为0x8A |
填充字节 |
1 |
固定为0x00 |
状态 |
2 |
状态好时为0x00 00 |
服务数(即测点数) |
2 |
应答数据点的个数 |
偏移量(和服务数相同) |
2*(服务数) |
从服务数第一个字节算起,每个服务的偏移量 |
应答服务1(即测点1) |
||
服务标识 |
1 |
0x83表示请求属性应答,0xd2读取数据标签应答 |
填充字节 |
1 |
固定为0x00 |
状态 |
1 |
状态好时为0x00 |
附加状态大小 |
1 |
默认0x00 |
数据类型 |
2 |
目前已知0x00C3(195)为整型,0x00CA(202)为实型,0x00C1(193)为布尔型,long型为0x00C4,BYTE为0x00C2 |
数据 |
|
|
应答服务2(即测点2) |
||
……. |
读写请求在第一层命令使用的都是0x70命令,都是使用0x0a多数据包命令,只是在每个数据服务请求包时不同,读数据的命令0x52,写为0x53
写入标签名称PC_PID_S0.S0_PID_VALVEAVERAGETEMP_D,PC_PID_S0.S0_PID_VALVEAVERAGETEMP_D1
70 00 9C 00 E8 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 A1 00 04 00 48 7C 9C FF B1 00 88 00 D1 15 0A 02 20 02 24 01 03 00 08 00 14 00 4A 00 03 02 20 AC 24 01 02 00 01 00 03 00 53 14 91 09 50 43 5F 50 49 44 5F 53 30 00 91 19 53 30 5F 50 49 44 5F 56 41 4C 56 45 41 56 45 52 41 47 45 54 45 4D 50 5F 44 00 CA 00 01 00 00 00 00 00 85 EB 51 41 53 14 91 09 50 43 5F 50 49 44 5F 53 30 00 91 1A 53 30 5F 50 49 44 5F 56 41 4C 56 45 41 56 45 52 41 47 45 54 45 4D 50 5F 44 31 C4 00 01 00 00 00 00 00 16 00 00 00
字段 |
字节数 |
说明 |
命令(0x0070) |
2 |
打开请求,固定为0x 00 70 |
长度 |
2 |
命令指定数据的大小 |
会话句柄 |
4 |
注册应答中返回的数值 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
接口句柄(0x00000000) |
4 |
默认为0x00000000(CIP) |
超时(0x0001) |
2 |
默认为0x0001 |
项数(0x0002) |
2 |
默认为0x0002 |
连接的地址项 |
2 |
默认为0x00 A1 |
长度 |
2 |
默认为0x00 04 |
连接标识 |
4 |
和打开应答中的 O-T NetWork Connection ID相同 |
连接的数据项 |
2 |
默认为(0x00b1) |
长度 |
2 |
后面数据包的长度 |
序号 |
2 |
数据服务请求帧的序号(从1开始) |
以下是CIP协议的内容 |
||
服务 |
1 |
固定为0x0A |
请求路径大小 |
1 |
固定为0x02 |
请求路径 |
4 |
固定为0x01240220 |
服务数(即测点数) |
2 |
请求数据点的个数 |
偏移量(和服务数相同) |
2*(服务数) |
从服务数第一个字节算起,每个服务的偏移量 |
服务标识 |
1 |
0x53为写数据服务 |
请求路径大小 |
1 |
目前总结的公式是size = (len +1)/2 +1; 其中size为请求路径大小,len为请求侧点名的长度 |
扩展符号 |
1 |
固定为0x91 |
数据大小 |
1 |
该服务所对应的PLC中的侧点名大小 |
数据内容 |
|
该服务所对应的PLC中的侧点名 |
目前,发现的规律是侧点名的长度是奇数时,有一个填充字节,偶数时不填充 |
||
数据类型 |
2 |
目前已知0x00C3(195)为整型,0x00CA(202)为实型,0x00C1(193)为布尔型,long型为0x00C4,BYTE为0x00C2 |
服务命令指定数据 |
2 |
服务类型为0x53时固定为0x00 01 |
预留 |
4 |
服务类型为0x53时固定为0x00000000 |
数据内容 |
|
长度由类型决定,BYTE和BOOL一个字节,整型两个字节,float和long四个字节 |
70 00 3E 00 E8 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 A1 00 04 00 48 7C 9C FF B1 00 2A 00 D1 15 8A 00 00 00 03 00 08 00 1C 00 20 00 83 00 00 00 02 00 01 00 00 00 00 00 03 00 00 00 2E 97 BA B8 D3 00 00 00 D3 00 00 00
字段 |
字节数 |
说明 |
命令(0x0070) |
2 |
打开请求,固定为0x 00 70 |
长度 |
2 |
命令指定数据的大小 |
会话句柄 |
4 |
注册应答中返回的数值 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
固定为0x00 00 00 00 00 00 00 00 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
接口句柄(0x00000000) |
4 |
默认为0x00000000(CIP) |
超时(0x0000) |
2 |
默认为0x0000 |
项数(0x0002) |
2 |
默认为0x0002 |
连接的地址项 |
2 |
默认为0x00 A1 |
长度 |
2 |
默认为0x00 04 |
连接标识 |
4 |
和打开应答中的 T-O NetWork Connection ID相同 |
连接的数据项 |
2 |
默认为(0x00b1) |
长度 |
2 |
后面数据包的长度 |
序号 |
2 |
和数据服务请求帧中的序号相同 |
以下是CIP协议的内容 |
||
服务 |
1 |
固定为0x8A |
填充字节 |
1 |
固定为0x00 |
状态 |
2 |
状态好时为0x00 00 |
服务数(即测点数) |
2 |
应答数据点的个数 |
偏移量(和服务数相同) |
2*(服务数) |
从服务数第一个字节算起,每个服务的偏移量 |
服务标识 |
1 |
写服务应答为0xD3 |
填充字节 |
1 |
固定为0x00 |
状态 |
2 |
状态好时为0x00 00 |
6f 00 28 00 6a 01 64 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 b2 00 18 00 4e 02 20 06 24 01 05 f7 02 00 1b 05 f8 f8 2f 00 03 00 01 00 20 02 24 01
字段 |
字节数 |
说明 |
命令(0x006F) |
2 |
关闭请求 |
长度(0x0028) |
2 |
固定为40字节(0x0028) |
会话句柄 |
4 |
注册应答中返回的数值 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求关闭一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
接口句柄(0x00000000) |
4 |
默认为0x00000000(CIP) |
超时(0x0001) |
2 |
默认为0x0001 |
项数(0x0002) |
2 |
默认为0x0002 |
空地址项(0x0000) |
2 |
默认为0x0000 |
长度(0x0000) |
2 |
默认为0x0000 |
未连接数据项(0x00b2) |
2 |
默认为(0x00b2) |
长度(0x0018) |
2 |
后面数据包的长度(24个字节) |
以下是CIP协议的内容 |
||
服务 |
1 |
固定为0x4E |
请求路径大小 |
1 |
固定为0x02 |
请求路径 |
4 |
固定为0x01240620(有可能会改变) |
Priority/time_tick |
1 |
固定为0x0A |
Time-out_ticks |
1 |
固定为0x05 |
Connection Serial Number |
2 |
0x0000 |
Verder ID |
2 |
0x0101 |
Originator Serial Number |
4 |
和打开请求帧中的 T-O NetWork Connection ID相同 |
连接路径大小 |
1 |
0x03 |
保留数据 |
1 |
0x00 |
连接路径 |
6 |
0x01 24 02 20 00 01 |
6f 00 1e 00 6a 01 64 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 b2 00 0e 00 ce 00 00 00 02 00 1b 05 f8 f8 2f 00 00 00
字段 |
字节数 |
说明 |
命令(0x006F) |
2 |
关闭应答 |
长度(0x001E) |
2 |
正常情况为30字节(0x001E) |
会话句柄 |
4 |
注册应答中返回的数值 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
接口句柄(0x00000000) |
4 |
默认为0x00000000(CIP) |
超时(0x0001) |
2 |
默认为0x0001 |
项数(0x0002) |
2 |
默认为0x0002 |
空地址项(0x0000) |
2 |
默认为0x0000 |
长度(0x0000) |
2 |
默认为0x0000 |
未连接数据项(0x00b2) |
2 |
默认为(0x00b2) |
长度(0x000E) |
2 |
后面数据包的长度(14个字节) |
以下是CIP协议的内容 |
||
服务 |
1 |
固定为0xCE |
保留数据 |
1 |
固定为0x00 |
状态 |
2 |
状态好为0x0000 |
Connection Serial Number |
2 |
0x0000 |
Verder ID |
2 |
0x0101 |
Originator Serial Number |
4 |
和T-O NetWork Connection ID相同 |
保留数据 |
2 |
0x00 00 |
66 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
字段 |
字节数 |
说明 |
命令(0x0066) |
2 |
卸载注册请求 |
长度(0x0004) |
2 |
|
会话句柄(0x00000000) |
4 |
初始值为0x00000000 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
以下是命令指定数据(Command Specific Data) |
||
协议版本(0x0001) |
2 |
默认为0x0001 |
选项标记(0x0000) |
2 |
默认为0x0000 |
66 00 00 00 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
字段 |
字节数 |
说明 |
命令(0x0066) |
2 |
卸载注册请求 |
长度(0x0000) |
2 |
|
会话句柄(0x00000000) |
4 |
初始值为0x00000000 |
状态(0x00000000) |
4 |
初始值为0x00000000(状态好) |
发送方描述 |
8 |
请求通信一方的说明 |
选项 |
4 |
默认为0x00000000 |
数据类型名称 |
编码 |
说明 |
BOOL |
C1 |
Logical Boolean with values TRUE and FALSE |
SINT |
C2 |
Signed 8–bit integer value |
INT |
C3 |
Signed 16–bit integer value |
DINT |
C4 |
Signed 32–bit integer value |
LINT |
C5 |
Signed 64–bit integer value |
USINT |
C6 |
Unsigned 8–bit integer value |
UINT |
C7 |
Unsigned 16–bit integer value |
UDINT |
C8 |
Unsigned 32–bit integer value |
ULINT |
C9 |
Unsigned 64–bit integer value |
REAL |
CA |
32–bit floating point value |
LREAL |
CB |
64–bit floating point value |
STIME |
CC |
Synchronous time information |
DATE |
CD |
Date information |
TIME_OF_DAY |
CE |
Time of day |
DATE_AND_TIME |
CF |
Date and time of day |
STRING |
D0 |
character string (1 byte per character) |
BYTE |
D1 |
bit string - 8-bits |
WORD |
D2 |
bit string - 16-bits |
DWORD |
D3 |
bit string - 32-bits |
LWORD |
D4 |
bit string - 64-bits |
STRING2 |
D5 |
character string (2 bytes per character) |
FTIME |
D6 |
Duration (high resolution) |
LTIME |
D7 |
Duration (long) |
ITIME |
D8 |
Duration (short) |
STRINGN |
D9 |
character string (N bytes per character) |
SHORT_STRING |
DA |
character sting (1 byte per character, 1 byte length indicator) |
TIME |
DB |
Duration (milliseconds) |
EPATH |
DC |
CIP path segments |
ENGUNIT |
DD |
Engineering Units |
在读取多标签时要对每次请求的标签的数据包进行优化分组。首先需要计算出每个标签对应的请求指令的字节大小,在对此进行动态规划划分最优组。
///
/// 获得当前标签名称对应的发送指令的长度
///
/// 标签名称
///
private static int GetBuilderReadTag(string tag)
{
List rtn = new List();
string[] str = tag.Split('.');
rtn.Add(0x52); //0x52 为UnconnectedSend, 0x4c为OpenConnected
rtn.Add(0x00);
for (int i = 0; i < str.Length; i++)
{
rtn.Add(0x91);
rtn.Add((byte)str[i].Length);
rtn.AddRange(Encoding.Default.GetBytes(str[i]));
if (str[i].Length % 2 != 0)
{
rtn.Add(0);
}
}
rtn.AddRange(new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 });
return rtn.Count;
}
///
/// 根据容量获得对标签的分组
///
/// 标签列表
/// 容量
///
public static List> GetGroupTags(List data, int capacity)
{
List> rtn = new List>();
List allTags = new List();
foreach(Tag tag in data)
{
allTags.Add(tag);
}
while (allTags.Count > 0)
{
List group = GetTags(allTags, capacity);
rtn.Add(group);
foreach (Tag tag in group)
{
allTags.Remove(tag);
}
}
return rtn;
}
///
/// 获得当前标签中的按指定容量的最优组合
///
/// 标签名称列表
/// 容量
///
public static List GetTags(List data, int capacity)
{
List rtn = new List();
int[] w = new int[data.Count + 1];
for (int i = 1; i < w.Length; i++)
{
w[i] = GetBuilderReadTag(data[i - 1].Code) + 2;
}
int[,] m = new int[data.Count + 1, capacity + 1];
for (int i = 1; i <= data.Count; i++)
{
for (int j = 1; j <= capacity; j++)
{
if (j >= w[i])
{
m[i, j] = Math.Max(m[i - 1, j], m[i - 1, j - w[i]] + w[i]);
}
else
{
m[i, j] = m[i - 1, j];
}
}
}
int[] x = new int[w.Length];
for (int i = data.Count; i > 1; i--)
{
if (m[i, capacity] == m[i - 1, capacity])
{
x[i] = 0;
}
else
{
x[i] = 1;
capacity -= w[i];
}
}
x[1] = (m[1, capacity] > 0) ? 1 : 0;
for (int i = 1; i < x.Length; i++)
{
if (x[i] == 1)
{
rtn.Add(data[i - 1]);
}
}
return rtn;
}
软件效果展现
软件下载地址:
下载软件