Qt 使用modbus协议

news2024/12/17 10:28:04

Qt 框架下 使用modbus协议

一,使用Qt原生的 QModbusClient ,比如QModbusTcpClient

1,因为modbus的读写 需要在同一个线程中,所以需要在主线程中利用moveToThread的方式,将业务逻辑封装到 子线程中。

2,modbus封装

#pragma once
#include <QObject>
#include <QModbusTcpClient>
#include <QCoreApplication>
#include <qdebug.h>

namespace Device {
    class Modbus:public QObject{
        Q_OBJECT
    public:
        Modbus(QObject*parent): QObject(parent){

        }

    protected:
        bool modbusRead(QModbusTcpClient * modbusMaster,int serverAddress, QModbusDataUnit::RegisterType type, int newStartAddress, quint16 newValueCount, QList<quint16> &array, bool warning){
            QModbusDataUnit readUnit(type, newStartAddress, newValueCount);
            QModbusReply *reply = modbusMaster->sendReadRequest(readUnit, serverAddress);

            if(reply == nullptr)
            {
                qDebug()<<QStringLiteral("modbusRead 无效请求");
                return false;
            }

            while (!reply->isFinished())
            {
                QCoreApplication::processEvents();
            }
            if (reply->error() == QModbusDevice::NoError)
            {
                QModbusDataUnit resultUnit = reply->result();
                for (int i = 0; i < static_cast<int>(resultUnit.valueCount()); ++i) {
                    array.append(resultUnit.value(i));
                }
                return true;
            }
            else
            {
                qDebug()<<QStringLiteral("modbusRead reply %1").arg(reply->errorString());
                return false;
            }
        }
        bool modbusWrite(QModbusTcpClient * modbusMaster,int serverAddress, QModbusDataUnit::RegisterType type, int newStartAddress, quint16 newValueCount, QList<quint16> array, bool warning){
            QModbusDataUnit writeUnit(type, newStartAddress, newValueCount);
            for(int i = 0; i < newValueCount; i++)
                writeUnit.setValue(i, array.value(i));

            QModbusReply *reply = modbusMaster->sendWriteRequest(writeUnit, serverAddress);
            if(reply == nullptr)
            {
                qDebug()<<QStringLiteral("modbusWrite 无效请求");
                return false;
            }

            while (!reply->isFinished())
            {
                QCoreApplication::processEvents();
            }

            if (reply->error() == QModbusDevice::NoError)
            {
                return true;
            }
            else
            {
                qDebug()<<QStringLiteral("modbusWrite reply %1").arg(reply->errorString());
                return false;
            }
        }
    };
}

3,继承上述类,封装业务逻辑,以压力传感器为例。将功能放到槽函数中。

class ModbusWorkerPressure:public Modbus{
Q_OBJECT
public:
    explicit ModbusWorkerPressure(Modbus*parent= nullptr);
    ~ModbusWorkerPressure() override;

public slots:
    void open();
    void close();
    void zero();

signals:
    void siStatus(int type,int result);

private:
    QModbusTcpClient *modbusClient{nullptr};
    bool bOpened{false};
    bool bExit{false};
};
ModbusWorkerPressure::ModbusWorkerPressure(Modbus *parent): Modbus(parent)  {

}

ModbusWorkerPressure::~ModbusWorkerPressure() {

}

void ModbusWorkerPressure::close() {
    bExit = true;
    DELAY(500);
    if (bOpened) {
        modbusClient->disconnectDevice();
    }
    if (modbusClient != nullptr) {
        modbusClient->deleteLater();
    }
}

void ModbusWorkerPressure::open() {
    modbusClient = new QModbusTcpClient(this);
    modbusClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "10.10.10.2");
    modbusClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, 502);
    modbusClient->setTimeout(500);
    if (modbusClient->connectDevice()) {
        DELAY(500);
        while (!bExit) {
            QList<quint16> list;
            int32_t pressure=0;
            if(modbusRead(modbusClient,1,QModbusDataUnit::HoldingRegisters,0x0000,0x0002,list,false)){
                int result = 0;
                result =static_cast<int32_t>(list[1])<<16|list[0];
                pressure = result;
                //单位g
                DataManager::Instance().setCurPressure(pressure);

                //能读出数据 认为连接成功
                static bool execute = false;
                if (!execute) {
                    bOpened = true;
                    emit siStatus(0, 0);
                    execute = true;
                }
            }
            DELAY(5);
        }
    } else {
        emit siStatus(0, -1);
        bOpened = false;
    }
}

