当前位置: 首页 > news >正文

Qt项目实现对西门子PLC的读写操作(snap7)——C++

实际项目中需要用到对西门子PLC进行通讯,故进行记录,方便后续回顾复习
实现功能:
①PLC连接与断开
②往PLC指定位置读写操作(bit、real、string)

PLC中的real相当于C++中的float,4字节,32bit

1,下载西门子对应的SDK

在这里插入图片描述

2,拿到snap7.dllsnap7.libsnap7.hsnap7.cpp

snap7-full-1.4.2\examples\cpp
snap7-full-1.4.2\examples\cpp\win64

snap7.h和snap7.cpp

知道snap7.h位置啦
知道snap7.cpp位置啦

在这里插入图片描述
在这里插入图片描述

3,在snap7-full-1.4.2\rich-demos\x86_64-win64\bin下有客户端和服务器可以进行本地模拟测试,这里需要使用服务器进行模拟PLC

在这里插入图片描述

这里大概讲解下软件如何使用

在这里插入图片描述
在这里插入图片描述

4,创建Qt项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
运行下看看效果,没问题
在这里插入图片描述
我个人喜欢手动创建文件夹和手动创建属性表
src存放snap7.cpp、include存放snap7.h、snap\lib文件夹存放snap7.lib、x64\解决方案平台\和exe同级路径下存放snap7.dll
在这里插入图片描述

配置snap7的属性表
在这里插入图片描述

VC++目录\库目录,选择对应的snap7.lib所在路径
在这里插入图片描述

链接器\输入\附加依赖项,填入具体的lib名称,snap7.lib
在这里插入图片描述

ok,运行项目,会发现snap7.cppmain.cppPLC_Demo_Qt.cpp报错,找不到头文件,我们改一下即可
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

再次运行,项目没问题,UI出来了就行
在这里插入图片描述

5,项目结构分析

我们主要在PLC_Demo_Qt.hPLC_Demo_Qt.cpp下调用snap7.hsnap7.cpp实现对应的读写操作功能
连接、断开、读写等操作均在PLC_Demo_Qt.hPLC_Demo_Qt.cpp声明和实现
在这里插入图片描述
UI布局也比较简单
在这里插入图片描述

6,代码实现

①main.cpp

#include "include/PLC_Demo_Qt.h"
#include <QtWidgets/QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);PLC_Demo_Qt w;w.show();return a.exec();
}

②PLC_Demo_Qt.h

1,QMutex mutex_{QMutex::Recursive};因为在访问PLC的时候可能会产生冲突,故加上了Qt的递归锁
2,std::shared_ptr<TS7Client> client_;snap的客户端智能指针,用它读写PLC
3,SetBit是设置具体DB的Index的具体bit位的值,因为是bit,故处理的是一个字节中的某个位
4,isLittleEndian()判断PLC是大端模式还是小端模式
大端模式:数据的高位字节存放在内存的低地址处,低位字节存放在内存的高地址处。符合人类正常的数字读写顺序,比如对于一个多字节的数值,从左到右(低地址到高地址)依次是高位到低位,就像我们平时书写数字一样。
小端模式:数据的低位字节存放在内存的低地址处,高位字节存放在内存的高地址处。对于计算机处理器来说,小端模式可能更便于处理,因为许多处理器(如 x86 架构的处理器)采用小端模式,在进行数据操作时,从低地址开始读取低位字节可能更符合处理器的设计逻辑。
5,SetValue和GetValue这里我进行了函数重载,应该比较好理解

