Qt 使用modbus协议

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_OBJECTpublic: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;//单位gDataManager::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.rhkb.cn/news/492362.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C 语言数据类型详解

目录 一、引言 二、基本数据类型 &#xff08;一&#xff09;整型 &#xff08;二&#xff09;浮点型 &#xff08;三&#xff09;字符型 三、构造数据类型 &#xff08;一&#xff09;数组 &#xff08;二&#xff09;结构体 &#xff08;三&#xff09;联合体&#…

《通信电子电路》入门手册

因为大学这门课好多同学理解不了这门课 于是考完试后花了两天时间整理了这份笔记&#xff0c;在这分享给完全没有学懂这门课的同学&#xff0c;也帮助“理解概念才能学得进去”的同学入门 笔记&#xff1a;通信电子电路 入门手册 —— flowus笔记 对应&#xff1a;《通信电子…

vscode远程服务器运行Jupyter文件时一直无法运行

问题&#xff1a; 在vscode运行jupyter时一直让我选择python版本&#xff0c;选择了之后又没有反应&#xff0c;如下所示&#xff1a; 原因&#xff1a; 服务器上没有安装Jupyter&#xff1b;解决&#xff1a; 运行pip install jupyter 进行安装&#xff08;或者其他的方式也可…

鸿蒙项目云捐助第十七讲云捐助我的页面上半部分的实现

鸿蒙项目云捐助第十七讲云捐助我的页面上半部分的实现 在一般的应用app中都会有一个“我的”页面&#xff0c;在“我的”页面中可以完成某些设置&#xff0c;也可以完成某些附加功能&#xff0c;如“修改密码”等相关功能。这里的鸿蒙云捐助也有一个“我的”功能页面。这里对“…

网络安全(3)_安全套接字层SSL

4. 安全套接字层 4.1 安全套接字层&#xff08;SSL&#xff09;和传输层安全&#xff08;TLS&#xff09; &#xff08;1&#xff09;SSL/TLS提供的安全服务 ①SSL服务器鉴别&#xff0c;允许用户证实服务器的身份。支持SSL的客户端通过验证来自服务器的证书&#xff0c;来鉴别…

【ArcGIS Pro】水文水资源、水生态与水环境

ArcGIS Pro 是一款集数据采集、处理、分析和可视化于一体的强大 GIS 工具&#xff0c;广泛应用于水文、水资源、水生态和水环境等领域。其全面的功能使得研究人员能够高效地处理各种水文和环境数据&#xff0c;从而为科学研究和决策支持提供强有力的技术保障。在水文分析方面&a…

【前端系列】Element-UI 悟道

???欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老…

中软高科身份证云解码金融(银行)解决方案介绍

多年来&#xff0c;中软高科一直深耕身份证云解码领域&#xff0c;对身份证云解码应用于金融&#xff08;银行&#xff09;&#xff0c;进行了大量且深入的研究。从长期调研来看&#xff0c;金融&#xff08;银行&#xff09;的痛点需求主要有&#xff1a; 传统身份证解码设备…

【LeetCode】每日一题 2024_12_19 找到稳定山的下标(模拟)

前言 每天和你一起刷 LeetCode 每日一题~ 最近力扣的每日一题出的比较烂&#xff0c;难度过山车&#xff0c;导致近期的更新都三天打鱼&#xff0c;两天断更了 . . . LeetCode 启动&#xff01; 题目&#xff1a;找到稳定山的下标 代码与解题思路 先读题&#xff1a;最重要…

SpringBoot开发——详解Tomcat线程池默认最大支持200并发

文章目录 1、SpringBoot 应用可以同时并发处理多少请求2、Tomcat线程池3、底层源码3.1 runWorker3.2 workQueue.offer 4、总结 1、SpringBoot 应用可以同时并发处理多少请求 Q&#xff1a;经典面试题&#xff0c;SpringBoot 应用可以同时并发处理多少请求&#xff1f; A&#…

Linux限制root 用户的远程登录(安全要求)

前言&#xff1a;现在基本用户主机都不允许使用root来操作&#xff0c;所以本文通过创建新用户&#xff0c;并限制root用户的ssh来解决这个问题 1. 创建新账户 aingo 首先&#xff0c;使用 root 账户登录系统。 sudo useradd aingo设置 aingo 账户密码&#xff1a; sudo pa…

计算机网络之王道考研读书笔记-2

第 2 章 物理层 2.1 通信基础 2.1.1 基本概念 1.数据、信号与码元 通信的目的是传输信息。数据是指传送信息的实体。信号则是数据的电气或电磁表现&#xff0c;是数据在传输过程中的存在形式。码元是数字通信中数字信号的计量单位&#xff0c;这个时长内的信号称为 k 进制码…

谁说C比C++快?

看到这个问题&#xff0c;我我得说&#xff1a;这事儿没有那么简单。 1. 先把最大的误区打破 "C永远比C快" —— 某位1990年代的程序员 这种说法就像"自行车永远比汽车省油"一样荒谬。我们来看个例子&#xff1a; // C风格 char* str (char*)malloc(100…

【Unity3D】无限循环列表(扩展版)

基础版&#xff1a;【Unity技术分享】UGUI之ScrollRect优化_ugui scrollrect 优化-CSDN博客 using UnityEngine; using UnityEngine.UI; using System.Collections.Generic;public delegate void OnBaseLoopListItemCallback(GameObject cell, int index); public class BaseLo…

Hive SQL 查询所有函数

-- 显示所有的函数 show functions; -- 对函数year进行解释 desc function year; -- 对函数year进行详细解释&#xff0c;并举例说明 desc function extended year;– 对函数year进行解释 desc function year; – 对函数year进行详细解释&#xff0c;并举例说明 desc functio…

Android13 系统签名应用编译调试说明

Android13 系统签名应用编译调试说明 文章目录 Android13 系统签名应用编译调试说明一、前言二、系统签名应用调试步骤1、新建一个应用&#xff0c;确保可以正常编译出APK2、获取系统签名文件3、Android Studio 编译安装系统权限应用&#xff08;1&#xff09;导入签名文件生成…

基于Spring Boot的医院质控上报系统

一、系统背景与意义 医院质控上报系统旨在通过信息化手段&#xff0c;实现医院质量控制的标准化、流程化和自动化管理。该系统能够帮助医院实时监控医疗质量数据&#xff0c;及时发现和处理潜在的质量问题&#xff0c;从而确保医疗服务的安全性和有效性。同时&#xff0c;系统…

将java项目部署到linux

命令解析 Dockerfile: Dockerfile 是一个文本文件&#xff0c;包含了所有必要的指令来组装&#xff08;build&#xff09;一个 Docker 镜像。 docker build: 根据 Dockerfile 或标准指令来构建一个新的镜像。 docker save: 将本地镜像保存为一个 tar 文件。 docker load: 从…

LeetCode:226.翻转二叉树

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;226.翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 …

Webpack学习笔记(2)

1.什么是loader? 上图是Webpack打包简易流程&#xff0c;webpack本身只能理解js和json这样的文件&#xff0c;loader可以让webpack解析其他类型文件&#xff0c;并且将文件转换成模块供我们使用。 test识别出那些文件被转换&#xff0c;use定义转换时使用哪个loader转换 上图…