void ModbusWorkerPressure::zero() {
    QList<quint16> list{0x0001};
    if (!modbusWrite(modbusClient,1, QModbusDataUnit::HoldingRegisters, 0x0002, 0x0001, list, false)) {
        LOGE(u8"压力传感器  清零失败!");
    }
}

4, 主线程 使用moveToThread 将上述业务线程进行封装,然后主线程中 用信号,进行触发

bool PressureSensor::open()
    {
        workerThread = new QThread();
        modbusWorker = new ModbusWorkerPressure();
        modbusWorker->moveToThread(workerThread);
        QObject::connect(workerThread, &QThread::started, modbusWorker, &ModbusWorkerPressure::open);
        QObject::connect(this, &PressureSensor::siClose, modbusWorker, &ModbusWorkerPressure::close);
        QObject::connect(this, &PressureSensor::siZero, modbusWorker, &ModbusWorkerPressure::zero);
        QObject::connect(modbusWorker, &ModbusWorkerPressure::siStatus, [&](int type, int result) {
            if (type == 0) {
                if (result == 0) {
                    bOpened = true;
                } else {
                    bOpened = false;
                }
            }
        });
        workerThread->start();
        return true;
    }

    bool PressureSensor::zero() {
        if(!bOpened)
            return false;

        emit siZero();
        return true;
    }

5,此种方式 优点就是Qt原生框架,但是缺点是 这种方式是异步的方式,想要做到同步调用,比如轴系运动中,需要自己去同步,试过 QEventLoop的方式,但是不行,会丢失事件。

二,第三方库 libmodbus

1,编译及下载

Libmodbus在win11下的编译与VS2019下的运行_libmodbus vs2019-CSDN博客

2,写bool

 auto home  = [&](bool flag){
            const int read_regAddress = 55;
            const int numBits = 1;
            uint16_t coilStatus[numBits];

            int rc;
            {
                QMutexLocker locker(&mutex);
                rc = modbus_read_registers(modbusContext,read_regAddress,numBits,coilStatus);
            }
            if (rc == -1) {
                LOGE(QString("goHome Failed to read Modbus coils %1").arg(modbus_strerror(errno)).toUtf8());
            }
            else {
                uint16_t value = coilStatus[0];
                if(flag){
                    value|=0x80;
                }else{
                    value&=0xFF7F;
                }

                {
                    QMutexLocker locker(&mutex);
                    rc = modbus_write_register(modbusContext, read_regAddress, value);
                }
                if (rc == -1) {
                    LOGE(QString("goHome Failed to write Modbus coil %1").arg(modbus_strerror(errno)).toUtf8());
                }
            }
        };
        home(false);
        DELAY(100);
        home(true);

3,读取double

            while (!bExit){
                const int read_regAddress = 1104;
                const int numBits = 4;
                uint16_t coilStatus[numBits];
                int rc;
                {
                    QMutexLocker locker(&mutex);
                    rc = modbus_read_registers(modbusContext,read_regAddress,numBits,coilStatus);
                }
                if (rc == -1) {
                    LOGE(QString("getPos Failed to read Modbus coils %1").arg(modbus_strerror(errno)).toUtf8());
                }
                else {
                    ::uint64_t combined  =  static_cast<uint64_t>(coilStatus[3]) << 48 |static_cast<uint64_t>(coilStatus[2]) << 32|static_cast<uint64_t>(coilStatus[1]) << 16| coilStatus[0];
                    double realValue;
                    std::memcpy(&realValue, &combined, sizeof(realValue));
                    DataManager::Instance().setCurZ(realValue*1000);
                }
                DELAY(5);
            }

4,写double

