文章目录

  • 前言
  • 一、Snap7源码下载
  • 二、Snap7的dll常用函数功能介绍
  • 三、Snap7Lib.pri模块的封装
  • 四、下载链接
  • 总结

前言

本文主要讲述了在Qt下调用Snap7库与西门子PLC进行通信,在这里将Snap7的源码与动态库整合在一起封装了一个自己的Snap7Lib.pri子模块,方便在之后的工作中进行使用,也希望可以帮助到大家,如有错误之处,欢迎大家批评指正。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Snap7源码下载

在这里我已经将需要的源码和库文件进行了整理,以及Snap7的参考手册pdf放在下文中的下载链接中。也可以在Snqp7的官网进行下载:
稳定版本snap7源码下载:snap7-full-1.4.2.7z
详情可见参考文章:C++[QT] 环境下使用Snap7与PLC通讯

二、Snap7的dll常用函数功能介绍

在这篇文章中有对Snap7库的常用函数进行了介绍,详情可见参考文章:C++(QT)调用snap7库连接西门子plc

三、Snap7Lib.pri模块的封装

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 
#include 
#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"<
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"<
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"<
}
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.文件夹结构
Qt下调用Snap7库与西门子PLC通信插图

四、下载链接

我的压缩包内容如下:
Qt下调用Snap7库与西门子PLC通信插图(1)

Snap7源码和库文件的百度网盘链接:
提取码:


总结

我的文章一开始使用了参考文章中的部分代码,在实际测试过程中也是出现了读写失败等问题,所以在其基础上进行了修改优化,其中的读写bool和int类型数据是没有问题的,而float和string类型实际没有测试,这里无法得之其是否正确,文中代码仅供参考。


hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。

参考博客:
C++[QT] 环境下使用Snap7与PLC通讯
C++(QT)调用snap7库连接西门子plc
Qt使用S7协议进行数据类型转换

本站无任何商业行为
个人在线分享 » Qt下调用Snap7库与西门子PLC通信
E-->