市场上那么多PLC ;
其实国内用的最多的就是西门子,和三菱 ;
因为西门子之前一直用OPC,虽然之前偶然间破译了ISO ON TCP 这个最常用以太网协议的一部分,但是距离正在明白还有一段距离;
仍需要继续测试和验证;
德国的东西虽然质量好,就是讨厌不公开;那个倍福plc也是的,官方提供的方法是dll,最不喜欢dll,谁知道里面有什么漏洞;
西门子也有一个prodave的dll; 有个德国爱好者做的开源的libnodave的dll,专门针对它的;
反正德国佬的东西,用起来就是不爽;
英国的图灵在二战期间破解德国的“谜”式密码机,让二战提前3年结束都不为过;
所以有的时候破解的收益也是不小的;可以砍掉软件开发的成本,提高性能,能随心所欲的做更优化的控制方法和手段;
还是日本的东西更open一些,轻便一些,就像二战风靡一时的零式战机;
三菱PLC市场上最常用的就是FX系列和Q系列;
FX系列用的少,协议研究的不透,仅仅会简单的D点的读写;等以后用到,再细化;
Q系列,稍微大一些的项目里,主流用这个;
到三菱的办事处,也质询过,厂商也不是太懂通讯,他们建议给客户的方法,还是最通用的中间件方法:OPC ;
他们自己的号称卖几十万的组态软件工具也是基于OPC的;
如果实在不想用opc,也可以调用他们的MXComponent的组件,类似dll或ocx之类的;
当然老朽肯定不想他们现成的这两样东西;就像孙大圣除了定海神针,看不上东海龙王的普通兵器一样;
要弄就要用深入骨髓的本质的东西,这样才能灵活的做72变;
网上可以下载到《Q系列 MELSEC 参考手册》官方协议说明;
里面东西很多,其实未必都用到;
看手册太麻烦,没经验的朋友,上来可能就被吓住了;
根据2-8原理,项目中能用到20%就不做了,所以不用全看;
最好有真实的plc做实验,验证;
条件再好一些,可以安装一个opc server,在opc client里监控和更改plc内存值,用 抓包工具smartsniff 等监控数据变化;
就能更快的搞清楚协议的交互的具体特点;
一般项目里,我们常常用D点做通讯;
其实PLC里的所有类型的点,都可以通过socket通讯的方法任意读写;比如:输入 X,输出Y,辅助寄存器M,扩展卡内存ZR;
如果输入X点是常开,又没有接入设备;那么写入就会保持不被覆盖;
下面举例子说明读写方法:
1.读D100开始的连续的20个数据块,也就是读D100~D119的数据;
电脑读命令:50 00 00 FF FF 03 00 0C 00 10 00 01 04 00 00 64 00 00 A8 14 00 (23:19:46:937)
PLC反馈:D0 00 00 FF FF 03 00 2A 00 00 00 86 F1 00 00 C9 01 00 00 D6 02 00 00 68 02 00 00 2E 02 00 00 00 00 00 00 C3 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (23:19:46:937)
下面解析一下:
电脑读命令: 50 00(命令):表示发起指令,固定50 00;
00(网路编号) :上位访问下位,固定00;
FF(PLC编号) : 上位访问下位,固定FF;
FF 03(请求目标模块IO编号): 值要从小到大看,也就是反过来看,三菱所有的协值都是这样,所以这里是03FF,十进制是1023;也是固定的;
00(请求目标模块站编号):上位访问下位,固定00;
0C 00(应答数据物理长度): 也要反过来,值是000C,也就是12;表示后面的报文内容的长度是12(手工数一下,后面报文长度真的是12)
10 00(cpu监视定时器): 表示等待PLC响应的timeout时间;这里 值是0010,十进制是16 ;相当与最大等待时间250ms*16=4秒;实际上PLC一般2,3个毫秒内就响应了;
01 04(命令): 值是0401(所有值都要反过来看,再说就啰嗦了,后面不说了);表示批量读取;如果是1401就是随机读取;
00 00(子命令) : 值是0表示按字读取(1个字=16位),如果值是1就按位读取;
64 00 00(首地址):地址因为跨度比较大,所以用了3个字节;这里的值是000064,十进制就是100
A8 (软元件) : 表示读取PLC寄存器的类型:这里的A8表示D点;其他常见的有: 90-M点;9C-X点;9D-Y点;B0-ZR外部存储卡
14 00(读取长度):值是0014,十进制就是20;
PLC反馈:D0 00(响应):表示反馈信息,固定D0 00;
00(网络编号): 与上同
FF(PLC编号): 与上同
FF 03(请求目标模块IO编号): 与上同
00(请求目标模块站编号): 与上同
2A 00(应答数据物理长度):值是002A; 十进制是 42;也就是说后面的:结束代码(2个字节)+值=42;所以值是40个字节;2个字节=1个字;所以值是20个字;
00 00(结束代码):可以理解成异常代码,如果正常的话,就是0000
86 F1 00 00 C9 01 00 00 D6 02 00 00 68 02 00 00 2E 02 00 00 00 00 00 00 C3 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (反馈的值);这里正好40个字节;
再啰嗦一下,值要反过来看,所以地址D100=F186;D101=0000,D102=01C9......
2.写D100这一个点的值为13,D101这个点的值为14,D102这个点的值为15;
电脑写命令: 50 00 00 FF FF 03 00 12 00 10 00 01 14 00 00 64 00 00 A8 03 00 0C 00 0D 00 0E 00 (00:14:55:188)
PLC反馈:D0 00 00 FF FF 03 00 02 00 00 00 (00:14:55:188)
下面解析一下:
电脑写命令:
50 00(命令):意义和读一样
00(网络编号) :意义和读一样
FF(PLC编号) :意义和读一样
FF 03(请求目标模块IO编号):意义和读一样
00(请求目标模块站编号):意义和读一样
12 00(请求数据物理长度) :
值是0012,也就是十进制18;表示后面的报文内容的长度是20;
也就是后面的:cpu监视定时器+命令+子命令+首地址+软元件+长度+值=18;前面有12个字节;所以值是6个字节;也就是3个字;
10 00(cpu监视定时器):同上
01 14(命令):跟读的差别是:读是0104,写是0114 ;就是04和14的差别;
0000(子命令):值是0表示按字读写入1个字=16位),如果值是1就按位写入;
640000(首地址) :意义和读一样,10进制100;表示从D100这个首地址写入;
A8(软元件):意义和读一样
0300(长度): 值是0003 表示连续写入3个长度;
0C00 0D000E00(写入的值): D100=13 ,D101=14,D102=15;
PLC反馈(所有正常的写入,反馈的信息都固定为这个):
D0 00(响应):表示反馈信息,与读取反馈一样,固定D0 00;
00(网络编号 ):同上
FF(PLC编号):同上
FF 03 (请求目标模块IO编号):同上
00(请求目标模块站编号):同上
02 00(应答数据物理长度): 就是后面的 00 00 正好两个字节,所以这里值=2;
00 00(结束代码):可以理解成异常代码,如果正常的话,就是0000
还有其他常见的 X,Y ,M 点读写方法,就懒得再举例子了;
道理一样,区别是: X,Y,M 读取和写入不但可以用字,也可以用位;而且这样更容易分析;
不过我的用法是:读取的时候,按字读取,这样性能高; 写入的时候按位,因为写入一般不会16个相邻的点同时写入;
上面介绍的是连续读写的例子;
其实还支持随机读写,也就是在一个指令里读写不连续的数据块;
这个功能,以前一直犹豫要不要在框架里加上;随着项目逐渐复杂,数据点跨度也比较大;有的时候还是需要的;
这样同一个功能点,可以一个指令写入,确保了类似数据库事务一致性的处理;
上面只是简单的介绍了最常见的读写说明;
我们再做项目的时候,很多细节要琢磨;
比如在高速读写的时候,有的时候可能会产生“脏数据”;特别做非阻塞异步通讯的时候,或者多端口并发通讯的时候;
比如刚刚写入的值之后1个毫秒,前一个周期读取的旧值响应了,这样你认为写入的是3,可能1毫秒后得到了旧的读取的2,
但是第二次读取,就肯定为3了; 不做异步的多端口读写不需要考虑这个; 因为老朽过于计较性能;
所以所有的通讯都是多端口非阻塞异步进行的;处理办法是:写入后,第一次接受的值如果和刚刚写入的不一样,不做更新覆盖,仍然认为写入的是正确的;
当第二次读取的时候,如果再发现和之前的写入值不一致,才覆盖;