【Qt】Qt + Modbus 服务端学习笔记

《Qt + Modbus 服务端学习笔记》

1.因为项目的需要,要写一个modbus通信,csdn上感觉有些回答,代码是人工智能生成的,有些细节不对。我这个经过实测,是可以直接用的。

首先要包含Qt 的相关模块

在这里插入图片描述

Qt Modbus 模块主要包含以下几类关键组件:

  • 设备类:如 QModbusTcpServerQModbusTcpClient,分别用于创建 Modbus TCP 服务器和客户端,提供了建立连接、断开连接等基础操作接口。

  • 数据单元类:例如 QModbusDataUnit,用于表示 Modbus 协议中的数据单元,可方便地操作和管理 Modbus 寄存器中的数据。

  • 协议数据单元类:像 QModbusPdu,它表示 Modbus 协议数据单元,用于处理 Modbus 请求和响应的协议层数据。

  • 初始化:在 ModeBusServer 类的构造函数里,初始化 QModbusTcpServer 和定时器,同时建立信号与槽的连接,以处理定时器超时、数据写入、状态改变等事件。

  • 启动与停止

    • startServer 函数会对输入参数进行检查,设定 Modbus 数据单元映射和连接参数,尝试连接设备,若成功就启动数据更新定时器。
    • stopServer 函数则停止定时器,断开服务器连接。
  • 数据更新:借助定时器,定时调用 updateData 函数。此函数从 SystemInfoCollector 获取系统信息,将其写入 Modbus 数据单元,再设置到服务器里。

  • 数据处理

    • printUpdateData 函数用于打印更新后的数据和解析出的系统信息。
    • handleRequest 函数处理客户端请求,记录请求信息。
    • onDataWritten 函数处理数据写入事件。

运行结果

在这里插入图片描述

