书接上文,通过对python-snap7的库的研究,笔者掌握了上位机与plc通讯的基本原理与方法,然工业现场上位机的用户界面基本上是以c++为底层语言制作的,python在工业应用中似乎不太吃香。因此,笔者针对基于c++的snap7进行了一段时间的学习,完成了以下的学习笔记,在此感谢部门的硬件支持及两位不愿意透露姓名的朱工与胡工的技术支持。
前文主要内容为基于python-snap7的上位机与plc通讯模块,首先,环境配置方面需要注意,snap7安装结束后需要把snap7-full-1.4.2\release\Windows\Win32目录下的dll文件和lib文件放至Anaconda的安装目录和系统windows下的SysWOW64下。此段中win32文件夹并非绝对,若python与系统版本为64位,则该文件夹为win64而非win32。其次,对前文关于python-snap7模块无法直接读写plc中某一位开关量的特性进行说明,snap7是基于c++进行封装的,而笔者后来在对snap7.dll的应用中发现,其中并没有支持位读取的相关类,因此,python-snap7是不支持plc位信号开关量的直接读写的。
掌握本文技术所需的技术为c++和MFC,本文不再对相关基础技术作过多解释。
笔者使用vs2019,需要vs2019具备MFC的开发的接口,需要将前文提及的snap7-full文件中的snap7.lib、snap7.h、snap7.cpp拷贝至项目文件夹根目录,添加至项目并且在窗体头文件中引入;将snap7.dll拷贝至debug目录。
此外,插个题外话,如果通讯失败,请仔细检查plc与上位机的网络是否连通。
工业应用中上位机与plc的通讯主要是通过db块数据读取实现,因此本文对M/Q/I区函数不再赘述,有兴趣的小伙伴可以自行进行实验。
以下为主要函数说明
ConnectTo(const char *RemAddress,int Rack, int Slot);
//通过PLC的IP地址*RemAddress建立连接。
// *RemAddress PLC的IP地址,
// Rack PLC的齿数,一般为0
// Slot PLC的槽数
Disconnect();
//断开PC与PLC的连接
DBRead(int DBNumber, int Start, int Size, void *pUsrData);
//读DB区的Byte值
//DBNumber为DB块识别号码
//Start为PLC的起始地址
//Size为PLC的字节个数
//*pUsrData数据缓冲区地址,函数读到的数据存在这个缓冲区内
DBWrite(int DBNumber, int Start, int Size, void *pUsrData);
//写DB区的Byte值
MBRead(int Start, int Size, void*pUsrData);
//读M区的Byte值
MBWrite(int Start, int Size, void*pUsrData);
//写M区的Byte值
ABRead(int Start, int Size, void*pUsrData);
//读I区的Byte值
ABWrite(int Start, int Size, void*pUsrData);
//写I区的Byte值
EBRead(int Start, int Size, void*pUsrData);
//读O区的Byte值
EBWrite(int Start, int Size, void *pUsrData);
//写O区的Byte值
需要注意的是,上述函数中读写都是以byte类型为基础的,如果读者对基于字节的读写有疑问,请参考上一篇博文对字节读写的描述,本文不再赘述。
本例直接使用MFC对snap7进行实验
创建窗口,创建两个编辑框IDC_EDIT1与IDC_EDIT2与一个按钮
编辑框关联两个整型变量,一个用来显示PLC的数值,一个用来确认按钮状态。
窗体头文件中定义一个snap7的类名与一个用于存储读取数据的byte数组和一个显示按钮状态的布尔量:
public:
afx_msg void OnBnClickedButton1();
int nPlcCom;
int nReadNum;
bool bArrIfStart;
TS7Client* pMyPlc;
byte nRead[2];
窗口初始化函数中定义snap7的类并且创建plc连接,至此,已经创建连接的pMyPlc类可以在全局中调用并读取plc中的数据:
// TODO: 在此添加额外的初始化代码
pMyPlc = new TS7Client;
pMyPlc->ConnectTo("192.168.0.10", 0, 1);
按钮的触发函数如下,该按钮实现的功能为:点击切换读取状态(其实这个功能可以不要),点击读取plc中db1004的心跳文,该心跳文为两个字节大小,并将心跳文显示到之前创建的文本编辑框中。
void CMFCApp032101Dlg::OnBnClickedButton1(){
// TODO: 在此添加控件通知处理程序代码
if (bArrIfStart == FALSE){
bArrIfStart = TRUE;
nPlcCom = 10;
}
else {
bArrIfStart = FALSE;
nPlcCom = 5;
}
pMyPlc->DBRead(1004, 0, 2, &nRead);
nReadNum = nRead[0] * 256 + nRead[1];
UpdateData(FALSE);
}
可以发现,每次点击按钮,编辑框1的数字变化显示按钮状态,编辑框2则读取了plc中的心跳文并显示。
至此,基于c++MFC的snap7实验基本成功,上位机与PLC的通讯其本质就是对内存区数据的读写,该技术的完整掌握可以写出便于windows系统运行的窗口程序。
此外,snap7目前笔者还没研究透反馈机制,在c++中即使读取错误程序也不会报错,因此无法确定读取状态,snap7的官方文档中有类似错误码返回的机制但是其demo中并未展示,所以笔者一直没找到正确的使用方法,替代方案是:在plc中固定地址存入一个常量让snap7去读取,若读取数值不一致则判断读取错误。如果有小伙伴知道错误码返回机制的使用方法也欢迎联系我。