一直想把三菱和西门子这两个使用频率最高的PLC上位通讯,融合到WCS系统的框架里;
现在三菱主流使用Q系列,使用的是MC协议, 前一段时间也写过一个入门介绍:
三菱Q系列通讯方式设计说明
去年8月份,无意中发现用网络抓包工具可以一点不漏的抓取通讯包,简单摸索一下,也把规律摸索的七七八八了,
也写了一个简单的说明:
终于破解了西门子通讯协议
但是真正用于项目,就需要100%的摸索出来;
一直都想早点弄出来,代替OPC, 但是拖延症太厉害,一直拖了半年,都没有进一步去完善;
这两天过年期间,难得心里静下来,不去考虑老项目维护,也暂时把手头上新项目先放下,脑子可以放空,把这个拖延了10几年的问题先处理掉;
这么多年来,做项目一直计较框架的设计; 每次新项目下来,都有些新的个性化的东西, 这时都会就会发现之前的框架里又有些不满足的地方;
这时候就会纠结; 到底是沿用老框架,迅速做项目;还是先把老框架升级好,再做项目;
如果不革新,项目就压的你就没机会升级;小米的MIUI基本每周升级一次;我们这点升级其实算不了什么;
这么大岁数了,能干活也没几年了,在避免不了的被淘汰之前, 还是咬牙升级框架升级好;不要被甩的太远;
想想白居易的<琵芭行>, 印象最深的几句:门前冷落鞍马稀,老大嫁作商人妇,商人重利轻别离 ;一直靠拼体力生存,岁月最终会让你体会到门可罗雀的凄凉;
再看看最冯小刚主演的<老炮儿>; 也许未曾拥有,就被时代的洪流给淹没了;如今互谅网时代,苟延残喘的混着吧。
言归正传,先说说
1.西门子和三菱的几个区别(上位只关心的通讯层面):
d. plc回复的第10个字节一个是06,一个是04,这个好像是小型号的区别;
细节摸索下来:1200该字节是06,314是04,315是03;咱写程序的时候,就不要考虑这个来校验了;
3.2交互二
PC发出报文
03 00 00 19 02 F0 80 32 01 00 00 FF FF 00 08 00
00 F0 00 00 01 00 01 07 80
PLC回复报文
03 00 00 1B 02 F0 80 32 03 00 00 FF FF 00 08 00
00 00 00 F0 00 00 01 00 01 00 F0
第二个初始化报文交互,通过1200 和314,315的对比,发现居然完全没有任何区别;
所以我们可以把这个交互完全固话;
到此,整个初始化处理就算结束了,正常在设计架构的时候,可以这么实现:
在ClentSocket的onConnect(即正常连接上)的瞬间,pc给plc发起第一个初始请求,得到回复后(为了简单,就仅仅判断长度为22即可);
立刻发起第二个固定的初始话请求,得到长度为24的报文后,就通过一个布尔变量通知整个系统可以正常读写;
4.读操作
读demo1:
西门子1200: 读取DB10, count=17 ,offset=19
PC发出报文
(A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x001C=序列号,A[24]~A[25]=0x0011=17=读取请求count;
A[26]~A[27]=0x000A=10=DB10, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x000098=152=198=读取偏移量offset(bit为单位) )
03 00 00 1F 02 F0 80 32 01 00 00 00 1C 00 0E 00
00 04 01 12 0A 10 02 00 11 00 0A 84 00 00 98
PLC回复报文:
(B[3]~B[4]=0x002A=42=回复报文总长度, B[12]~B[13]=0x001C=序列号,B[16]~B[17]=0x0015=21=读取请求count(17)+4
B[24]~B[25]=0x0088=178=请求数据长度(bit为单位), B[26]~最后=数据值)
03 00 00 2A 02 F0 80 32 03 00 00 00 1C 00 02 00
15 00 00 04 01 FF 04 00 88 13 14 15 16 17 00 00
00 00 00 00 00 00 00 00 00 00
读demo2:
西门子1200: 读取DB11, count=17 ,offset=19
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x008E=序列号,A[24]~A[25]=0x0011=17=读取请求count;
A[26]~A[27]=0x000B=11=DB11, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x000098=152=198=读取偏移量offset(bit为单位) )
03 00 00 1F 02 F0 80 32 01 00 00 00 8E 00 0E 00
00 04 01 12 0A 10 02 00 11 00 0B 84 00 00 98
PLC回复报文:
(B[3]~B[4]=0x002A=42=回复报文总长度, B[12]~B[13]=0x001C=序列号,B[16]~B[17]=0x0015=21=读取请求count(17)+4
B[24]~B[25]=0x0088=178=请求数据长度(bit为单位), B[26]~B[42]=数据值)
03 00 00 2A 02 F0 80 32 03 00 00 00 8E 00 02 00
15 00 00 04 01 FF 04 00 88 13 14 15 16 17 18 00
00 00 00 00 00 00 00 21 22 23
读demo3:
西门子1200:读取DB11, count=16 ,offset=18
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0013=序列号,A[24]~A[25]=0x0010=16=读取请求count;
A[26]~A[27]=0x000B=11=DB11, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x000090=146=188=读取偏移量offset(bit为单位) )
03 00 00 1F 02 F0 80 32 01 00 00 00 13 00 0E 00
00 04 01 12 0A 10 02 00 10 00 0B 84 00 00 90
PLC回复报文:
(B[3]~B[4]=0x0029=41=回复报文总长度, B[12]~B[13]=0x0013=序列号,B[16]~B[17]=0x0014=20=读取请求count(16)+4
B[24]~B[25]=0x0080=168=请求数据长度(bit为单位), B[26]~B[41]=数据值)
03 00 00 29 02 F0 80 32 03 00 00 00 13 00 02 00
14 00 00 04 01 FF 04 00 80 00 13 14 15 16 17 18
00 00 00 00 00 00 00 00 21
读demo4:
西门子300 (314) 读取D50, count=20 ,offset=4000
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0028=序列号,A[24]~A[25]=0x0014=20=读取请求count;
A[26]~A[27]=0x0032=50=DB50, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x007D00=32000
=40008=读取偏移量offset(bit为单位) )
03 00 00 1F02 F0 80 32 01 00 00 00 28 00 0E 00
00 04 01 12 0A 10 02 00 14 00 32 8400 7D 00
PLC回复报文:
(B[3]~B[4]=0x002D=45=回复报文总长度, B[12]~B[13]=0x0028=序列号,B[16]~B[17]=0x0018=24=读取请求count(20)+4
B[24]~B[25]=0x00A0=208=请求数据长度(bit为单位), B[26]~B[45]=数据值)
03 00 00 2D02 F0 80 32 03 00 00 00 28 00 02 00
1800 00 04 01 FF 04 00 A0 00 04 0E AB 00 00 00
00 00 00 03 00 00 00 00 00 00 00 00 00
读demo5:
西门子300 (315) 读取D10, count=100 ,offset=2
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0003=序列号,A[24]~A[25]=0x0064=100=读取请求count;
A[26]~A[27]=0x000A=10=DB10, A[28]=0x84=读取的数据类型为DB块,A[29]~A[31]=0x000010=16=28=读取偏移量offset(bit为单位) )
03 00 00 1F 02 F0 80 32 01 00 00 00 03 00 0E 00
00 04 01 12 0A 10 02 00 64 00 0A 84 00 00 10
PLC回复报文:
(B[3]~B[4]=0x007D=125=回复报文总长度, B[12]~B[13]=0x0003=序列号,B[16]~B[17]=0x0068=104=读取请求count(100)+4
B[24]~B[25]=0x0320=1008=请求数据长度(bit为单位), B[26]~B[125]=数据值)
03 00 00 7D 02 F0 80 32 03 00 00 00 03 00 02 00
68 00 00 04 01 FF 04 03 20 00 00 00 01 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00
读demo6:
西门子1200 读取X输入(input)两个byte:
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0002=序列号,A[24]~A[25]=0x0002=2=读取请求count;
A[26]~A[27]=0x000A=10=DB10[其实这里写什么都可以,因为input不属于DB块],
A[28]=0x81=读取的数据类型为Input,A[29]~A[31]=0x000000=0=08=读取偏移量offset(bit为单位) )
03 00 00 1F 02 F0 80 32 01 00 00 00 02 00 0E 00
00 04 01 12 0A 10 02 00 02 00 0A 81 00 00 00
PLC回复报文:
(B[3]~B[4]=0x001B=27=回复报文总长度, B[12]~B[13]=0x0002=序列号,B[16]~B[17]=0x0068=104=读取请求count(100)+4
B[24]~B[25]=0x0320=1008=请求数据长度(bit为单位), B[26]~B[27]=数据值)
03 00 00 1B 02 F0 80 32 03 00 00 00 02 00 02 00
06 00 00 04 01 FF 04 00 10 08 00
读demo7:
西门子1200 读取Y输出(output)两个byte:
PC发出报文:
(A[3]~A[4]=0x001F=31=读取报文总长度, A[12]~A[13]=0x0001=序列号,A[24]~A[25]=0x0002=2=读取请求count;
A[26]~A[27]=0x000A=10=DB10[其实这里写什么都可以,因为input不属于DB块],
A[28]=0x82=读取的数据类型为Output,A[29]~A[31]=0x000000=0=08=读取偏移量offset(bit为单位) )
03 00 00 1F 02 F0 80 32 01 00 00 00 01 00 0E 00
00 04 01 12 0A 10 02 00 02 00 0A 82 00 00 00
PLC回复报文:
(B[3]~B[4]=0x001B=27=回复报文总长度, B[12]~B[13]=0x0002=序列号,B[16]~B[17]=0x0068=104=读取请求count(100)+4
B[24]~B[25]=0x0320=1008=请求数据长度(bit为单位), B[26]~B[27]=数据值)
03 00 00 1B 02 F0 80 32 03 00 00 00 01 00 02 00
06 00 00 04 01 FF 04 00 10 05 00
读demo8:
西门子1200 读取flag两个byte:
PC发出报文:
03 00 00 1F 02 F0 80 32 01 00 00 05 65 00 0E 00
00 04 01 12 0A 10 02 00 02 00 09 83 00 00 00
PLC回复报文:
(B[3]~B[4]=0x001B=27=回复报文总长度, B[12]~B[13]=0x0565=序列号,B[16]~B[17]=0x0006=6=读取请求count(2)+4
B[24]~B[25]=0x0010=2*8=请求数据长度(bit为单位), B[26]~B[27]=数据值)
03 00 00 1B 02 F0 80 32 03 00 00 05 65 00 02 00
06 00 00 04 01 FF 04 00 10 FF 17
根据以上8个报文的demo,摸索出大致规律如下(未必完全正确,但是应付项目可以了);
A[1]~A[2]: 03 00 固定报文头;
A[3]~A[4]: 00 1F 整个读取请求长度为0x1F= 31 ;
A[5]~A[11]: 02 F0 80 32 01 00 00 固定6个字节;
A[12]~A[13]: 两个字节,标识序列号,回复报文相同位置和这个完全一样;范围是0~65535;
A[14]~A[23]:00 0E 00 00 04 01 12 0A 10 02 固定10个字节
A[24]~A[25]:两个字节,访问数据的个数,以byte为单位;
A[26]~A[27]: DB块的编号,比如DB50, 就是0x32=50, 两个字节,范围是0~65535(也许是一个1个字节,因为没有设置估DB255以上的数据块,所以不知道到底是几个字节,姑且认为是2个字节);
A[28] : 访问数据块的类型:0x81-input ,0x82-output ,0x83-flag , 0x84-DB(这个最常见);
A[29]~A[31]: 访问DB块的偏移量offset (地址+1以byte为单位); 3个字节,范围是0~16777216(一般 用不到这么大)
程序设计的时候,其实主要关注最后4个信息,即:
原文链接:https://m.baidu.com/mip/c/www.360doc.cn/mip/763580999.html
原文:https://www.cnblogs.com/zhaopei/p/11691613.html