使用多线程后台批量刷寄存器的状态,在某种程度上保证了上层接口读取的时候,不会卡顿,
整体应用效果比较友好。程序应用简单稳定高效,是一个比较不错的尝试。
代码如下:
#pragma once
#include "modbustcpclient_global.h"
#include
#include
#include
#include
#include
namespace YModbus {
class Master;
};
class MODBUSTCPCLIENT_EXPORT cModbusItem : public QThread
{
Q_OBJECT
public:
cModbusItem(QObject *parent = 0);
~cModbusItem();
public:
bool connect(QString strIp, int nPort);
bool isConnected();
void setServerId(int nId);
int getServerId();
void release();
bool getBit(uint16_t uAddr, uint16_t uBit);
bool readRegister16(uint16_t uAddr, uint16_t &uValue);
bool readRegister32(uint16_t uAddr, uint32_t &uValue);
bool readCoil(uint16_t uAddr);
bool setBit(uint16_t uAddr, uint16_t uBit, bool bState);
bool writeRegister16(uint16_t uAddr, uint16_t uValue);
bool writeRegister32(uint16_t uAddr, uint32_t uValue);
bool writeCoil(uint16_t uAddr, bool bState);
private:
void addValueCoilsHash(uint16_t uAddr);
void addValueHoldingRegistersHash(uint16_t uAddr);
private:
bool ctxReadRegister(uint16_t nStartAddress, int nNum);
bool ctxReadCoil(uint16_t nStartAddress, int nNum);
bool ctxWriteRegister(uint16_t nStartAddress, uint16_t uValue);
bool ctxWriteCoil(uint16_t nStartAddress, bool bState);
protected:
void run() override;
private:
bool m_bConnected;
bool m_bAppClose;
bool m_bWriteRequestState;
bool m_bReadRequestState;
bool m_bReadNext;
int m_nServerID;
QString m_strIp;
int m_nPort;
QMutex m_mutex;
YModbus::Master *m_pMaster;
private:
QHash<uint16_t, uint16_t> m_readValueHoldingRegistersHash;
QHash<uint16_t, uint16_> m_readAddrHoldingRegistersHash;
QHash<uint16_t, uint16_t> m_readValueCoilsHash;
QHash<uint16_t, uint16_t> m_readAddrCoilsHash;
};
代码如下:
#include "ModbusItem.h"
#include "ymblog.h"
#include "Ymod/ymbutils.h"
#include "Ymod/ymbtask.h"
#include "Ymod/ymbdefs.h"
#include "Ymod/Master/ymbmaster.h"
#include "Ymod/Master/ymaster.h"
#include
#include
#include
#include
#include
#define COILS_READ_COUNT 100
#define HOLDING_REGISTERS_READ_COUNT 100
struct sModbusTimer
{
QTime time;
uint32_t interval;
void start(uint32_t t)
{
interval = t;
time.restart();
};
bool isTimeOut()
{
return time.elapsed() > interval;
};
};
cModbusItem::cModbusItem(QObject *parent)
: QThread(parent)
, m_bConnected(false)
, m_bAppClose(false)
, m_bWriteRequestState(false)
, m_bReadRequestState(false)
, m_bReadNext(true)
, m_nServerID(1)
, m_strIp("")
, m_nPort(-1)
, m_pMaster(nullptr)
{
this->start();
}
cModbusItem::~cModbusItem()
{
m_bAppClose = true;
QThread::msleep(100);
}
void cModbusItem::run()
{
static sModbusTimer timeout;
static int nMainStep = 0;
while (true)
{
if (m_bAppClose)
return;
switch (nMainStep)
{
case 0:
{
if (m_bConnected)
nMainStep = 1;
else
QThread::msleep(500);
}break;
case 1:
{
if (true)
{
static int nIndex = 0;
QHash<uint16_t, uint16_t> readAddrHoldingRegistersHashTemp;
readAddrHoldingRegistersHashTemp = m_readAddrHoldingRegistersHash;
auto keys = readAddrHoldingRegistersHashTemp.keys();
if (keys.size() > 0)
{
nIndex = nIndex < keys.size() ? nIndex : 0;
uint16_t uAddr = keys[nIndex];
int nRead = uAddr - (HOLDING_REGISTERS_READ_COUNT / 2);
// 从中间覆盖读取
ctxReadRegister(nRead < 0 ? 0 : nRead, readAddrHoldingRegistersHashTemp[uAddr]);
nIndex++;
}
}
if (true)
{
static int nIndex = 0;
QHash<uint16_t, uint16_t> readAddrCoilsHashTemp;
readAddrCoilsHashTemp = m_readAddrCoilsHash;
auto keys = readAddrCoilsHashTemp.keys();
if (keys.size() > 0)
{
nIndex = nIndex < keys.size() ? nIndex : 0;
uint16_t uAddr = keys[nIndex];
// 从中间覆盖读取
int nRead = uAddr - COILS_READ_COUNT / 2;
ctxReadCoil(nRead < 0 ? 0 : nRead, readAddrCoilsHashTemp[uAddr]);
nIndex++;
}
}
}break;
default:
break;
}
}
}
// 连接
bool cModbusItem::connect(QString strIp, int nPort)
{
if (m_bConnected)
return true;
char *chIp;
QByteArray ba = strIp.toLatin1();
chIp = ba.data();
m_pMaster = new YModbus::Master(chIp, nPort, YModbus::TCP, YModbus::POLL);
if (!m_pMaster->CheckConnect())
{
m_bConnected = false;
delete m_pMaster;
m_pMaster = nullptr;
}
else
{
m_bConnected = true;
}
m_strIp = strIp;
m_nPort = nPort;
return m_bConnected;
}
// 是否连接
bool cModbusItem::isConnected()
{
return m_bConnected;
}
void cModbusItem::setServerId(int nId)
{
m_nServerID = nId;
}
int cModbusItem::getServerId()
{
return m_nServerID;
}
void cModbusItem::release()
{
m_bAppClose = true;
QThread::msleep(30);
}
// 读取16位值
bool cModbusItem::readRegister16(uint16_t uAddr, uint16_t &uValue)
{
if (!m_bConnected)
return false;
// 如果值读取哈希链表没有找到,需要插入到地址哈希链表,让它在后台读取
auto itFind = m_readValueHoldingRegistersHash.find(uAddr);
if (itFind == m_readValueHoldingRegistersHash.end())
{
m_readAddrHoldingRegistersHash[uAddr] = HOLDING_REGISTERS_READ_COUNT;
if (true)
{
// 如果没有就先读取一次
QHash<uint16_t, uint16_t> readAddrHoldingRegistersHashTemp;
readAddrHoldingRegistersHashTemp = m_readAddrHoldingRegistersHash;
int nRead = uAddr - (HOLDING_REGISTERS_READ_COUNT / 2);
ctxReadRegister(nRead < 0 ? 0 : nRead, readAddrHoldingRegistersHashTemp[uAddr]);
auto itFindItem = m_readValueHoldingRegistersHash.find(uAddr);
if (itFindItem == m_readValueHoldingRegistersHash.end())
return false;
else
uValue = m_readValueHoldingRegistersHash[uAddr];
}
}
else
{
// 找到值,直接返回
uValue = m_readValueHoldingRegistersHash[uAddr];
}
return true;
}
// 读取32位值
bool cModbusItem::readRegister32(uint16_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 cModbusItem::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 cModbusItem::readCoil(uint16_t uAddr)
{
bool bResult = false;
uint16_t uValue = 0;
auto itFind = m_readValueCoilsHash.find(uAddr);
if (itFind == m_readValueCoilsHash.end())
{
m_readAddrCoilsHash[uAddr] = COILS_READ_COUNT;
if (true)
{
QHash<uint16_t, uint16_t> readAddrCoilsHashTemp;
readAddrCoilsHashTemp = m_readAddrCoilsHash;
// 从中间覆盖读取
int nRead = uAddr - COILS_READ_COUNT / 2;
ctxReadCoil(nRead < 0 ? 0 : nRead, readAddrCoilsHashTemp[uAddr]);
auto itFindItem = m_readValueCoilsHash.find(uAddr);
if (itFindItem == m_readValueCoilsHash.end())
bResult = false;
else
uValue = m_readValueCoilsHash[uAddr];
}
bResult = false;
}
else
uValue = m_readValueCoilsHash[uAddr];
if (true)
{
int nAddrSize = m_readAddrCoilsHash.size();
int nValueSize = m_readValueCoilsHash.size();
}
uValue == 1 ? bResult = true : bResult = false;
return bResult;
}
// 设置位值
bool cModbusItem::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 cModbusItem::writeRegister16(uint16_t uAddr, uint16_t uValue)
{
bool bRet = true;
ctxWriteRegister(uAddr, uValue);
return bRet;
}
// 写入32位值
bool cModbusItem::writeRegister32(uint16_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 cModbusItem::writeCoil(uint16_t uAddr, bool bState)
{
ctxWriteCoil(uAddr, bState);
return true;
}
// 添加线圈数据列表
void cModbusItem::addValueCoilsHash(uint16_t uAddr)
{
int nAddrStart = uAddr - (COILS_READ_COUNT / 2);
if (nAddrStart < 0)
nAddrStart = 0;
// 提前添加数据列表,避免重复查询
for (size_t i = 0; i < COILS_READ_COUNT; i++)
{
m_readValueCoilsHash[nAddrStart + i] = 0;
}
}
// 添加保持寄存器数据列表
void cModbusItem::addValueHoldingRegistersHash(uint16_t uAddr)
{
int nAddrStart = uAddr - (HOLDING_REGISTERS_READ_COUNT / 2);
if (nAddrStart < 0)
nAddrStart = 0;
// 提前添加数据列表,避免重复查询
for (size_t i = 0; i < HOLDING_REGISTERS_READ_COUNT; i++)
{
m_readValueHoldingRegistersHash[nAddrStart + i] = 0;
}
}
// 读取寄存器
bool cModbusItem::ctxReadRegister(uint16_t nStartAddress, int nNum)
{
if (!m_bConnected || m_pMaster == nullptr)
return false;
m_mutex.lock();
static uint8_t buf[BUFSIZ] = { 0 };
memset(buf, 0, sizeof(buf));
int nLen;
nLen = m_pMaster->ReadHoldingRegisters(m_nServerID, nStartAddress, nNum, buf, sizeof(buf));
if (nLen == (nNum*2) && nLen != 0)
{
QList<uint16_t> uReadDataList;
for (int i = 0; i < nLen; i++)
{
if ((i %2) ==0 )
{
uint16_t uValue = (buf[i] << 8) | buf[i+1];
uReadDataList << uValue;
}
}
// 写入数据哈希表
for (size_t i = 0; i < uReadDataList.size(); i++)
{
m_readValueHoldingRegistersHash[nStartAddress + i] = uReadDataList[i];
}
}
m_mutex.unlock();
return true;
}
// 读取线圈
bool cModbusItem::ctxReadCoil(uint16_t nStartAddress, int nNum)
{
if (!m_bConnected || m_pMaster == nullptr)
return false;
m_mutex.lock();
static uint8_t buf[BUFSIZ] = { 0 };
memset(buf, 0, sizeof(buf));
int nLen;
nLen = m_pMaster->ReadCoils(m_nServerID, nStartAddress, nNum, buf, sizeof(buf));
if (nLen > 0)
{
QList<uint16_t> uReadDataList;
for (int i = 0; i < nLen; i++)
{
uint16_t uValue = buf[i];
uReadDataList << uValue;
}
// 写入数据哈希表
for (size_t i = 0; i < nNum; i++)
{
int nIndex = i / 8;
if (nIndex >= 0 && nIndex < uReadDataList.size())
{
int nBit = i - (8)*nIndex;
if (nBit >= 0 && nBit < 8)
{
uint16_t uValue = (uReadDataList[nIndex] & (1 << nBit)) > 0 ? 1 : 0;
m_readValueCoilsHash[nStartAddress + i] = uValue;
}
}
}
}
m_mutex.unlock();
return true;
}
bool cModbusItem::ctxWriteRegister(uint16_t nStartAddress, uint16_t uValue)
{
m_mutex.lock();
int nLen = m_pMaster->WriteSingleRegister(m_nServerID, nStartAddress, uValue);
m_mutex.unlock();
return true;
}
bool cModbusItem::ctxWriteCoil(uint16_t nStartAddress, bool bState)
{
m_mutex.lock();
int nLen = m_pMaster->WriteSingleCoil(m_nServerID, nStartAddress, bState);
m_mutex.unlock();
return true;
}