#ifndef MODEBUSSERVER_H
#define MODEBUSSERVER_H#include <QObject>
#include <QModbusTcpServer>
#include <QModbusDataUnit>
#include <QTimer>
#include <QMap>
#include <memory> // 包含 std::unique_ptr 所在的头文件
#include <iostream>
#include <QDateTime>#include "SystemInfoCollector.h"
//解决中文乱码
#pragma execution_character_set("utf-8")class ModeBusServer : public QObject
{Q_OBJECT
public:explicit ModeBusServer(QObject* parent = nullptr);~ModeBusServer();void startServer(quint16 deviceID, const QString& ipAddress, quint16 port);void stopServer();void printUpdateData(QModbusDataUnit& unit);signals:void statusUpdated(const QString& message);void dataUpdated(const QModbusDataUnit& data);void requestReceived(const QModbusPdu& request, QModbusDataUnit::RegisterType table, int address, int size);private slots:void updateData();void writeSystemInfoToModbus(QModbusDataUnit& unit, SystemInfo& info);void handleRequest(const QModbusPdu& request, QModbusDataUnit::RegisterType table, int address, int size);void sendDataPeriodically();void onDataWritten(QModbusDataUnit::RegisterType table, int address, int size);
private:std::unique_ptr<QModbusTcpServer> m_modbusServer;std::unique_ptr<QTimer> m_dataUpdateTimer;std::unique_ptr<QTimer> m_sendDataTimer;QMap<int, quint16> m_dataCache; // 数据缓存quint16 m_holdingRegistersSize = 100;int m_deviceID;int m_valueIndex;SystemInfoCollector m_infoCollector;
};#endif // MODEBUSSERVER_H#include "modeBusServer.h"
#include <QDebug>
ModeBusServer::ModeBusServer(QObject* parent): QObject(parent),m_modbusServer(std::make_unique<QModbusTcpServer>(this)),m_dataUpdateTimer(std::make_unique<QTimer>(this)),m_sendDataTimer(std::make_unique<QTimer>(this))
{connect(m_dataUpdateTimer.get(), &QTimer::timeout, this, &ModeBusServer::updateData);connect(this, &ModeBusServer::statusUpdated, this, [&](const QString& statusUpdateStr) {qDebug() << statusUpdateStr;});// 修改连接,使用新的槽函数connect(m_modbusServer.get(), &QModbusTcpServer::dataWritten, this, &ModeBusServer::onDataWritten);connect(m_modbusServer.get(), &QModbusTcpServer::stateChanged, this, [this](int state) {if (state == QModbusDevice::ConnectedState) {qDebug() << "Server connected";}else if (state == QModbusDevice::UnconnectedState) {qDebug() << "Server disconnected";}});
}
ModeBusServer::~ModeBusServer()
{stopServer();
}
void ModeBusServer::startServer(quint16 deviceID, const QString& ipAddress, quint16 port)
{if (port == 0 || deviceID == 0 || ipAddress.isEmpty()) {emit statusUpdated("Invalid parameters.");return;}m_deviceID = deviceID;QModbusDataUnitMap reg;reg.insert(QModbusDataUnit::HoldingRegisters, { QModbusDataUnit::HoldingRegisters, 0, m_holdingRegistersSize });m_modbusServer->setMap(reg);m_modbusServer->setConnectionParameter(QModbusDevice::NetworkAddressParameter, ipAddress);m_modbusServer->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);m_modbusServer->setServerAddress(deviceID);if (m_modbusServer->connectDevice()) {emit statusUpdated("Server started on " + ipAddress + ":" + QString::number(port));m_dataUpdateTimer->start(500);}else {emit statusUpdated("Server start failed: " + m_modbusServer->errorString());}
}
void ModeBusServer::stopServer()
{m_dataUpdateTimer->stop();m_modbusServer->disconnectDevice();emit statusUpdated("Server stopped.");
}
void ModeBusServer::printUpdateData(QModbusDataUnit& unit)
{for (int i = 0; i < unit.valueCount(); ++i) {m_dataCache[i] = unit.value(i); // 更新缓存}qDebug() << QString("%1号设备").arg(m_deviceID) << "寄存器数据:" << unit.values();SystemInfo info1 = m_infoCollector.parseSystemInfo(unit.values());qDebug() << "Memory Usage:" << info1.memoryUsage << "%";qDebug() << "CPU Usage:" << info1.cpuUsage << "%";qDebug() << "Boot Time:" << info1.bootTime.toString(Qt::ISODate);qDebug() << "Up Time:" << info1.upTime << "seconds";}
// 更新数据
void ModeBusServer::updateData()
{m_valueIndex = 0;QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, 0, 10);SystemInfo info;// 将系统信息写入 Modbus 数据单元writeSystemInfoToModbus(unit, info);bool  isSetDataSuccess = m_modbusServer->setData(unit);//设置数据if (isSetDataSuccess){m_dataCache.clear(); // 清空缓存printUpdateData(unit);emit dataUpdated(unit);}else{qDebug() << "Failed to update data: " << m_modbusServer->errorString();}}
// 将 SystemInfo 数据写入 QModbusDataUnit
void ModeBusServer::writeSystemInfoToModbus(QModbusDataUnit& unit, SystemInfo& info) {info = m_infoCollector.getSystemInfo();// 写入内存占用率quint16 memoryUsageInt = static_cast<quint16>(info.memoryUsage * 100); // 转换为整数unit.setValue(m_valueIndex++, memoryUsageInt & 0xFFFF);// 写入 CPU 占用率quint16 cpuUsageInt = static_cast<quint16>(info.cpuUsage * 100); // 转换为整数unit.setValue(m_valueIndex++, cpuUsageInt & 0xFFFF);// 写入开机时间(时间戳)qint64 bootTimestamp = info.bootTime.toSecsSinceEpoch();unit.setValue(m_valueIndex++, static_cast<quint16>(bootTimestamp & 0xFFFF));unit.setValue(m_valueIndex++, static_cast<quint16>((bootTimestamp >> 16) & 0xFFFF));// 写入运行时间unit.setValue(m_valueIndex++, static_cast<quint16>(info.upTime & 0xFFFF));unit.setValue(m_valueIndex++, static_cast<quint16>((info.upTime >> 16) & 0xFFFF));}
void ModeBusServer::handleRequest(const QModbusPdu& request, QModbusDataUnit::RegisterType table, int address, int size)
{emit requestReceived(request, table, address, size);qDebug() << "Request received: Function Code" << request.functionCode() << ", Type" << table << ", Address" << address << ", Size" << size;
}
void ModeBusServer::sendDataPeriodically()
{qDebug() << "Periodic Data Send:";for (auto it = m_dataCache.begin(); it != m_dataCache.end(); ++it) {qDebug() << "Address" << it.key() << "@" << it.value();}
}
// 实现新增的槽函数
void ModeBusServer::onDataWritten(QModbusDataUnit::RegisterType table, int address, int size)
{// 处理数据写入事件,可根据需要扩展qDebug() << "写入数据类型" << table << "从地址" << address << "开始。" << "数据长度:" << size;}}
}
// 实现新增的槽函数
void ModeBusServer::onDataWritten(QModbusDataUnit::RegisterType table, int address, int size)
{// 处理数据写入事件,可根据需要扩展qDebug() << "写入数据类型" << table << "从地址" << address << "开始。" << "数据长度:" << size;}

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

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

相关文章

CherryStudio + 火山引擎DeepSeek R1 告别服务器繁忙

CherryStudio 火山引擎DeepSeek R1 告别服务器繁忙 一、下载CherryStudio并安装 CherryStudio是功能强大的多模型桌面客户端&#xff0c;支持Windows、macOS和Linux系统。集成了多种主流的大语言模型&#xff08;如OpenAI、DeepSeek、Gemini等&#xff09;以及本地模型运行功…

医院人事科室病区管理系统基于Spring Boot-SSM

目录 摘要 一、研究背景与意义 二、国内外研究现状 三. 系统目标 四、研究目的与内容 五、研究方法与技术路线 5.1 系统技术架构 六. 系统功能 6.1 人事管理 6.2 科室病区管理 6.3 科研管理 七. 系统安全性 八. 系统运行与维护 摘要 随着医疗行业的快速发展和医院…

Unity TextMeshPro中显示建筑特殊符号

示例&#xff1a;显示效果如图 实现步骤 1、下载 SJQY 字体库 2、导入字体&#xff1a;将 SJQY 字体文件&#xff08;如 .ttf 或 .otf 文件&#xff09;导入到 Unity 项目的 Assets 文件夹中。 3、创建 TMP 字体资产 方法一 方法二 选择刚导入的字体文件&#xff0c;在…

工具层handle_excel

该工具类利用openpyxl的load_workbook加载Excel&#xff0c;通过iter_rows按行迭代数据&#xff0c;将表头和用例数据用zipdict组合成字典&#xff0c;通过list.append将字典(单条测试用例)追加到列表中&#xff0c;从而封装Excel数据解析工具。 模块/类方法/属性使用场景描述o…

九、JavaScript作用域、预解析

一、JavaScript作用域 1.JavaScript作用域 ①代码名字&#xff08;变量&#xff09;在某个范围内起作用和效果 目的是为了提高程序的可靠性更重要的是减少命名冲突 ②js的作用域&#xff08;es6&#xff09;之前&#xff1a;全局作用域 局部作用域 ③全局作用域&#xff1a;整…

Rust语言学习

Rust语言学习 通用编程概念所有权所有权引用和借用slice struct(结构体)定义并实例化一个结构体使用结构体方法语法 枚举 enums定义枚举match控制流运算符if let 简单控制流 使用包、Crate和模块管理不断增长的项目&#xff08;模块系统&#xff09;包和crate定义模块来控制作用…

Windows Docker 报错: has no HTTPS proxy,换源

pull python 3.7报错&#xff1a; 尝试拉取Docker 测试库hello world也失败 尝试使用临时镜像源&#xff0c;可以成功拉取&#xff1a; sudo docker pull docker.m.daocloud.io/hello-world说明确实是网络问题&#xff0c;需要配置镜像源&#xff0c;为了方便&#xff0c;在d…

Git远程拉取和推送配置

Git进行远程代码拉取和推送时候提示配置user.name 和 user.email 背景&#xff1a;换新电脑后使用Git进行代码拉取和推送过程中&#xff0c;提示“Make sure you configure your “user.name” and “user.email” in git.”。这个配置针对git的正常使用仅需要配置一次&#xf…

详解string类+迭代器

迭代器 概念&#xff1a;在 C 中&#xff0c;迭代器是访问容器&#xff08;如数组、列表、向量、字符串等&#xff09;元素的一种方式。迭代器提供了一种统一的接口&#xff0c;使得你可以使用相同的代码来遍历不同类型的容器。迭代器本质上是一个指针或者指针的封装&#xff0…

小红书不绑定手机号会显示ip吗

小红书作为一个生活方式分享平台&#xff0c;拥有庞大的用户群体。在小红书上&#xff0c;用户可以分享自己的生活点滴、购物心得、美食体验等&#xff0c;与其他用户进行互动交流。最近&#xff0c;不少用户对于小红书是否会在不绑定手机号的情况下显示IP属地产生了疑问&#…

Web-Machine-N7靶机实战攻略

1.安装并开启靶机 下载VirtualBox&#xff1a;https://www.virtualbox.org 导入虚拟机 设置为桥接模式 2.获取靶机IP Kali设为桥接模式 3.访问靶机 4.获取敏感目录文件和端口 gobuster dir -u http://172.16.2.68 -w /usr/share/wordlists/dirbuster/directory-list-2.3-me…

wsl配置指南

wsl配置步骤 1.安装2.列出当前的发行版3.导出要迁移的发行版&#xff0c;并指定导出的路径及文件名4.注销掉已经导出的发行版5.重新导入到新的路径&#xff0c;可以指定新的名称6.修改默认用户7.更换source8.配置gpu环境 1.安装 在microsoft store中搜索ubuntu&#xff0c;选择…

Linux|fork命令及其使用的写时拷贝技术

fork复制进程 fork通过以下步骤来复制进程&#xff1a; 分配新的进程控制块&#xff1a;内核为新进程分配一个新的进程控制块&#xff08;PCB&#xff09;&#xff0c;用于存储进程的相关信息&#xff0c;如进程 ID、状态、寄存器值、内存指针等。复制进程地址空间&#xff1…

Android Compose 框架基础按钮模块深度剖析(四)

Android Compose 框架基础按钮模块深度剖析 一、引言 在现代 Android 应用开发中&#xff0c;Android Compose 框架以其声明式编程范式和简洁高效的开发体验&#xff0c;逐渐成为开发者构建用户界面的首选。而注解在 Android Compose 框架中扮演着至关重要的角色&#xff0c;…

HarmonyOS开发,A持有B,B引用A的场景会不会导致内存泄漏,看这里!

问题 :A持有B,B引用A的场景会不会导致内存泄漏? 答案 :方舟虚拟机的内存管理和GC采用的是根可达算法,根可达算法可以解决循环引用问题,不会导致A引用B,B引用A的内存泄漏。 根可达算法原理 根可达算法以一系列被称为 “根对象”(如栈中的局部变量、静态变量等)作为起…

【数据库备份】docker中数据库备份脚本——MySql备份脚本

docker中数据库备份脚本——MySql备份脚本 #!/bin/bash# MySQL数据库信息 DB_USER"root" DB_PASSWORD"你的密码"# 备份保存主目录 BACKUP_ROOT"/data/data_backup/mysql"# 最多保留的备份日期文件夹数 MAX_DATE_FOLDERS15# 数组包含要备份的数据…

TCP、UDP协议的应用、ServerSocket和Socket、DatagramSocket和DatagramPacket

DAY13.1 Java核心基础 TCP协议 TCP 协议是面向连接的运算层协议&#xff0c;比较复杂&#xff0c;应用程序在使用TCP协议之前必须建立连接&#xff0c;才能传输数据&#xff0c;数据传输完毕之后需要释放连接 就好比现实生活中的打电话&#xff0c;首先确保电话打通了才能进…

Web爬虫利器FireCrawl:全方位助力AI训练与高效数据抓取

Web爬虫利器FireCrawl&#xff1a;全方位助力AI训练与高效数据抓取 一、FireCrawl 项目简介二、主要功能三、FireCrawl应用场景1. 大语言模型训练2. 检索增强生成&#xff08;RAG&#xff09;&#xff1a;3. 数据驱动的开发项目4. SEO 与内容优化5. 在线服务与工具集成 四、安装…

excel文件有两列,循环读取文件两列赋值到字典列表。字典的有两个key,分别为question和answer。将最终结果输出到json文件

import pandas as pd import json# 1. 读取 Excel 文件&#xff08;假设列名为 question 和 answer&#xff09; try:df pd.read_excel("input.xlsx", usecols["question", "answer"]) # 明确指定列 except Exception as e:print(f"读取文…

【C#】CS学习之Modbus通讯

摘要 本文详细描述了如何在在C#的Winform应用程序中使用NModbus库实现Modbus通讯&#xff0c;包括读取保持寄存器、以及相应的UI界面设计和事件处理。 前言 ​应用场景 Modbus 从站广泛应用于工业自动化领域&#xff1a; 1、传感器数据采集&#xff08;如温度、压力等&#xf…