0 引言
( 可编程逻辑控制器) 是一种专门为在工业环境下应用而设计的数字运算操作电子系统。在工业上的使用十分广泛,其是许多大规模工作系统包括钢铁、石油、化工、电力、建材、机械制造、汽车、轻纺、交通、环保、文化娱乐等各行业中得到广泛的应用的基础控件。
虽然 对基础的工业控制能起到很好的效果,但对于更加复杂的应用场景,单纯使用PLC 进行控制难度较大。因此为了实现较为复杂的功能,在PLC 的上层,往往需要使用应用更加广泛的编程语言如C++、编写的复杂程序等进行控制,由于PLC 的运行与承载程序的上位计算机的底层逻辑并不相同,PLC 与上位计算机的成为工程上的重要课题。在实际应用中不同厂家所提供的PLC 方法并不相同,因此需要根据实际需求设计针对性的程序[1]。
欧姆龙PLC提供Fins协议作为与其通信的方法,为了搭建计算机主机与PLC 通信的桥梁,当计算机使用语言编写控制逻辑时,需要专门的 通信程序用于与PLC 通信。由于大部分的PLC 设备都不直接与Python程序直连,而是通过C/C++ 的方式作为中间层的程序,这使得网上公开的使用Python 程序基于Fins 协议通信欧姆龙PLC 的程序难以寻找且仅有的程序注释解释不足,在实际运行过程中容易出现各种问题,自己根据实际要求开发对应的Python 通信程序是必要的[2]。
本文介绍了使用Python 程序与欧姆龙PLC 进行通信的基本步骤以及在项目中Python 通信程序的具体构建方法,在如今基于Python 程序进行Fins 协议进行通信的资料较少的情况下,对同类型的项目有着借鉴作用。
1 Python程序与欧姆龙PLC通信
在进行程序上的通信前首先要建立物理上的连接。此次使用的欧姆龙PLC 型号为CP1H,其状态如图1 所示。此次选择的通讯方式是通过网线连接,一端连接计算机,一端连接PLC,用以传输数据,该PLC 还能够使用USB 连接,此时可以使用对应的调试程序对其进行修改,主要用于程序调试和测试。
硬件连接后,下一步则是进行软件的连接通讯,Fins协议属于数据层的协议,依赖于TCP/IP 协议,该型号的PLC 使用TCP 协议进行通讯,这里Fins 协议将作为TCP协议的数据进行传输。TCP协议的可靠性较好,但传输数据需要经过多次确认,因此在速度上略有损失。同时Fins 协议也规定了自己的返回信息。Fins 协议的数据传递逻辑如下。
图1 CP1H型PLC与连接
1.1 TCP连接
在主机与PLC 沟通之前,首先要建立TCP 的连接。这一部分可以调用Socket 包来实现,对于不同语言来说,需要设置的结构会存在一些差别。TCP 协议共需要经过3 次握手,第1 次握手为客户端向服务端发送网络包,以此发起连接,同时向服务端告知自己的传输能力。第2 次为服务端向客服端回发,以测试客户端的接受能力以及服务端的发送能力。第3 次客户端再次向服务端发信,进行进一步确认,从而进行联系。在TCP 层面,之后每次传递数据都会收到回复;
1.2 Fins连接
完成TCP 连接后,接下来依靠TCP 协议进行Fins协议的连接。对于Fins 协议连接也需要进行一次请求,这里的请求与TCP 不同,只需要进行两次,一次由主机向PLC 发送请求信息,另一次由PLC 发送确认信息,这一过程规定了Fins 协议通信中的一些基础信息,包括节点地址、节点网络号等;
1.3 PLC通信
当主机和PLC 完成确认后就可以进行信息的传输工作。对于Fins 协议通信,最重要的特点就是所有的数据请求都由主机发出,PLC 只进行被动应答操作。如对于读入操作,由主机发送信息,确认要进行读取以及读取的位置,PLC 接受到信号后按照信号进行操作,之后将数据进行返回[3],在这种情况下,主机被称为发送方,而PLC 被称为接收方。在这种框架下,命令只能由发送方发出,PLC 会将数据存储在实现规定的地方,让主机发布命令获取。
2 Fins数据帧
Fins 数据帧包括Fins header、Fins command 和Fins data3 部分,其中Fins header 用于对地址目标进行识别,Fins command 用于传递命令,而Fins data 用于传输所需数据。在Fins 协议中,发送方使用的命令帧和接收方使用的响应帧的结构是有所区别的,其中命令帧的主要结构如下。
2.1 Fins header
该部分共有10个字节,按顺序依次为ICF(Information Control Field),用于显示框架信息,由4 个子字段组成,有效信息包括是否使用网关、该数据的具体类型(响应或命令)以及是否必须进行回应,该字节还有5个保留位,在一些情况下可以指定一些特殊信息;RSV(Reserved),系统保留字;GCT(Gateway count),网关的允许数量;DNA(Destination network address)发送目标的网络地址,用于指定目标的节点网络号;DA1(Destination node number)目标节点地址,此处需要注意的是可以设置为FF,此时为广播编号;DA2(Source unit number)目的单位地址,指定目标节点的单元编号;SNA(Source network address)发送方的网络地址,用于指定发送方所在的节点网络号,SA1(Source node number)发送方的节点地址;SA2(Source Unit address)发送方的目的单位地址;SID(Service ID)服务序列号,用于标识生成传输过程,响应中返回相同数字来匹配命令和响应。以上各单元均占1 个字节;
2.2 Fins command
该部分共占两个字节,包括MRC 和SRC,分别储存了1 个字节的请求代码,其中MRC 为主代码,决定命令类别,SRC 为次要代码,决定类别下的具体命令。例如当MRC 为01 时,类型为I/O 读写,此时当SRC为01 时,该命令为读取I/O。读取Fins 协议能够指定的命令相当多,常见的操作包括读写I/O 信息、读写参数区信息、操作模式切换、状态读取、时间读取、读写故障信息、读写文件和强制设置某些位等;
2.3 Fins data
存放需要传输的数据,最大2 000 字节,实际结构受到Fins command部分的约束,即不同命令需要传输的数据格式是不同的。例如读取I/O 区时,data 区的结构为一个字节的I/O 储存区的对应编号、3 个字节的数据起始位置和两个字节的读取数据大小。
对于响应帧,其与命令帧的区别在于Fins data 部分增加了两个字节,分别为MRES、SRES,存储了命令帧的主相应和子响应代码,其和实际的传输数据合一起组成了FINS 数据域,这使得响应帧能够传输的实际数据最大值变为1 998 个字节。
确定了欧姆龙PLC 传输数据的基本格式后,接下来就是设计所需的Python 程序了。本设计使用了Python包的socket 进行编写,通过调用该包的方式,可以避免对TCP 协议的头部进行设计和模拟,简化操作。
3 PLC连接过程的程序设计
3.1 对PLC进行初步连通
在对PLC 进行Fins 协议通信前,首先要确认连通了PLC 本身。此时应输入PLC 的IP 地址,还需要PLC的端口号,欧姆龙PLC 的端口号默认为9 600,基本不会改变。计算机的IP 地址由函数进行获取,由于不同系统获取IP 地址的方式不同,需要对应编写函数,再进行汇总,再使用socket 包自带的TCP 通讯函数在TCP 层面将主机与PLC 进行连通,测试成功连接后就可以进行下一步骤。
3.2 对PLC进行握手
根据之前的PLC 沟通过程,对PLC 进行握手是进行数据传输的必要步骤。拼接PLC 帧的方式是首先根据通信帧的具体结构对得出各个部分的具体数据,根据要求转换为十六进制,再转化为字符串,拼接在一起。拼接完数据后,再将数据发送出去,等待PLC 的响应和对应的连接数据传回。数据传回后,对其进行核对,不为空则成功连接,否则输出连接失败。如果数据超过一定时间没有传回,同样输出连接失败;
3.3 使用PLC
本项目中,主要通过程序对PLC的数据区进行操作。一共涉及读数据和写数据两种操作。在读数据的过程中,需要分别设计Fins 帧中的3 部分,其中Fins header由于主要用于输出连接信息,Fins command 则需要输出命令的具体内容,包括读命令,读取的具体区域,本项目中指DM 区,读数据的具体起始地址以及读取的长度,Fins data 则为空。然后将3 段拼接起来进行输出。之后线程开始等待PLC 返回的具体数值,数据返回后,对命令进行解析判断,当格式符合要求时,读取对应的数据,并对其进行格式转换。等待需要设计1 个时限,当超过一定时间数据仍为进行返回或者没有返回正确数据时,认为发生了未知错误,程序报错并退出,并及时通知主程序。写数据与读数据存在一些区别,包括Finsdata 将输出写入命令,写入的具体区域,写数据的具体起始区域以及写入长度,之后还要传输经过格式转换后的写入数据。当命令传输后,只需要解析返回帧是否符合要求即可。
3.4 注意事项
除了对于读写数据需要设计专门的函数外,由于不同的数据类型数据结果区别很大,且PLC 的数据类型与Python 不能简单兼容,需要将数据分为Bool、UShort、Int、Float 和String 几类,根据具体情况分别设计读写函数,且不同情况下的字符转换方式也需要分别设计。对于Bool 类型的数据,由于Python 中将Bool和数字分为两类,读取后需要将其转换为对应的数字,对于数字类型,则需要根据实际存放的要求转换为对应的十六进制数。对于Float 类型数据需要注意小数点的位置。对于String 类型则需要逐一读取字符,先转换回十进制字符,再根据对应的ASCII 码值替换成对应的字符。对于写操作则按以上进行相反的操作。对于位操作,由于其不需要进行类型转换,为了提高读取写入速度,需要设计专门的写入读取函数,以简化操作步骤,提高运行效率。最后对于所有操作都需要设计1 个时钟计数,当1 个操作经过一定时间没有被响应,则判断连接出现问题,需要及时向上层程序进行反馈。
在实际一般的需求通信的程序运行过程中,一般需要单独开辟1 个线程负责与PLC 的连接通信操作,并将其全程挂起,以达到实时接收发送数据的目的,但这样可能会占用大量的内存资源。鉴于Fins 协议可以只从主机一端开始通信的特点,可以设计专门的线程函数,在未使用时将其挂起,当需要使用是再进行唤醒和调用,从而提高使用效率,将更多内存放松给主机使用。这种操作的缺点在于,当系统需要与PLC 进行通信时,才可能得知现在的连接状态是否出现问题。如果需要对连接状态进行实时控制,还是应当使用连续的进程。
为了保证数据进行平稳传输,还需要为线程设计1个锁对象,用于锁定通讯流程,使流程过程中其操作的数据不会被随意改变且线程不会被随意中断,保证整个读取过程的稳定性。此外当通信的中间环节发生错误时,需要及时将信号发送给上层程序,之后退出通信。值得注意的是,Fins 协议中不存在实际上的退出过程,这意味着实际上通讯是能够随时中断的,并不会影响端口的使用和占用[4]。
由于PLC 存在多个接口,所以可以在连通的情况下使用另一根导线连接另一个接口,这样可以使用另一台主机对PLC 的实时状态进行监测,以在没有让PLC实际运行线下设备的状态下进行调试。
在设计完成后,需要对程序设计测试。本次测试使用CX-Programmer 软件在另一台监控主机上进行,在运行Python 程序前先查看PLC 的DM 区的具体数据,再使用程序进行PLC 进行读写操作,然后再使用软件查看具体数据变化,对于读操作则查看是否读取了对应数据,对于写操作则查看是否写入正确数据,结果完美完成了读出写入数据的任务,反应时长达到使用要求,且当人为设计了连接错误后,能较好地将连接错误反应到上层程序。
4 结束语
本文设计了欧姆龙PLC 使用Python 程序基于Fins协议的进行通信的具体步骤,并设计了一种基于Python的软件通信方法,该方法完成了主机与PLC的连接通信,并完成了读取数据区数据和写入数据的操作,在Python软件程序与欧姆龙PLC 通过Fins 协议进行通讯的网络资料较少的现状下,具有一定的借鉴作用。由于现存的实际项目对于通讯的功能要求不高,不要求通过软件对PLC 进行实际控制,本文只研究了读取和写入数据区的两类功能,在今后的研究中,还需要继续研究对PLC 进行控制以及读写大文件的软件程序编写方法,从而提高程序的泛用性和功能性。
参考文献:
[1] 黄燕民,陈宏轩,罗友高,等.基于Python与S7-1500的清洗机器人运动控制系统[J].机电一体化,2022,28(Z2):79-84.
[2] 王奚,王新月,李航,等.面向PLC产品的自动化测试系统平台设计与实现[J].自动化仪表,2022,43(5):8-11+19.
[3] 韩志三.基于Python的丰炜系列PLC与PC串行通信的实现[J].硅谷,2013,6(22):59-60+15.
[4] 周任杰,刘宇,张子立,等.基于Python的龙骨成型机通信方案设计[J].计算机应用与软件,2019,36(06):93-96+135.
(本文来源于《电子产品世界》杂志2023年8月期)