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

server/2025/3/25 20:58:57/

《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.ppmy.cn/server/177165.html

相关文章

云原生高级实验

任务需求&#xff1a;客户端通过访问 www.nihao.com 后&#xff0c;能够通过 dns 域名解析&#xff0c;访问到 nginx 服务中由 nfs 共享的首页文件&#xff0c;内容为&#xff1a;Very good, you have successfully set up the system. 各个主机能够实现时间同步&#xff0c;并…

同旺科技USB to I2C 适配器 ---- 指令循环发送功能

所需设备&#xff1a; 内附链接 1、同旺科技USB to I2C 适配器 1、周期性的指令一次输入&#xff0c;即可以使用 “单次发送” 功能&#xff0c;也可以使用 “循环发送” 功能&#xff0c;大大减轻发送指令的编辑效率&#xff1b; 2、 “单次发送” 功能&#xff0c;“发送数据…

Kafka消息自定义序列化

文章目录 1. 默认序列化2.自定义序列化3.示例4.自定义解序列化器 1. 默认序列化 在网络中发送数据都是以字节的方式&#xff0c;Kafka也不例外。Apache Kafka支持用户给broker发送各种类型的消息。它可以是一个字符串、一个整数、一个数组或是其他任意的对象类型。序列化器(se…

基于Python+Ollama DeepSeek与MySQL进行数据分析探索

目录 1、前言 2、环境准备 3、本地部署 3.1、安装ollama 3.2、部署deepseek-r1模型 4、Python接入本地模型 4.1、示例代码 4.2、附加一些功能 4.3、其他的数据库 4.3.1、PostgreSQL 示例 4.3.2、SQLite 示例 5、总结 1、前言 在当今数据驱动的时代&#xff0c;数据…

开源模型应用落地-LangGraph101-多智能体协同实践(六)

一、前言 随着人工智能技术的快速发展,如何高效处理复杂任务成了 AI 系统的一大挑战。传统的线性架构在面对多轮对话和动态决策时常常显得无能为力。而 LangGraph 这种多智能体合作框架的出现,为这个问题提供了新的解决方案。 相关文章: 开源模型应用落地-LangGraph101-探索…

Java 中 PriorityQueue 的底层数据结构及相关分析

Java 中 PriorityQueue 的底层数据结构及相关分析 1. PriorityQueue 的底层数据结构 在 Java 中&#xff0c;PriorityQueue 的底层数据结构是基于堆&#xff08;Heap&#xff09;实现的二叉堆&#xff08;Binary Heap&#xff09;&#xff0c;默认使用最小堆&#xff08;Min …

MySQL-sql优化

插入数据 insert优化 批量插入 insert into tb_users values (1,cat),(2,mouse),(3,bird); 手动提交事务 因为MySQL中默认的事务提交方式为自动提交&#xff0c;所以在每次插入数据时&#xff0c;就是涉及到事务频繁地开启和关闭。所以将事务提交方式改成自动提交&#xf…

wps字符很分散

出现的问题图 alt enter开启自动换行即可解决 检查全局自动换行设置 在 WPS 表格中&#xff0c;点击 ​​“文件”​ → ​​“选项”​ → ​​“高级”​ → 确保 ​​“自动换行”​ 选项开启。 ​使用快捷键快速调整 ​启用自动换行&#xff1a;选中文本后按 ​Alt Enter…