//位置
        const int write_regAddress_pos = 1150;
        const int numReg = 4;
        ::uint64_t  rawPos = *reinterpret_cast<::uint64_t*>(&ptpPos);
        ::uint16_t listPos[4]{static_cast<uint16_t>(rawPos&0xFFFF),static_cast<uint16_t>((rawPos>>16)&0xFFFF),static_cast<uint16_t>((rawPos>>32)&0xFFFF),static_cast<uint16_t>((rawPos>>48)&0xFFFF)};
        int rc;
        {
            QMutexLocker locker(&mutex);
            rc = modbus_write_registers(modbusContext, write_regAddress_pos,numReg, listPos);
        }

        if (rc == -1) {
            LOGE(QString("setPos Failed to write Modbus coil %1").arg(modbus_strerror(errno)).toUtf8());
        }

5,注意

modbus_t 并不是线程安全的,因此在使用的地方 需要加锁。亲测这个 比Qt原生的好用

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

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

相关文章

学习笔记:Verilog VHDL硬件描述语言简介及在线仿真Verilog环境

RTL Verilog VHDL HLS等概念 RTL Register Transfer Level&#xff0c;寄存器传输级。它是数字电路设计中的一种抽象层次&#xff0c;描述了电路中寄存器之间的数据传输和逻辑操作。RTL代码通常用Verilog或VHDL语言编写&#xff0c;是后续综合、仿真等工作的基础。 VHDL全名Ve…

druid图形化监控 + MyBatis优化器使用

文章目录 1.集成druid图形化监控1.配置application.yml2.测试访问 http://localhost:项目端口/druid 2.MyBatis优化器(显示完整sql)1.目录2.SqlBeautyInterceptor.java&#xff1a;sql拦截器3.MybatisConfiguration.java&#xff1a;将sql拦截器注入容器4.测试5.MyBatis优化器动…

1216作业

思维导图 作业 使用无名信号量实现输出春夏秋冬 #include <myhead.h> sem_t sem1,sem2,sem3,sem4; void *fun1() {while(1){sem_wait(&sem1);sleep(1);printf("春\n");sem_post(&sem2);} }void *fun2() {while(1){sem_wait(&sem2);sleep(1);prin…

学习maven(maven 项目模块化,继承,聚合)

前言 本篇博客的核心&#xff1a;理解maven 项目模块化&#xff0c;继承&#xff0c;聚合 的含义 maven 项目模块化 含义 maven项目模块化&#xff1a;使用maven 构建项目&#xff0c;管理项目的方式&#xff0c;我们可以将maven项目根据内在的关系拆分成很多个小项目【模块】…

【Linux】自定义项目-进度条

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 准备工作&#xff1a;"\r"与"\n"字符 ①&#xff1a;基本含义 在C语言和Linux环境中&#xff0c;\r是回车符&#xff0c;\n是换行符&#xff0c;用于控制文本格式和输出…

OpenLinkSaas 2025年1月开发计划

先来看看OpenLinkSaas的大目标 在OpenLinkSaas的产品目标中&#xff0c;让开发人员更加方便的使用云资源是目标之一。通过各大云厂商的API&#xff0c;来可视化云上基础设施的数据是远远不够的。我们准备在2025年1月份增加方便管理和运营研发场景下服务器的能力。 这部分的功能…

电工电子技术实验:电压比较器及其应用电路

实验目的 1&#xff0e;了解电压比较器与运算放大器的性能区别&#xff1b; 2&#xff0e;掌握电压比较器的结构及特点&#xff1b; 3&#xff0e;掌握电压比较器电压传输特性的测试方法&#xff1b; 4&#xff0e;学习比较器在电路设计中的应用 实验原理 电压比较器是一…

代理 IP 行业现状与未来趋势分析

随着互联网的飞速发展&#xff0c;代理 IP 行业在近年来逐渐兴起并成为网络技术领域中一个备受关注的细分行业。它在数据采集、网络营销、隐私保护等多个方面发挥着重要作用&#xff0c;其行业现状与未来发展趋势值得深入探讨。 目前&#xff0c;代理 IP 行业呈现出以下几个显著…

旅游系统旅游小程序PHP+Uniapp