#pragma once
#include <QMutex>
#include <QtWidgets/QMainWindow>
#include <memory>#include "PLC_Demo_Qt.h"
#include "include/snap7.h"
#include "ui_PLC_Demo_Qt.h"class PLC_Demo_Qt : public QMainWindow {Q_OBJECTQMutex mutex_{QMutex::Recursive};std::shared_ptr<TS7Client> client_;public:PLC_Demo_Qt(QWidget* parent = nullptr);~PLC_Demo_Qt();[[nodiscard]] bool Connect();[[nodiscard]] bool Disconnect();[[nodiscard]] bool IsConnected();[[nodiscard]] void SetBit(unsigned char& byte, const int bit_index,const bool value);[[nodiscard]] bool GetBit(const unsigned char& byte, const int bit_index);[[nodiscard]] bool isLittleEndian();[[nodiscard]] bool SetValue(const int db, const int index, const int bit, bool value);[[nodiscard]] bool GetValue(const int db, const int index, const int bit, bool& is_set);[[nodiscard]] bool SetValue(const int db, const int index, const int bit, float f_value);[[nodiscard]] bool GetValue(const int db, const int index, const int bit, float& f_value);[[nodiscard]] bool SetValue(const int db, const int index, const int bit, const int max_length, QString s_value);[[nodiscard]] bool GetValue(const int db, const int index, const int bit, const int max_length, QString& s_value);private:Ui::PLC_Demo_QtClass ui;};

③PLC_Demo_Qt.cpp

1,client_->DBRead(db, index, 1, &buffer);读取某一位,这里的1表示几个字节的意思,因为要处理某个位,默认应该是一个字节,所以这里就按一个字节进行处理了
2,client_->DBRead(db, index, 4, buffer);读取real数据类型,相当于float,4字节,故第三个参数是4
3,client_->DBRead(db, index, max_length + 2, buffer);读取String类型,需要注意的是,字符第一个字节记录的是字符串总长度,第二个字节记录的是字符串的有效长度,之后才是具体的内容,故要进行+2处理
4,其他的应该没啥了,也比较好理解

