Qt下调用Snap7库与西门子PLC通信

news2024/10/5 21:13:56

文章目录

  • 前言
  • 一、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 <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协议进行数据类型转换

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1800992.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Gitblit软件开启git服务器

文章目录 使用Gitblit软件开启git服务器&#xff0c;供局域网其他电脑当做git仓库服务1. java依赖环境安装2. Mac系统操作2.1 下载Gitblit、配置参数2.2 启动服务2.3 终止服务&#xff1a;停止脚本即可 3. window系统操作3.1 下载Gitblit、配置参数3.2 启动服务3.3 终止服务&am…

科技云报道:走出“实验室”,GenAI迎来关键拐点

科技云报道原创。 对传统产业来说&#xff0c;GenAI是一场“哥白尼式的革命”&#xff0c;它改变了传统的业务模式&#xff0c;开启了人类与AI合作的新纪元。基于AI助手和大语言模型&#xff0c;企业能够实现智能运营的目标。 如果说&#xff0c;2022年是AI大模型元年&#x…

mysql工具----dbForgeStudio2020

dbForgeStudio2020&#xff0c;除了基本的操作外&#xff0c;还具有可调试mysql存储过程的功能&#xff0c;是一个不可夺得的mysql软件工具。 本文的软件将简单介绍软件的安装方式&#xff0c;仅供学习交流&#xff0c;不可做它用。 1.安装软件&#xff0c;安装后&#xff0c…

Chrome DevTools解密:成为前端调试大师的终极攻略

Chrome DevTools是一套内置于Google Chrome浏览器中的开发者工具&#xff0c;它允许开发者对网页进行调试、分析和优化。本文将全面介绍DevTools的功能、使用方法以及注意事项&#xff0c;帮助开发者更好地利用这些工具来提升开发效率和网页性能。 一、简介 1. DevTools是什么…

RabbitMQ-工作模式(Publish模式Routing模式)

文章目录 发布/订阅&#xff08;Publish/Subscribe&#xff09;交换机临时队列绑定总体代码示例 路由&#xff08;Routing&#xff09;绑定直连交换机多重绑定发送日志订阅总体代码示例 更多相关内容可查看 发布/订阅&#xff08;Publish/Subscribe&#xff09; 构建一个简单的…

Playwright框架入门

自从2023年底playwright框架火起来之后,很多小伙伴咨询我们这个框架,甚至问我们什么时候出这个课程. 这步这个课程在我们千呼万唤中出来了.具体的课程大纲和试听可以联系下方二维码获取. 今天给大家分享一下playwright的安装和一些常用API,为后续的学习做好准备工作. Playwrig…

批量重命名大解放!自定义取文本左侧长度,轻松实现文件名焕新之旅!

文件管理是我们日常工作和生活中不可或缺的一部分。然而&#xff0c;面对成千上万的文件&#xff0c;手动重命名无疑是一项繁琐且耗时的任务。今天&#xff0c;我们为您推荐一款高效便捷的批量文件重命名工具——文件批量改名高手&#xff0c;让您轻松实现取文本左的长度来进行…

【PythonCode】力扣Leetcode21~25题Python版

【PythonCode】力扣Leetcode21~25题Python版 前言 力扣Leetcode是一个集学习、刷题、竞赛等功能于一体的编程学习平台&#xff0c;很多计算机相关专业的学生、编程自学者、IT从业者在上面学习和刷题。 在Leetcode上刷题&#xff0c;可以选择各种主流的编程语言&#xff0c;如C…

【会议征稿,IEEE出版】EEI 2024,6月28-30

第六届电子工程与信息学国际学术会议&#xff08;EEI 2024&#xff09;将于2024年6月28日至6月30日在中国重庆召开。EEI 2024将围绕“电子工程”、“信息学”与“计算机科学”等相关最新研究领域 &#xff0c;为来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、…

海康威视综合安防管理平台 多处 FastJson反序列化RCE漏洞复现

