赞
踩
本文主要讲述了在Qt下调用Snap7库与西门子PLC进行通信,在这里将Snap7的源码与动态库整合在一起封装了一个自己的Snap7Lib.pri子模块,方便在之后的工作中进行使用,也希望可以帮助到大家,如有错误之处,欢迎大家批评指正。
提示:以下是本篇文章正文内容,下面案例可供参考
在这里我已经将需要的源码和库文件进行了整理,以及Snap7的参考手册pdf放在下文中的下载链接中。也可以在Snqp7的官网进行下载:
稳定版本snap7源码下载:snap7-full-1.4.2.7z
详情可见参考文章:C++[QT] 环境下使用Snap7与PLC通讯
在这篇文章中有对Snap7库的常用函数进行了介绍,详情可见参考文章:C++(QT)调用snap7库连接西门子plc
pri模块化开发在Qt中是非常重要的,不仅可以使工程结构更加清晰,还能方便我们对这个子模块的复用,在其它的项目中需要实现类似的功能,直接在项目Pro文件中来包含这个pri文件就能实现调用:
#包含子模块
include (./Snap7Lib/Snap7Lib.pri)
在这里实现了一个Snap7Lib.pri的创建,下面是这个子模块的全部代码:
1.Snap7Lib.pri
HEADERS += \
$$PWD/myts7client.h \
$$PWD/snap7.h
SOURCES += \
$$PWD/myts7client.cpp \
$$PWD/snap7.cpp
LIBS += -L$$PWD/lib/ -lsnap7
2.myts7client.h
#ifndef MYTS7CLIENT_H #define MYTS7CLIENT_H #include <QTime> #include <QDebug> #include "snap7.h" #define LOGDEBUG qDebug()<<QTime::currentTime().toString("[hh:mm:ss:zzz]") class MyTS7Client { public: MyTS7Client(); ~MyTS7Client(); bool initConnect(QString ip); bool writeBool(int address,int offset,int bit,bool value); bool readBool(int address,int offset,int bit); void printByteBits(uchar byteValue); bool writeInt(int address,int offset,const QString &value); QString readInt(int address,int offset); bool writePlcData(int type,int address,int offset,const QString &value); QString readPlcData(int type,int address,int offset); private: bool m_connectFlag; //连接标志 TS7Client *m_ts7Client; //TS7Client对象 }; #endif // MYTS7CLIENT_H
3.myts7client.cpp
#include "myts7client.h" MyTS7Client::MyTS7Client() { m_connectFlag = false; } MyTS7Client::~MyTS7Client() { } //初始化连接 bool MyTS7Client::initConnect(QString ip) { //创建TS7Client对象 m_ts7Client = new TS7Client(); int rNum = m_ts7Client->ConnectTo(ip.toLocal8Bit().data(),0,1); if(rNum == 0) { LOGDEBUG<<"PLC连接成功!"; m_connectFlag = true; return true; } else { LOGDEBUG<<"PLC连接失败!"; m_connectFlag = false; return false; } return true; } //将bool类型数据写入指定偏移量的位 bool MyTS7Client::writeBool(int address,int offset,int bit,bool value) { //是否连接 if(!m_connectFlag) { LOGDEBUG<<"PLC未连接!"; return false; } //验证偏移量和位号是否有效 if(offset < 0 || offset > 3 || bit < 0 || bit > 7) { LOGDEBUG<<"无效的偏移量和位号!"; return false; } //读取包含目标位的字节以保留其他位的值 uchar byteValue; int result = m_ts7Client->DBRead(address,offset,1,&byteValue); if(result != 0) { //处理错误 LOGDEBUG<<"读取DB"<<address<<"_"<<offset<<"."<<bit<<"失败!"; return false; } //LOGDEBUG<<"byteValue1:"<<byteValue; //设置或清除目标位的值 uchar bitMask = 1 << bit; if(value) { byteValue |= bitMask; //设置目标位为1 } else { byteValue &= ~bitMask; //清除目标位 } //将修改后的字节写回DB100 result = m_ts7Client->DBWrite(address,offset,1,&byteValue); if(result != 0) { //处理错误 LOGDEBUG<<"写入数据到DB"<<address<<"_"<<offset<<"."<<bit<<"失败!"; return false; } LOGDEBUG<<"写入数据"<<value<<"到DB"<<address<<"_"<<offset<<"."<<bit<<"成功!"; return true; } //从指定偏移量的位读取bool类型数据 bool MyTS7Client::readBool(int address,int offset,int bit) { //是否连接 if(!m_connectFlag) { LOGDEBUG<<"PLC未连接!"; return false; } //验证偏移量和位号是否有效 if(offset < 0 || offset > 3 || bit < 0 || bit > 7) { LOGDEBUG<<"无效的偏移量和位号!"; return false; } //读取包含目标位的字节 uchar byteValue; int result = m_ts7Client->DBRead(address,offset,1,&byteValue); if(result != 0) { //处理错误 LOGDEBUG<<"读取DB"<<address<<"_"<<offset<<"."<<bit<<"失败!"; return false; } //printByteBits(byteValue); //提取目标位的bool值 uchar bitMask = 1 << bit; bool boolValue = (byteValue & bitMask) != 0; //LOGDEBUG<<"读取DB"<<address<<"_"<<offset<<"."<<bit<<"成功! 返回值为:"<<boolValue; return boolValue; } //打印字节各位的值 void MyTS7Client::printByteBits(uchar byteValue) { for(int bitIndex = 0;bitIndex <8;++bitIndex) { //使用位操作符检查特定位的值 bool bitValue = (byteValue & (1 << bitIndex)) != 0; //打印位的值 LOGDEBUG<<"bit["<<bitIndex<<"]:"<<(bitValue ? "1" : "0"); } } //将int类型数据写入指定偏移量 bool MyTS7Client::writeInt(int address,int offset,const QString &value) { //是否连接 if(!m_connectFlag) { LOGDEBUG<<"PLC未连接!"; return false; } qint16 buff = value.toShort(); QVector<char> buffToWrite(2); buffToWrite[0] = (buff >> 8) & 0xFF; //高位字节 buffToWrite[1] = buff & 0xFF; //低位字节 int rNum = m_ts7Client->DBWrite(address,offset,2,buffToWrite.data()); if(rNum == 0) { //LOGDEBUG<<"写入DB"<<address<<"_"<<offset<<"成功!"; return true; } else { LOGDEBUG<<"写入DB"<<address<<"_"<<offset<<"失败! 错误码:"<<rNum; return false; } return true; } //从指定偏移量读取int类型数据 QString MyTS7Client::readInt(int address,int offset) { //是否连接 if(!m_connectFlag) { LOGDEBUG<<"PLC未连接!"; return ""; } QString result; QVector<char> buff(2); int rNum = m_ts7Client->DBRead(address,offset,2,buff.data()); if(rNum == 0) { qint16 resultValue = ((unsigned char)buff[0] << 8) | (unsigned char)buff[1]; result = QString::number(resultValue); //LOGDEBUG<<"读取DB"<<address<<"_"<<offset<<"成功! 返回值为:"<<result; } else { LOGDEBUG<<"读取DB"<<address<<"_"<<offset<<"失败! 错误码:"<<rNum; } return result; } //写入数据到PLC bool MyTS7Client::writePlcData(int type,int address,int offset,const QString &value) { //是否连接 if(!m_connectFlag) { LOGDEBUG<<"PLC未连接!"; return false; } //1-int 2-string 3-float switch(type) { case 1: { qint16 buff = value.toShort(); QVector<char> buffToWrite(2); buffToWrite[0] = (buff >> 8) & 0xFF; //高位字节 buffToWrite[1] = buff & 0xFF; //低位字节 int rNum = m_ts7Client->DBWrite(address,offset,2,buffToWrite.data()); if(rNum == 0) { LOGDEBUG<<"写入数据:"<<value<<"成功!"; return true; } else { LOGDEBUG<<"写入DB失败!返回错误码:"<<rNum; } break; } case 2: { QByteArray strBytes = value.toUtf8(); //转换为UTF-8编码的字节序列 QByteArray byte; //用来发送到PLC的数据 byte.append(strBytes); //字符串的内容 if(byte.size() <= 0) { QByteArray byte(64,' '); //创建一个包含64个空格字节的字节数组 int rNum = m_ts7Client->DBWrite(address,offset,byte.size(),byte.data()); //写入PLC if(rNum == 0) { LOGDEBUG<<"写入数据:"<<value<<"成功!"; } else { LOGDEBUG<<"写入DB失败!返回错误码:"<<rNum; } } else { int rNum = m_ts7Client->DBWrite(address,offset,byte.size(),byte.data()); //写入PLC if(rNum == 0) { LOGDEBUG<<"写入数据:"<<value<<"成功!"; } else { LOGDEBUG<<"写入DB失败!返回错误码:"<<rNum; } } break; } case 3: { float floatValue = value.toFloat(); quint32 iniValue; union { float floatVal; quint32 iniValue; }u; u.floatVal = floatValue; iniValue = u.iniValue; QVector<char> buffToWrite(4); buffToWrite[0] = (iniValue >> 24) & 0xFF; buffToWrite[1] = (iniValue >> 16) & 0xFF; buffToWrite[2] = (iniValue >> 8) & 0xFF; buffToWrite[3] = iniValue & 0xFF; int rNum = m_ts7Client->DBWrite(address,offset,4,buffToWrite.data()); if(rNum == 0) { LOGDEBUG<<"写入数据:"<<value<<"成功!"; } else { LOGDEBUG<<"写入DB失败!返回错误码:"<<rNum; } break; } default: LOGDEBUG<<"无效的数据类型!"; return false; } } //从PLC读取数据 QString MyTS7Client::readPlcData(int type,int address,int offset) { //是否连接 if(!m_connectFlag) { LOGDEBUG<<"PLC未连接!"; return ""; } //1-int 2-string 3-float QString result; switch(type) { case 1: { QVector<char> buff(2); int rNum = m_ts7Client->DBRead(address,offset,2,buff.data()); if(rNum == 0) { qint16 resultValue = ((unsigned char)buff[0] << 8) | (unsigned char)buff[1]; //qint16 resultValue = ((unsigned char)buff[0]) | (unsigned char)buff[1] << 8; result = QString::number(resultValue); LOGDEBUG<<"读取成功,返回结果"<<result<<" "<<resultValue; } else { LOGDEBUG<<"读取DB失败!返回错误码:"<<rNum; } break; } case 2: { QVector<char> buff(64); int rNum = m_ts7Client->DBRead(address,offset,64,buff.data()); if(rNum == 0) { for(int i=0;i<buff.size();++i) { if(buff[i] == ' ') break; result += buff[i]; } LOGDEBUG<<"读取成功,返回结果"<<result; } else { LOGDEBUG<<"读取DB失败!返回错误码:"<<rNum; } break; } case 3: { QVector<char> buff(4); int rNum = m_ts7Client->DBRead(address,offset,4,buff.data()); if(rNum == 0) { //qint32 iniValue = ((unsigned char)buff[0] << 24) | ((unsigned char)buff[1] << 16) | // ((unsigned char)buff[2] << 8) | (unsigned char)buff[3]; qint32 iniValue = ((unsigned char)buff[0]) | ((unsigned char)buff[1] << 8) | ((unsigned char)buff[2] << 16) | ((unsigned char)buff[3] << 24); union { quint32 intValue; float floatValue; }u; u.intValue = iniValue; float floatValue = u.floatValue; result = QString::number(floatValue); LOGDEBUG<<"读取成功,返回结果"<<result<<" "<<floatValue; } else { LOGDEBUG<<"读取DB失败!返回错误码:"<<rNum; } break; } default: LOGDEBUG<<"无效的数据类型!"; } return result; }
4.文件夹结构
我的压缩包内容如下:
Snap7源码和库文件的百度网盘链接:
提取码:
我的文章一开始使用了参考文章中的部分代码,在实际测试过程中也是出现了读写失败等问题,所以在其基础上进行了修改优化,其中的读写bool和int类型数据是没有问题的,而float和string类型实际没有测试,这里无法得之其是否正确,文中代码仅供参考。
hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。
参考博客:
C++[QT] 环境下使用Snap7与PLC通讯
C++(QT)调用snap7库连接西门子plc
Qt使用S7协议进行数据类型转换
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。