旅游门票预订系统&#xff0c;支持景点门票、导游产品便捷预订、美食打卡、景点分享、旅游笔记分享等综合系统 更新日志 V1.3.0 1、修复富文本标签 2、新增景点入驻【高级版本】3、新增门票核销【高级版】4、新增门票端口【高级版】

【日常笔记】Spring boot:编写 Content type = ‘text/plain‘ 接口

一、项目场景&#xff1a; 接口&#xff1a;Context-Type&#xff1a;text/plain 方式&#xff1a;POST 项目场景&#xff1a;硬件回调接口 二、实战 PostMapping(value "/xx/xxx", consumes "text/plain" ) 2.1、接口 /*** return String* time 202…

STM32F407+LAN8720A +LWIP +FreeRTOS UDP通讯

STM32F407+LAN8720A +LWIP +FreeRTOS ping通 上一篇实现了LWIP ping 通 本篇实现UDP通讯 实现如下功能: 串口1空闲中断+DMA接收,收到数据用UDP发送UDP接收,收到数据用串口1发送STM32CUBEIDE配置和代码 1. 配置UARAT1的空闲中断+DMA接收 UART1接收到数据,释放信号量,在任…

KeyFormer:使用注意力分数压缩KV缓存

Keyformer: KV Cache Reduction through Key Tokens Selection for Efficient Generative Inference 202403&#xff0c;发表在Mlsys Introduction 优化KV cache的策略&#xff0c;主要是集中在系统级别的优化上&#xff0c;比如FlashAttention、PagedAttention&#xff0c;它…

3.9 网际控制报文协议ICMP

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言1 ICMP报文的封装2 ICMP差错报告报文的类型3 不应发送ICMP差错报告报文的情况4 常用的ICMP询问报文类型5 ICMP的应用 前言 网际控制报文协议&#xff08;ICMP&#xff09…

东北大学《2024年839自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《东北大学839自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2024年真题 Part1&#xff1a;2024年完整版真题 2024年真题

百度智能云千帆AppBuilder升级,百度AI搜索组件上线,RAG支持无限容量向量存储!

百度智能云千帆 AppBuilder 发版升级&#xff01; 进一步降低开发门槛&#xff0c;落地大模型到应用的最后一公里。在千帆 AppBuilder 最新升级的 V1.1版本中&#xff0c;企业级 RAG 和 Agent 能力再度提升&#xff0c;同时组件生态与应用集成分发更加优化。 • 企业级 RAG&am…

就业相关(硕士)

一、嵌入式 1.机器人行业 1.1 大致情况 要做机器人行业&#xff0c;主要技术栈是运动控制、深度学习、强化学习、具身智能等&#xff0c;主要求职方向有运动控制算法工程师和机器人算法工程师等等。大致薪资在30w到50w不等&#xff0c;主要看方向&#xff08;双211&#xff…

Selenium操作指南

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 大家好&#xff0c;今天带大家一起系统的学习下模拟浏览器运行库Selenium&#xff0c;它是一个用于Web自动化测试及爬虫应用的重要工具。 Selenium测试直接运行在…

OpenCV相机标定与3D重建(11)用于在图像上绘制世界坐标系的三条轴函数drawFrameAxes()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 从姿态估计绘制世界/物体坐标系的轴。 cv::drawFrameAxes 是 OpenCV 库中的一个函数&#xff0c;用于在图像上绘制世界坐标系的三条轴&#xff0…

spring学习(XML中定义与配置bean(超详细)。IoC与DI入门spring案例)

目录 一、配置文件(XML)中定义与配置bean。 &#xff08;1&#xff09;bean的基础配置。&#xff08;id、class&#xff09; &#xff08;2&#xff09;bean的别名配置。 1、基本介绍。 2、demo测试。 3、NoSuchBeanDefinitionException&#xff01; &#xff08;3&#xff09;…

Docker容器编排与Docker Compose

1. Docker Compose介绍与基础概念 Docker Compose是一个用于定义和运行多容器Docker应用的工具。通过Compose&#xff0c;用户可以用YAML文件来定义多个容器的服务、网络、存储等配置&#xff0c;并通过一个命令来启动、停止和管理这些容器。它简化了多容器应用的管理&#xf…