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.mzph.cn/pingmian/64319.shtml

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

相关文章

C语言中怎样将NULL定义为空指针?空指针的具体值其实为0哈,即地址值为0的指针为空指针。

用下面这条命令即可&#xff1a; #define NULL (void *)0下面是对这句代码的解释&#xff1a; #define NULL (void *)01. #define 的作用 #define 是一个预处理指令&#xff0c;用于定义宏。宏替换发生在编译前的预处理阶段&#xff0c;所有出现 NULL 的地方都会被替换为 (v…

Linux中的cp命令:使用、原理与源码分析

在Linux系统中&#xff0c;cp命令是最常用的命令之一&#xff0c;用于复制文件或目录。无论是日常的文件管理&#xff0c;还是系统维护&#xff0c;cp命令都扮演着重要的角色。本文将深入探讨cp命令的使用方法、工作原理&#xff0c;并从源码层面分析其实现细节。 1. cp命令的…

oracle client linux服务器安装教程

p13390677_112040_Linux-x86-64_4of7.zip 安装前&#xff0c;确认/etc/hosts文件已配置正确 cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 10.2…

云和恩墨 zCloud 与华为云 GaussDB 完成兼容性互认证

近日&#xff0c;云和恩墨&#xff08;北京&#xff09;信息技术有限公司&#xff08;以下简称&#xff1a;云和恩墨&#xff09;的多元数据库智能管理平台 zCloud 与华为云计算技术有限公司&#xff08;以下简称&#xff1a;华为云&#xff09;的 GaussDB 数据库完成了兼容性互…

《计算机视觉:瓶颈之辩与未来之路》

一、计算机视觉的崛起 计算机视觉是使用计算机模仿人类视觉系统的科学&#xff0c;让计算机拥有类似人类提取、处理、理解和分析图像以及图像序列的能力。它是一个多学科交叉的领域&#xff0c;与机器视觉、图像处理、人工智能、机器学习等领域密切相关。 计算机视觉行业可分为…

iOS在项目中设置 Dev、Staging 和 Prod 三个不同的环境

在 Objective-C 项目中设置 Dev、Staging 和 Prod 三个不同的环境&#xff0c;并为每个环境使用不同的 Bundle ID&#xff0c;可以通过以下步骤实现&#xff1a; 步骤 1: 创建不同的 Build Configuration 打开项目&#xff1a; 启动 Xcode 并打开你的项目。 选择项目文件&…

6.3.1 MR实战:计算总分与平均分

在本次实战中&#xff0c;我们的目标是利用Apache Hadoop的MapReduce框架来处理和分析学生成绩数据。具体来说&#xff0c;我们将计算一个包含五名学生五门科目成绩的数据集的总分和平均分。这个过程包括在云主机上准备数据&#xff0c;将成绩数据存储为文本文件&#xff0c;并…

计算机毕业设计Python+Django农产品推荐系统 农产品爬虫 农产品商城 农产品大数据 农产品数据分析可视化 PySpark Hadoop

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

如何高效获取Twitter数据:Apify平台上的推特数据采集解决方案

引言 在数据分析和市场研究领域&#xff0c;Twitter&#xff08;现在的X&#xff09;数据一直是重要的信息来源。但是&#xff0c;自从Twitter更改API定价策略后&#xff0c;获取数据的成本大幅提升。本文将介绍一个经济实惠的替代方案。 为什么需要Twitter数据&#xff1f; …

Oracle 临时表空间管理与最佳实践

Oracle 临时表空间管理与最佳实践 内容摘要 本文深入探讨了Oracle数据库中临时表空间的管理和最佳实践。主要内容包括&#xff1a; 临时表空间的概述及其在Oracle 19c多租户架构中的特点临时表空间组的优势及其创建方法非临时表空间组的临时表空间日常维护操作命令临时表空间…

【论文笔记】Editing Models with Task Arithmetic

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Editing Models with Task…

下载红米Note 9 Pro5G对应的LineageOS代码下载及编译

构建 LineageOS 进入网站&#xff1a;Info about gauguin | LineageOS Wiki&#xff0c;点击&#xff1a;Build for yourself&#xff0c;里面有详细的教程&#xff0c;我这里就按照Note 9 Pro 5G来。 机器环境 Ubuntu环境为&#xff1a;20.04.6LinagesOS版本&#xff1a;21-…

四十六:如何使用Wireshark解密TLS/SSL报文?

TLS/SSL是保护网络通信的重要协议&#xff0c;其加密机制可以有效地防止敏感信息被窃取。然而&#xff0c;在调试网络应用或分析安全问题时&#xff0c;解密TLS/SSL流量是不可避免的需求。本文将介绍如何使用Wireshark解密TLS/SSL报文。 前提条件 在解密TLS/SSL报文之前&…

Git-分布式版本控制工具

目录 1. 概述 1. 1集中式版本控制工具 1.2分布式版本控制工具 2.Git 2.1 git 工作流程 1. 概述 在开发活动中&#xff0c;我们经常会遇到以下几个场景&#xff1a;备份、代码回滚、协同开发、追溯问题代码编写人和编写时间&#xff08;追责&#xff09;等。备份的话是为了…

ffmpeg和ffplay命令行实战手册

文章目录 视频拼接用concat视频分段拼接(ffplay 不可调用seek函数进行seek)给视频添加黑边&#xff0c;让视频填充并居中显示不同分辨率视频分段拼接&#xff0c;并且&#xff0c;设置单个视频的缩放比例和摆放位置视频画中画复杂嵌套用overlay(ffplay 可调用seek函数进行seek)…

详解排序几大算法

一、插入排序 基本思想&#xff1a; 直接插入排序是一种简单的插入排序算法&#xff0c;其基本思想是&#xff1a;把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列。 步骤&#x…

Java 垃圾回收机制详解

1 垃圾回收的概念 垃圾回收&#xff08;Garbage Collection&#xff0c;GC&#xff09;是自动管理内存的一种机制&#xff0c;用于释放不再使用的对象所占用的内存空间&#xff0c;防止内存溢出。垃圾回收器通过识别和回收那些已经死亡或长时间未使用的对象&#xff0c;来优化…

车载终端_智能车载终端定制_农机/出租车/叉车/驾培车载终端MTK方案

车载终端集成了先进的技术和卓越的性能&#xff0c;采用了联发科的高效低功耗ARM处理器&#xff0c;具备八核架构&#xff0c;主频高达2.0GHz&#xff0c;基于12nm制程工艺&#xff0c;不仅性能强劲&#xff0c;而且功耗控制出色。基本配置为4GB内存与64GB存储&#xff0c;用户…

【JavaSE基础】第十七章:反射+设计模式

一、反射 1.反射(Reflection)&#xff1a;允许在程序运行状态中&#xff0c;可以获取任意类中的属性和方法&#xff0c;并且可以操作任意对象内部的属性和方法&#xff0c;这种动态获取类的信息及动态操作对象的属性和方法对应的机制称为反射机制。 2.类对象 和 类的对象(实例…

Scratch教学作品 | 3D圆柱体俄罗斯方块——旋转视角的全新挑战! ✨

今天为大家推荐一款创意十足的Scratch益智游戏——《3D圆柱体俄罗斯方块》&#xff01;由Ceratophrys制作&#xff0c;这款作品将经典俄罗斯方块与立体圆柱舞台相结合&#xff0c;为玩家带来了前所未有的空间挑战与乐趣。更棒的是&#xff0c;这款游戏的源码可以在小虎鲸Scratc…