当前位置: 首页 > news >正文

【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;

}

相关文章:

  • 论数据结构
  • 基于单片机控制的电动汽车双闭环调速系统(论文+源码)
  • PowerShell 美化 增强教程
  • go语言中空结构体
  • [代码规范]1_良好的命名规范能减轻工作负担
  • golang+redis 实现分布式限流
  • 蓝桥杯 握手问题
  • 【C#高阶编程】—单例模式详解
  • MySQL性能优化,sql优化有哪些,数据库如何优化设计(二)
  • 【软件工程】08_结构化设计方法
  • Bash 脚本基础
  • numpy学习笔记15:模拟100次随机游走,观察平均行为
  • 数据处理专题(二)
  • vue2 el-table跨分页多选以及多选回显
  • Springboot的MultipartFile,获取不到inputStream
  • SeaCMS代码审计
  • 基于深度学习的OCR+NLP,医疗化验单智能识别方案
  • 【量化实战】利用miniqmt实现远程下单的完整指南
  • 阿里开源QwQ-32B推理模型!32.5B vs 671B|仅需1/10成本
  • python函数的多种参数使用形式
  • 澎湃读报丨解放日报9个版聚焦:上海,加快建成具有全球影响力的科技创新高地
  • TCL科技一季度净利增超三倍,去年半导体显示业务营收创新高
  • 以“最美通缉犯”为噱头直播?光明网:违法犯罪不应成网红跳板
  • 买新房可申领学位,广州南沙出台购房入学政策
  • 解放日报头版:人民城市共建共享展新卷
  • 柴德赓、纪庸与叫歇碑