0x01 产品简介 海康威视综合安防管理平台是一套“集成化”、“智能化”的平台,通过接入视频监控、一卡通、停车场、报警检测等系统的设备。海康威视集成化综合管理软件平台,可以对接入的视频监控点集中管理,实现统一部署、统一配置、统一管理和统一调度。 0x02 漏洞概述 由于…

javacv ffmpeg使用笔记 (补充中...)

javacv ffmpeg使用笔记 一、maven依赖二、示例代码1. 获取视频时长 三、小技巧 一、maven依赖 使用javacv ffmpeg并指定classifier之后&#xff0c;就不需要额外安装ffmpeg软件&#xff08;jar包中已经内置&#xff09;了。 全量依赖包&#xff08;不推荐&#xff09;安装包总大…

Docker 管理 | 代理配置、内网共享和 Harbor 部署

唠唠闲话 在现代软件开发和运维中&#xff0c;容器技术已经成为构建、部署和管理应用程序的标准工具。然而&#xff0c;在实际操作中&#xff0c;我们常常需要面对一些常见的挑战&#xff0c;如容器访问外部资源的代理配置、内网环境下的镜像共享以及企业级镜像管理。 本教程…

Transformer动画讲解:注意力计算Q、K、V

暑期实习基本结束了&#xff0c;校招即将开启。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。提前准备才是完全之策。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c…

自动化Reddit图片收集:Python爬虫技巧

引言 Reddit&#xff0c;作为一个全球性的社交平台&#xff0c;拥有海量的用户生成内容&#xff0c;其中包括大量的图片资源。对于数据科学家、市场研究人员或任何需要大量图片资源的人来说&#xff0c;自动化地从Reddit收集图片是一个极具价值的技能。本文将详细介绍如何使用…

GCB | 全球草地和森林土壤呼吸对降水量变化的不对称响应

全球变化导致地球水文循环的强化放大了降水的年际变化&#xff0c;这将显著影响陆地碳&#xff08;C&#xff09;循环。然而&#xff0c;在极端降水变化情况下&#xff0c;先前观测到的土壤呼吸&#xff08;Rs&#xff09;和降水之间的关系是否仍然适用&#xff0c;目前仍不清楚…

thinkphp6.0版本下子查询sql处理

目录 一&#xff1a;背景 二&#xff1a;查询实例 三&#xff1a;总结 一&#xff1a;背景 我们在实际业务的开发过程中&#xff0c;经常会碰到这样的场景&#xff0c;查询某些部门的客户信息&#xff0c;查询下过订单的客户信息。这里查询客户信息实际上就用到了子查询&…

PS的stable diffusion插件安装指南

PS的stable diffusion插件安装指南 1.首先要安装stable diffusion&#xff0c;具体安装方法&#xff0c;参考https://blog.csdn.net/sheji888/article/details/139196688 stable diffusion要求要启用API功能 2.安装ps2023以上版本&#xff0c;低于这个版本不能使用stable diff…

TCP攻击是怎么实现的,如何防御?

TCP&#xff08;Transmission Control Protocol&#xff09;是互联网协议族中的重要组成部分&#xff0c;用于在不可靠的网络上提供可靠的数据传输服务。然而&#xff0c;TCP协议的一些特性也使其成为攻击者的目标&#xff0c;尤其是DDoS&#xff08;Distributed Denial of Ser…

马斯克五步流程法在产品创新中的实践与应用

引言&#xff1a; 在科技创新的浪潮中&#xff0c;埃隆马斯克以其独到的思维方式和创新实践&#xff0c;引领着多个行业的前沿。他提出的“第一性原理”下的五步流程法&#xff0c;不仅是对创新过程的深刻洞见&#xff0c;也为产品经理和工程师们提供了一套行之有效的工作方法。…

【Redis】Redis经典问题:缓存穿透、缓存击穿、缓存雪崩

目录 缓存的处理流程缓存穿透解释产生原因解决方案1.针对不存在的数据也进行缓存2.设置合适的缓存过期时间3. 对缓存访问进行限流和降级4. 接口层增加校验5. 布隆过滤器原理优点缺点关于扩容其他使用场景SpringBoot 整合 布隆过滤器 缓存击穿产生原因解决方案1.设置热点数据永不…