#include "include\PLC_Demo_Qt.h"
#include <iostream>#include <QDebug>PLC_Demo_Qt::PLC_Demo_Qt(QWidget* parent) : QMainWindow(parent),
client_(std::make_shared<TS7Client>())
{ui.setupUi(this);connect(ui.pushButton_connect, &QPushButton::clicked, this, [this]{bool ret = Connect();qDebug() << "Connect:" << ret;if (ret){ui.label->setText("Connected");ui.label->setStyleSheet("background-color: rgb(0, 255, 0);");}else{ui.label->setText("Disconnected");ui.label->setStyleSheet("background-color: rgb(255, 0, 0);");}});connect(ui.pushButton_disconnect, &QPushButton::clicked, this, [this]{bool ret = Disconnect();qDebug() << "Disconnect:" << ret;if (ret){ui.label->setText("Connected");ui.label->setStyleSheet("background-color: rgb(0, 255, 0);");}else{ui.label->setText("Disconnected");ui.label->setStyleSheet("background-color: rgb(255, 0, 0);");}});connect(ui.pushButton_bit_write, &QPushButton::clicked, this, [this] {//对DB1 index0 bit0 这个位的值int i = ui.spinBox_bit_write->value();bool value = false;(i == 1)? value = true : value = false;bool ret = SetValue(1, 0, 0, value);if (!ret) {qDebug() << "SetValue_bool failed";return;}qDebug() << "SetValue_bool success";});connect(ui.pushButton_bit_read, &QPushButton::clicked, this, [this]{//读取DB1 index0 bit0 这个位的值bool value;bool ret = GetValue(1, 0, 0, value);if (!ret){qDebug() << "GetValue_bool failed";return;}qDebug() << "GetValue_bool success";ui.spinBox_bit_read->setValue(value ? 1 : 0);});connect(ui.pushButton_real_write, &QPushButton::clicked, this, [this] {float f_value = ui.doubleSpinBox_real_write->value();bool ret = SetValue(1, 0, 0, f_value);if (!ret){qDebug() << "SetValue_float failed";return;}qDebug() << "SetValue_float success";});connect(ui.pushButton_real_read, &QPushButton::clicked, this, [this]{//读取DB1 index0 real3 的值float f_value;bool ret = GetValue(1, 0, 0, f_value);if (!ret){qDebug() << "GetValue_float failed";return;}qDebug() << "GetValue_float success";ui.doubleSpinBox_real_read->setValue(f_value);});connect(ui.pushButton_string_write, &QPushButton::clicked, this, [this] {//int max_length = ui.lineEdit_string_write->text().length();//QString s_value = ui.lineEdit_string_write->text();int  max_length = 15;QString s_value = ui.lineEdit_string_write->text();bool ret = SetValue(1, 0, 0, max_length, s_value);if (!ret){qDebug() << "SetValue_string failed";return;}qDebug() << "SetValue_string success";});connect(ui.pushButton_string_read, &QPushButton::clicked, this, [this] {int max_length = 15;QString s_value;bool ret = GetValue(1, 0, 0, max_length, s_value);if (!ret){qDebug() << "GetValue_string failed";return;}qDebug() << "GetValue_string success";ui.lineEdit_string_read->setText(s_value);});}PLC_Demo_Qt::~PLC_Demo_Qt() {}bool PLC_Demo_Qt::Connect() {QMutexLocker locker(&mutex_);const int ret = client_->ConnectTo("127.0.0.1", 0, 0);if (ret != 0) {qCritical() << QString("Snap7Interface::Connect failed, code: 0x%1").arg(ret, 8, 16, QLatin1Char('0')).toUtf8().data();return false;}return true;
}bool PLC_Demo_Qt::IsConnected() {QMutexLocker locker(&mutex_);return client_->Connected();
}bool PLC_Demo_Qt::Disconnect() {QMutexLocker locker(&mutex_);return client_->Disconnect();
}void PLC_Demo_Qt::SetBit(unsigned char& byte, const int bit_index, const bool value)
{if (value) { byte |= 1 << bit_index; }else { byte &= ~(1 << bit_index); }
}
bool PLC_Demo_Qt::GetBit(const unsigned char& byte, const int bit_index) { return (byte & 1 << bit_index) != 0; }
bool PLC_Demo_Qt::isLittleEndian()
{uint16_t num = 1;return (*(reinterpret_cast<uint8_t*>(&num)) == 1);
}bool PLC_Demo_Qt::SetValue(const int db, const int index, const int bit, const bool value) {QMutexLocker locker(&mutex_);unsigned char buffer;int ret = client_->DBRead(db, index, 1, &buffer);if (ret != 0) {qCritical() << QString("Snap7Interface::SetValue read failed, code: 0x%1").arg(ret, 8, 16, QLatin1Char('0')).toUtf8().data();return false;}SetBit(buffer, bit, value);ret = client_->DBWrite(db, index, 1, &buffer);if (ret != 0) {qCritical() << QString("Snap7Interface::SetValue write failed, code: 0x%1").arg(ret, 8, 16, QLatin1Char('0')).toUtf8().data();return false;}bool is_set;if (!GetValue(db, index, bit, is_set)) {return false;}return is_set == value;
}bool PLC_Demo_Qt::GetValue(const int db, const int index, const int bit, bool& is_set)
{QMutexLocker locker(&mutex_);unsigned char buffer;const int ret = client_->DBRead(db, index, 1, &buffer);if (ret != 0){qCritical() << QString("Snap7Interface::GetValue_bool failed, code: 0x%1").arg(ret, 8, 16, QLatin1Char('0')).toUtf8().data();return false;}is_set = GetBit(buffer, bit);return true;
}bool PLC_Demo_Qt::SetValue(const int db, const int index, const int bit, const float f_value)
{QMutexLocker locker(&mutex_);uint8_t buffer[4];std::memcpy(buffer, &f_value, 4);// 检查字节序并进行转换if (isLittleEndian()){std::reverse(buffer, buffer + 4);}const int ret = client_->DBWrite(db, index, 4, buffer);if (ret != 0){qCritical() << QString("Snap7Interface::SetValue_int write failed, code: 0x%1").arg(ret, 8, 16, QLatin1Char('0')).toUtf8().data();return false;}return true;
}bool PLC_Demo_Qt::GetValue(const int db, const int index, const int bit, float& f_value)
{QMutexLocker locker(&mutex_);uint8_t buffer[4];const int ret = client_->DBRead(db, index, 4, buffer);if (ret != 0){qCritical() << QString("Snap7Interface::GetValue_float failed, code: 0x%1").arg(ret, 8, 16, QLatin1Char('0')).toUtf8().data();return false;}if (isLittleEndian()){std::reverse(buffer, buffer + 4);}std::memcpy(&f_value, buffer, 4);return true;
}bool PLC_Demo_Qt::SetValue(const int db, const int index, const int bit, const int max_length, const QString s_value)
{QByteArray byteArray = s_value.toUtf8();int actualLength = qMin(byteArray.size(), max_length);char* buffer = new char[max_length + 2];buffer[0] = static_cast<char>(max_length);buffer[1] = static_cast<char>(actualLength);std::memcpy(buffer + 2, byteArray.constData(), actualLength);int sizeToWrite = 2 + actualLength;int ret = client_->DBWrite(db, index, sizeToWrite, buffer);if (ret != 0){qCritical() << QString("Snap7Interface::SetValue_string write failed, code: 0x%1").arg(ret, 8, 16, QLatin1Char('0')).toUtf8().data();delete[] buffer;return false;}delete[] buffer;return true;
}bool PLC_Demo_Qt::GetValue(const int db, const int index, const int bit, const int max_length, QString& s_value)
{char* buffer = new(std::nothrow) char[max_length + 2];if (!buffer){std::cerr << "Memory allocation failed." << std::endl;return false;}int ret = client_->DBRead(db, index, max_length + 2, buffer);if (ret == 0){const int maxLength = static_cast<int>(buffer[0]);const int actualLength = static_cast<int>(buffer[1]);const QByteArray byteArray(buffer + 2, actualLength);s_value = QString::fromUtf8(byteArray);// 释放动态分配的内存delete[] buffer;return true;}else{std::cerr << "Failed to read string from DB1 78.0." << std::endl;// 释放动态分配的内存delete[] buffer;return false;}
}

④snap7.h

我们下载PLC的SDK里面的头文件,内容太多这里就不放了
snap7.h具体位置

继续

⑤snap7.cpp

我们下载PLC的SDK里面的对应头文件的实现,内容太多这里就不放了
snap7.cpp具体位置

继续

7,效果展示

Ⅰ. 打开serverdemo.exe

在这里插入图片描述

Ⅱ. 模拟PLC的IP选择本地就行

在这里插入图片描述

Ⅲ. 运行项目

①连接

在这里插入图片描述

②按bit读写

按bit读写,代码里面是DB1 Index0 bit0,也可以做个配置文件
在这里插入图片描述
在这里插入图片描述

③按real读写(其实就是float)

在这里插入图片描述
在这里插入图片描述

④按字符串读写(代码里面设置的字符串长度是15)

在这里插入图片描述
在这里插入图片描述

相关文章:

  • 关于大型语言模型的“生物学”
  • 算法题(128):费解的开关
  • 从裸仓库到GitLab全解析
  • 【愚公系列】《Python网络爬虫从入门到精通》056-Scrapy_Redis分布式爬虫(Scrapy-Redis 模块)
  • 不确定与非单调推理的可信度方法
  • REST 架构详解:从概念到应用的全面剖析
  • 多人五子棋联机对战平台 测试报告
  • AI文生图工具推荐
  • 计算机网络期中复习笔记(自用)
  • 8、表单控制:预言水晶球——React 19 复杂表单处理
  • tigase源码学习杂记-AbstractMessageReceiver
  • 二级评论列表-Java实现
  • PyTorch深度学习框架60天进阶学习计划 - 第46天:自动化模型设计(二)
  • 实战设计模式之备忘录模式
  • 数量关系 多级数列1
  • ClawCloud的免费空间(github用户登录可以获得$5元/月的免费额度)
  • PostgreSQL 的pgloader 工具介绍
  • Qt C++ 解析和处理 XML 文件示例
  • django基于爬虫的网络新闻分析系统的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
  • 数据驱动未来:大数据在智能网联汽车中的深度应用
  • 美关税政策冲击本土车企:福特7月涨价,通用汽车盈利预期下调
  • 大理州工业投资(集团)有限公司党委副书记、副总经理赵云接受审查调查
  • 冲线!“天工”夺得全球首个人形机器人半马冠军
  • 恒安集团创始人许连捷逝世
  • 俄最高法宣布解除针对阿富汗塔利班的禁令
  • 习近平会见柬埔寨人民党主席、参议院主席洪森