使用QModbusTcpClient来做ModbusTCP来通讯,原滋原味。使用方便并且稳定,虽然网上有很多开源的,当时有时候应用起来,出了问题,真的头大。这里介绍QModbusTcpClient的使用,已经完全满足了应用程序的基本开发,并且在原有的基础上了,做了后台线程的处理,保证了读取寄存器相对同步获取,以及服务端掉线能自动重新连接等机制。
#pragma once
#include
#include
#include
#include
#include
class CModbusClient : public QThread
{
Q_OBJECT
public:
CModbusClient(QObject *parent = 0);
~CModbusClient();
signals:
void signalConnectDevice();
void signalReadRegisterData(int, int, int);
void signalWriteRegisterData(int, uint16_t, int);
private slots:
void slotErrorOccurred(QModbusDevice::Error error);
void slotStateChanged(QModbusDevice::State state);
void slotConnectDevice();
void slotReadyRead();
void slotReadRegisterData(int nStartAddress, int nNum, int type = QModbusDataUnit::HoldingRegisters);
void slotWriteRegisterData(int nStartAddress, uint16_t uValue, int type = QModbusDataUnit::HoldingRegisters);
public:
bool connect(QString strIp, int nPort);
bool isConnected();
void release();
bool getBit(uint16_t uAddr, uint16_t uBit);
bool readRegister16(uint16_t uAddr, uint16_t &uValue);
bool readRegister32(uint32_t uAddr, uint32_t &uValue);
bool readCoil(uint16_t uAddr, uint16_t &uValue);
bool setBit(uint16_t uAddr, uint16_t uBit, bool bState);
bool writeRegister16(uint16_t uAddr, uint16_t uValue);
bool writeRegister32(uint32_t uAddr, uint32_t uValue);
bool writeCoil(uint16_t uAddr, uint16_t uValue);
protected:
void run() override;
private:
QModbusTcpClient *m_pClient;
// 连接状态
bool m_bConnected;
// 程序退出,用来退出线程
bool m_bAppClose;
// 寄存器
QHash<uint16_t, uint16_t> m_readValueHoldingRegistersHash;
QHash<uint16_t, uint16_t> m_readAddrHoldingRegistersHash;
// 线圈
QHash<uint16_t, uint16_t> m_readValueCoilsHash;
QHash<uint16_t, uint16_t> m_readAddrCoilsHash;
};
#include "CModbusClient.h"
#include
#include
#include
#include
#define SERVER_ID 100
// 自定义的定时器
struct sModbusTimer
{
QTime time;
uint32_t interval;
void start(uint32_t t)
{
interval = t;
time.start();
};
bool isTimeOut()
{
return time.elapsed() > interval;
};
};
CModbusClient::CModbusClient(QObject *parent)
: QThread(parent)
,m_bConnected(false)
,m_bAppClose(false)
,m_pClient(new QModbusTcpClient())
{
// 信号槽关联
QObject::connect(this, &CModbusClient::signalConnectDevice, this, &CModbusClient::slotConnectDevice);
QObject::connect(this, &CModbusClient::signalReadRegisterData, this, &CModbusClient::slotReadRegisterData);
QObject::connect(this, &CModbusClient::signalWriteRegisterData, this, &CModbusClient::slotWriteRegisterData);
}
CModbusClient::~CModbusClient()
{
// 退出程序
m_bAppClose = true;
QThread::msleep(100);
if (m_bConnected)
m_pClient->disconnect();
}
// 连接
bool CModbusClient::connect(QString strIp, int nPort)
{
if (m_bConnected)
return true;
m_pClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, QVariant::fromValue(nPort));
m_pClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter,QVariant::fromValue(strIp));
m_pClient->setTimeout(1000);
m_pClient->setNumberOfRetries(3);
m_bConnected = m_pClient->connectDevice();
if (m_bConnected)
{
QObject::connect(m_pClient, &QModbusTcpClient::stateChanged, this, &CModbusClient::slotStateChanged);
QObject::connect(m_pClient, &QModbusTcpClient::errorOccurred, this, &CModbusClient::slotErrorOccurred);
}
else
qDebug() << m_pClient->errorString() << "\n";
if (!this->isRunning())
this->start();
return m_bConnected;
}
// 是否连接
bool CModbusClient::isConnected()
{
return m_bConnected;
}
// 资源释放
void CModbusClient::release()
{
m_bAppClose = true;
// 延时确保后台线程全部退出
QThread::msleep(200);
m_pClient->disconnect();
}
// 错误触发
void CModbusClient::slotErrorOccurred(QModbusDevice::Error error)
{
qDebug() << "ModbusClient Error Num:" << error << "\n";
qDebug() << "ModbusClient Error Info:" << m_pClient->errorString() << "\n";
}
// 连接状态变化
void CModbusClient::slotStateChanged(QModbusDevice::State state)
{
switch (state)
{
case QModbusDevice::UnconnectedState:
{
m_bConnected = false;
}break;
case QModbusDevice::ConnectedState:
{
m_bConnected = true;
}break;
case QModbusDevice::ClosingState:
{
m_bConnected = false;
}break;
default:
break;
}
qDebug() << "ModbusClient StateChanged:" << state << "\n";
}
// 连接设备
void CModbusClient::slotConnectDevice()
{
if (!m_bConnected)
m_pClient->connectDevice();
}
// 返回读取的值
void CModbusClient::slotReadyRead()
{
auto reply = qobject_cast<QModbusReply*>(sender());
if (reply)
{
if (reply->error() == QModbusDevice::NoError)
{
const QModbusDataUnit unit = reply->result();
uint16_t nStartAddr = unit.startAddress();
auto valueList = unit.values();
// 将读取的返回值直接插入到值哈希表里面
if (unit.registerType() == QModbusDataUnit::RegisterType::HoldingRegisters)
{
for (int i = 0; i < valueList.size(); i++)
{
m_readValueHoldingRegistersHash[nStartAddr + i] = valueList[i];
qDebug() << QString("Read HoldingRegisters Value[%1]:%2").arg(i + nStartAddr).arg(valueList[i]) << "\n";
}
}
else if (unit.registerType() == QModbusDataUnit::RegisterType::Coils)
{
for (int i = 0; i < valueList.size(); i++)
{
m_readValueHoldingRegistersHash[nStartAddr + i] = valueList[i];
qDebug() << QString("Read Coils Value[%1]:%2").arg(i + nStartAddr).arg(valueList[i]) << "\n";
}
}
}
else
{
qDebug() << "ReadyRead Error:" << reply->errorString() << "\n";
}
reply->deleteLater();
}
}
// 后台线程
void CModbusClient::run()
{
sModbusTimer timeout;
int nStep = 0;
while (true)
{
QThread::msleep(30);
// 线程退出
if (m_bAppClose)
return;
switch (nStep)
{
case 0:
{
if (!m_bConnected)
{
timeout.start(3 * 1000);
nStep = 1;
}
else
nStep = 2;
}break;
case 1:
{
if (timeout.isTimeOut() && !m_bConnected)
{
// 通过发送信号,再次连接设备,不同线程需要用信号发送
emit signalConnectDevice();
nStep = 0;
}
else if (m_bConnected)
nStep = 0;
}break;
case 2:
{
// 后台不断刷新读取
if (true)
{
static int nIndex = 0;
auto keys = m_readAddrHoldingRegistersHash.keys();
if (nIndex < keys.size())
{
uint16_t uAddr = keys[nIndex];
// 发送信号,读寄存器请求,不同线程需要用信号发送
emit signalReadRegisterData(uAddr, m_readAddrHoldingRegistersHash[uAddr],(int)QModbusDataUnit::RegisterType::HoldingRegisters);
nIndex++;
}
else
nIndex = 0;
}
if (true)
{
static int nIndex = 0;
auto keys = m_readAddrCoilsHash.keys();
if (nIndex < keys.size())
{
uint16_t uAddr = keys[nIndex];
// 发送信号,读寄存器请求,不同线程需要用信号发送
emit signalReadRegisterData(uAddr, m_readAddrCoilsHash[uAddr], (int)QModbusDataUnit::RegisterType::Coils);
nIndex++;
}
else
nIndex = 0;
}
nStep = 0;
}break;
default:
break;
}
}
}
// 发送寄存器读取请求
void CModbusClient::slotReadRegisterData(int nStartAddress, int nNum, int type)
{
QModbusDataUnit readUnit((QModbusDataUnit::RegisterType)type, nStartAddress, nNum);
if (auto* reply = m_pClient->sendReadRequest(readUnit, SERVER_ID))
{
if (!reply->isFinished())
QObject::connect(reply, &QModbusReply::finished, this, &CModbusClient::slotReadyRead);
else
delete reply;
}
else
qDebug() << m_pClient->errorString();
}
// 发送寄存器写入请求
void CModbusClient::slotWriteRegisterData(int nStartAddress, uint16_t uValue, int type)
{
if (!m_bConnected)
return;
const int nLength = 1;
QModbusDataUnit writeUnit((QModbusDataUnit::RegisterType)type, nStartAddress, nLength);
writeUnit.setValue(0, uValue);
auto *reply = m_pClient->sendWriteRequest(writeUnit, SERVER_ID);
if (reply != nullptr)
{
if (!reply->isFinished())
{
QObject::connect(reply, &QModbusReply::finished, this, [this, reply]()
{
if (reply->error() != QModbusDevice::NoError)
qDebug() << "write response error:" << reply->errorString();
});
}
reply->deleteLater();
}
else
qDebug() << "write request error:" << m_pClient->errorString();
}
// 写入16位值
bool CModbusClient::readRegister16(uint16_t uAddr, uint16_t &uValue)
{
if (!m_bConnected)
return false;
// 一次设置读取长度为100
const int nReadLength = 100;
// 如果值读取哈希链表没有找到,需要插入到地址哈希链表,让它在后台读取
auto itFind = m_readValueHoldingRegistersHash.find(uAddr);
if (itFind == m_readValueHoldingRegistersHash.end())
{
m_readAddrHoldingRegistersHash[uAddr] = nReadLength;
return false;
}
else
{
// 找到值,直接返回
uValue = m_readValueHoldingRegistersHash[uAddr];
}
return true;
}
// 读取32位值
bool CModbusClient::readRegister32(uint32_t uAddr, uint32_t &uValue)
{
if (!m_bConnected)
return false;
bool bRet = false;
uint16_t uData16[2] = { 0 };
bRet = readRegister16(uAddr, uData16[0]);
bRet = readRegister16(uAddr + 1, uData16[1]);
uValue = uData16[0] | (uData16[1] << 16);
return bRet;
}
// 获取位值
bool CModbusClient::getBit(uint16_t uAddr, uint16_t uBit)
{
if (!m_bConnected)
return false;
uint16_t uValue = 0;
readRegister16(uAddr, uValue);
return uValue & (1 << (uBit % 16));
}
// 读取线圈值
bool CModbusClient::readCoil(uint16_t uAddr, uint16_t &uValue)
{
const int nReadLength = 100;
auto itFind = m_readValueCoilsHash.find(uAddr);
if (itFind == m_readValueCoilsHash.end())
{
m_readAddrCoilsHash[uAddr] = nReadLength;
return false;
}
else
uValue = m_readValueCoilsHash[uAddr];
return true;
}
// 设置位值
bool CModbusClient::setBit(uint16_t uAddr, uint16_t uBit, bool bState)
{
bool bRet = false;
uint16_t uValue = 0;
bRet = readRegister16(uAddr, uValue);
if (bRet)
{
if (bState)
uValue |= (1 << (uBit % 16)); // 把某位置1
else
uValue &= ~(1 << (uBit % 16)); // 把某位置0
writeRegister16(uAddr, uValue);
}
return bRet;
}
// 写入16位值
bool CModbusClient::writeRegister16(uint16_t uAddr, uint16_t uValue)
{
bool bRet = true;
signalWriteRegisterData(uAddr, uValue, (int)QModbusDataUnit::RegisterType::HoldingRegisters);
return bRet;
}
// 写入32位值
bool CModbusClient::writeRegister32(uint32_t uAddr, uint32_t uValue)
{
bool bRet = false;
uint16_t uData16[2] = {0};
uData16[0] = uValue & 0Xffff;
uData16[1] = (uValue >> 16) & 0Xffff;
bRet = writeRegister16(uAddr, uData16[0]);
bRet = writeRegister16(uAddr+1, uData16[1]);
return bRet;
}
// 写入线圈值
bool CModbusClient::writeCoil(uint16_t uAddr, uint16_t uValue)
{
bool bRet = true;
signalWriteRegisterData(uAddr, uValue, (int)QModbusDataUnit::RegisterType::Coils);
return bRet;
}
https://download.csdn.net/download/u013083044/86784777