勇哥注:
modbus通讯协议本身是可靠的,不然也不会发明起至今快50年了,还在自动化行业中广泛使用。
然而当应用各种第三方modbus组件编程的时候,由于组件的实现者、组件的应用者没有遵循标准来实现和应用,
就会造成各种通讯异常。
这就有点像一台pc机,硬件厂商能保证自己的硬件没有问题,然而架不住使用者乱装软件,最终还是可能会死机蓝屏。
使用modbus组件的使用者想怎么用就怎么用,没有章法,这是此问题的原因。
有关ModbusTCP的介绍及使用,见勇哥下面的教程:
Modbus Poll和Modbus slave软件应用(二)
http://www.skcircle.com/?id=2528
Modbus Poll和Modbus slave软件应用(一)
http://www.skcircle.com/?id=2494
ModbusTCP是主从模式的通讯架构,即Master/Slave。
game4automation实现了Slave从站。
Slave从站会公布自己的ip及端口号,若干个Master会连接它。
下图显示的是game4automation的Modubs组件参数面板。
可以看到没有任何设置。
原因是它的缺省值就是:ip为本机,可以是127.0.0.1,或者是本机的地址。
端口号为502。
端口号不可以改变。
master端通常由C#端使用某种ModbusTCP通讯组件来进行编程。
请使用github上开源的并且使用量较大的组件,例如EasyModbus。
开源很重要,不然通讯失败你都不知道是什么原因。
另外,不要牛B哄哄总想着自己去写,这里面水很深,不是一件容易的事。
勇哥重点说一下Master端访问要注意的内容:
(1)modbus的四个存储区(1.输入继电器、2.可读可写继电器、3.输入寄存器、4.可读可写寄存器),
请在程序里使用6个通信通道进行通讯(存储区1一个,2两个,3一个,4两个)。
原因是你一定会在多线程进通讯操作,用一个通道进行多个区的并发操作,死定了。
(2)对某通道信道操作的时候,你最好设计成队列方式,然后这个队列统一交给一个线程逐条处理这些通信请求
这是因为任何一次通道操作,都是有交互握手的过程,如果这个过程没完成,你再来一个,就会报下面这样的错误。
它实际上就是通讯阻塞了。
具体来说,就是两种写入的通道(继电器写入,寄存器写入)应该以这种队列来完成。
然后队列的追加操作内容的函数你应该把它Lock起来,这样就让调用的线程间进行了排队,有了先后次序。
(3)对于读的通道,设计一个线程专门定时读。
读的内容是4个通道:输入继电器、输入寄存器、可读可写继电器、可读可写寄存器。
读到的内容进行缓存。
任何读通道数据的api都设计成从缓存内容中获取数据。
(4)4个区的6个通道是可以并发进行操作的,这个没有问题。
因此读通道与写通道是可以并发的,不用互锁。
(5)以上设计中,由于读的线程的周期不固定,并且读线程和应用缓存数据的逻辑不在同一个周期内,因此要特别留意死等模式的逻辑判定。
这种模式是在一个死循环方式读一组缓存数据是否满足条件,如果满足就跳出,否则继续循环。
因为读线程和应用缓存数据的逻辑不在同一个周期内的原因,有可能对于“动作很快”的情况下,死循环里只有一次机会读到缓存数据才会退出,然而不幸的是读和应用者的周期不同,错过了瞬间的正确值后,就再也读不到退出条件的值了。
然后结果就是死循环成立,永远退不出去
然后由于这个死循环位于一个函数内,调用者每次调用都会卡在这个死循环中,函数其实是在反复重入而不能正常退出的,所以时间一长就会有内存与堆栈方面的问题。
另外还会造成一个现场,就是主逻辑卡死,不能继续。
因为这种原因,你得确保你的循环跳出条件是有机会多个执行周期内总有机会命中。
像那种起步时两个状态,停止时两个状态,它们是交替的,这一种逻辑判断往往是不可靠的,因为一但速度快了后,这种状态就是瞬间的过程,错过了就没有了。
总结一下:
多个通道之间可以并发,单个通道间的任何读写,只能一个完成后,再进行下一条指令。如果想做到这一点,可以考虑用队列方式来进行。

