python 读写三菱PLC数据,使用以太网读写Q系列,L系列,Fx系列的PLC数据
本文将使用一个gitHub开源的组件技术来读写三菱的plc数据,使用的是基于以太网的TCP/IP实现,不需要额外的组件,读取操作只要放到后台线程就不会卡死线程,本组件支持超级方便的高性能读写操作
github地址:https://github.com/dathlin/HslCommunication
如果喜欢可以star或是fork,还可以打赏支持。
官网地址:http://www.hslcommunication.cn/
Python版本地址:https://github.com/dathlin/HslCommunicationPython
商业使用请联系作者:QQ:200962190 或是加QQ群1:592132877(满) QQ群2: 948305931 QQ群3: 767856490 或是赞助240元加入QQ群VIP:838185568
里面各种小伙伴,为您解答数据交互,编程技巧,如果对本界面提供的API有任何疑问,都可以加群咨询,如果有更好的建议,欢迎提出。
本文将展示如何配置网络参数及怎样使用代码来访问PLC数据,希望给有需要的人解决一些实际问题。主要对三菱的不同协议的不同数据块的数据读写,亲测有效。
此处使用了网线直接的方式,如果PLC接进了局域网,就可以进行远程读写了^_^
导入 HslCommunication.py 后,事实上只需要这么一个库就够了,环境要求为python3.4+,支持的PLC的协议为MC协议,主要支持的细节如下:
1 | pip install HslCommunication |
Qna3E协议的二进制类 MelsecMcNet
Qna3E协议的ascii类 MelsecMcAsciiNet
A兼容1E协议的二进制类 MelsecA1ENet
此处以3E协议的二进制为示例,其余两种的数据操作是一模一样的,如下:
1 | from HslCommunication import MelsecMcNet |
随便聊聊
当我们一个上位机需要读取100台西门子PLC设备(此处只是举个例子,凡是都是使用Modbus tcp的都是一样的)的时候,你采用服务器主动去请求100台设备的机制对性能来说是个极大的考验,如果开100个线程去轮询100台设备,那么性能损失将是非常大的,更不用说再增加设备,如果搭建Modbus tcp服务器,就可以完美的解决性能问题,因为连接的压力将会平均分摊给每一台PLC,服务器端只要新增一个时间戳就可以知道客户端有没有连接上。
我们在100台PLC里都增加发送Modbus tcp方法,将数据发送到服务器的ip和端口上去,服务器根据站号来区分设备。这样就可以搭建一个高性能总站。 本组件支持快速搭建一个高性能的Modbus tcp总站。
http://www.cnblogs.com/dathlin/p/7782315.html
关于两种模式
在PLC端,包括三菱,西门子,欧姆龙以及Modbus Tcp客户端的访问器上,都支持两种模式,短连接模式和长连接模式,现在就来解释下什么原理。
短连接:每次读写都是一个单独的请求,请求完毕也就关闭了,如果服务器的端口仅仅支持单连接,那么关闭后这个端口可以被其他连接复用,但是在频繁的网络请求下,容易发生异常,会有其他的请求不成功,尤其是多线程的情况下。
长连接:创建一个公用的连接通道,所有的读写请求都利用这个通道来完成,这样的话,读写性能更快速,即时多线程调用也不会影响,内部有同步机制。如果服务器的端口仅仅支持单连接,那么这个端口就被占用了,比如三菱的端口机制,西门子的Modbus tcp端口机制也是这样的。以下代码默认使用长连接,性能更高,还支持多线程同步。
在短连接的模式下,每次请求都是单独的访问,所以没有重连的困扰,在长连接的模式下,如果本次请求失败了,在下次请求的时候,会自动重新连接服务器,直到请求成功为止。另外,尽量所有的读写都对结果的成功进行判断。
关于日志记录
不管是三菱的数据访问类,还是西门子的,还是Modbus tcp访问类,都有一个LogNet属性用来记录日志,该属性是一个接口类,ILogNet,凡事继承该接口的都可以用来记录日志,该日志会在访问失败时,尤其是因为网络的原因导致访问失败时会进行日志记录(如果你为这个 LogNet 属性配置了真实的日志记录器的话):如果你想使用该记录日志的功能,请参照如下的博客进行实例化:
python版本暂不支持。
访问测试项目
https://github.com/dathlin/HslCommunicationPython
基于PYQT5开发的DEMO项目
演示项目
下面的三篇演示了具体如何去访问PLC的数据,我们在访问完成后,通常需要进行处理,以下的示例项目就演示了后台从PLC读取数据后,前台显示并推送给所有在线客户端的功能,客户端并进行图形化显示,具有一定的参考意义,并且推送给网页前端,项目地址为:
https://github.com/dathlin/RemoteMonitor
下面的图片示例中的左边程序就是服务器程序,它应该和PLC直接连接并接入局域网,然后把数据推送给客户端显示。注意:一个复杂高级的程序就应该把处理逻辑程序和界面程序分开,比如这里的服务器程序实现数据采集,推送,存储。让客户端程序去实现数据的整理,分析,显示,这样即使客户端程序因为BUG奔溃,服务器端仍然可以正常的工作。
三菱PLC篇(下面列举了三种配置方法,本组件支持二进制和ASCII通讯,支持1E帧兼容协议访问)
Q06UDV Plc的访问测试感谢:hwdq0012
fx5u plc的访问测试感谢:山楂
Q02CPU, L02CPU-CM : 本人测试
感谢:小懒猪雨中人 的测试,VB程序也可以调用本通讯库
环境1:此处以GX Works3为示例,fx5u的配置如下:(感谢 山楂 提供的图片)
环境2:此处以GX Works2为示例,测试PLC为L02CPU,内置了以太网协议
环境3:此处以GX Works2为示例,添加以太网模块,型号为QJ71E71-100,组态里添加完成后进行以太网的参数配置,此处需要注意的是:参数的配置对接下来的代码中配置参数要一一对应
注意:在PLC的以太网模块的配置中,无法设置网络号为0,也无法设置站号为0, 所以此处均设置为1,在C#程序中也使用上述的配置,在代码中均配置为0,如果您自定义设置为网络2, 站号8,那么在代码中就要写对应的数据。如果仍然通信失败,重新测试0,0。
打开设置:在上图中的打开设置选项,进行其他参数的配置,下图只是举了一个例子,开通了4个端口来支持读写操作:
端口号设置规则:
- 为了不与原先存在的系统发生冲突,您在添加自己的端口时尽量使用您自己的端口。
- 如果读写都需要,尽可能的将读取端口和写入端口区分开来,这样做比较高性能。
- 如果您的网络状态不是特别稳定,读取端口使用2个,一个受阻切换另一个读取可以提升系统的稳定性。
本文档仅作组件的测试,所以只用了一个端口作为读写。如果你的程序也使用了一个端口,那么你在读取数据时候, 刚好也在写入(异步操作可能发生这样的情况),那么写入会失败!)(在长连接模式下没有这个问题)
三菱PLC的数据主要由两类数据组成,位数据和字数据,在位数据中,例如X,Y,M,L都是位数据,字数据例如D,W。 两类的数据在读取解码上存在一点小差别。(事实上也可以先将16个M先赋值给一个D,读取D数据再进行解析, 在读取M的数量比较多的时候,这样操作效率更高)
关于两种地址的表示方式
第一种,使用系统的类来标识,比如M200,写成(MelsecDataType.M, 200)的表示形式,这样也可以去MelsecDataType里面找到所有支持的数据类型。
第二种,使用字符串表示,这个组件里所有的读写操作提供字符串表示的重载方法,所有的支持访问的类型对应如下,字符串的表示方式存在十进制和十六进制的区别:
- 输入继电器:"X100","X1A0" // 字符串为十六进制机制
- 输出继电器:"Y100" ,"Y1A0" // 字符串为十六进制机制
- 内部继电器:"M100","M200" // 字符串为十进制
- 锁存继电器:"L100" ,"L200" // 字符串为十进制
- 报警器: "F100", "F200" // 字符串为十进制
- 边沿继电器:"V100" , "V200" // 字符串为十进制
- 链接继电器:"B100" , "B1A0" // 字符串为十六进制
- 步进继电器:"S100" , "S200" // 字符串为十进制
- 数据寄存器:"D100", "D200" // 字符串为十进制
- 链接寄存器:"W100" ,"W1A0" // 字符串为十六进制
- 文件寄存器:"R100","R200" // 字符串为十进制
展示一些简单实用基础数据读写,这些数据的读写经过了实际的测试,如果需要更详细的文档,参考C#版本的,或是加VIP技术支持群咨询。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | from HslCommunication import MelsecMcNet from HslCommunication import SoftBasic def printReadResult(result): if result.IsSuccess: print (result.Content) else : print ( "failed " + result.Message) def printWriteResult(result): if result.IsSuccess: print ( "success" ) else : print ( "falied " + result.Message) if __name__ = = "__main__" : print (SoftBasic.GetUniqueStringByGuidAndRandom()) melsecNet = MelsecMcNet( "192.168.8.12" , 6002 ) if melsecNet.ConnectServer().IsSuccess = = False : print ( "connect falied " ) else : # bool read write test melsecNet.WriteBool( "M200" , True ) printReadResult(melsecNet.ReadBool( "M200" )) # bool array read write test melsecNet.WriteBool( "M300" ,[ True , False , True , True , False ]) printReadResult(melsecNet.ReadBool( "M300" , 5 )) # int16 read write test melsecNet.WriteInt16( "D200" , 12358 ) printReadResult(melsecNet.ReadInt16( "D200" )) # int16 read write test melsecNet.WriteInt16( "D201" , - 12358 ) printReadResult(melsecNet.ReadInt16( "D201" )) # uint16 read write test melsecNet.WriteUInt16( "D202" , 52358 ) printReadResult(melsecNet.ReadUInt16( "D202" )) # int32 read write test melsecNet.WriteInt32( "D210" , 12345678 ) printReadResult(melsecNet.ReadInt32( "D210" )) # int32 read write test melsecNet.WriteInt32( "D212" , - 12345678 ) printReadResult(melsecNet.ReadInt32( "D212" )) # uint32 read write test melsecNet.WriteUInt32( "D214" , 123456789 ) printReadResult(melsecNet.ReadInt32( "D214" )) # int64 read write test melsecNet.WriteInt64( "D220" , 12345678901234 ) printReadResult(melsecNet.ReadInt64( "D220" )) # float read write test melsecNet.WriteFloat( "D230" , 123.456 ) printReadResult(melsecNet.ReadFloat( "D230" )) # double read write test melsecNet.WriteDouble( "D240" , 123.456789 ) printReadResult(melsecNet.ReadDouble( "D240" )) # string read write test melsecNet.WriteString( "D250" , '123456' ) printReadResult(melsecNet.ReadString( "D250" , 3 )) # int16 array read write test melsecNet.WriteInt16( "D260" , [ 123 , 456 , 789 , - 1234 ]) printReadResult(melsecNet.ReadInt16( "D260" , 4 )) melsecNet.ConnectClose() |
需要注意的是,每次的数据交互您都可以判断是否成功,如果您不判断,如果网络断开或是异常,会影响程序的执行。
读写操作支持单个数和数组。
支持自定义的数据读写操作。直接调用Read方法,可以读取到原生的byte数组,然后进行组合数据。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理