柚子快報激活碼778899分享:Qt 使用modbus協(xié)議
柚子快報激活碼778899分享:Qt 使用modbus協(xié)議
Qt 框架下 使用modbus協(xié)議
一,使用Qt原生的 QModbusClient ,比如QModbusTcpClient
1,因為modbus的讀寫 需要在同一個線程中,所以需要在主線程中利用moveToThread的方式,將業(yè)務(wù)邏輯封裝到 子線程中。
2,modbus封裝
#pragma once
#include
#include
#include
#include
namespace Device {
class Modbus:public QObject{
Q_OBJECT
public:
Modbus(QObject*parent): QObject(parent){
}
protected:
bool modbusRead(QModbusTcpClient * modbusMaster,int serverAddress, QModbusDataUnit::RegisterType type, int newStartAddress, quint16 newValueCount, QList
QModbusDataUnit readUnit(type, newStartAddress, newValueCount);
QModbusReply *reply = modbusMaster->sendReadRequest(readUnit, serverAddress);
if(reply == nullptr)
{
qDebug()< return false; } while (!reply->isFinished()) { QCoreApplication::processEvents(); } if (reply->error() == QModbusDevice::NoError) { QModbusDataUnit resultUnit = reply->result(); for (int i = 0; i < static_cast array.append(resultUnit.value(i)); } return true; } else { qDebug()< return false; } } bool modbusWrite(QModbusTcpClient * modbusMaster,int serverAddress, QModbusDataUnit::RegisterType type, int newStartAddress, quint16 newValueCount, QList QModbusDataUnit writeUnit(type, newStartAddress, newValueCount); for(int i = 0; i < newValueCount; i++) writeUnit.setValue(i, array.value(i)); QModbusReply *reply = modbusMaster->sendWriteRequest(writeUnit, serverAddress); if(reply == nullptr) { qDebug()< return false; } while (!reply->isFinished()) { QCoreApplication::processEvents(); } if (reply->error() == QModbusDevice::NoError) { return true; } else { qDebug()< return false; } } }; } 3,繼承上述類,封裝業(yè)務(wù)邏輯,以壓力傳感器為例。將功能放到槽函數(shù)中。 class ModbusWorkerPressure:public Modbus{ Q_OBJECT public: explicit ModbusWorkerPressure(Modbus*parent= nullptr); ~ModbusWorkerPressure() override; public slots: void open(); void close(); void zero(); signals: void siStatus(int type,int result); private: QModbusTcpClient *modbusClient{nullptr}; bool bOpened{false}; bool bExit{false}; }; ModbusWorkerPressure::ModbusWorkerPressure(Modbus *parent): Modbus(parent) { } ModbusWorkerPressure::~ModbusWorkerPressure() { } void ModbusWorkerPressure::close() { bExit = true; DELAY(500); if (bOpened) { modbusClient->disconnectDevice(); } if (modbusClient != nullptr) { modbusClient->deleteLater(); } } void ModbusWorkerPressure::open() { modbusClient = new QModbusTcpClient(this); modbusClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "10.10.10.2"); modbusClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, 502); modbusClient->setTimeout(500); if (modbusClient->connectDevice()) { DELAY(500); while (!bExit) { QList int32_t pressure=0; if(modbusRead(modbusClient,1,QModbusDataUnit::HoldingRegisters,0x0000,0x0002,list,false)){ int result = 0; result =static_cast pressure = result; //單位g DataManager::Instance().setCurPressure(pressure); //能讀出數(shù)據(jù) 認(rèn)為連接成功 static bool execute = false; if (!execute) { bOpened = true; emit siStatus(0, 0); execute = true; } } DELAY(5); } } else { emit siStatus(0, -1); bOpened = false; } } void ModbusWorkerPressure::zero() { QList if (!modbusWrite(modbusClient,1, QModbusDataUnit::HoldingRegisters, 0x0002, 0x0001, list, false)) { LOGE(u8"壓力傳感器 清零失敗!"); } } 4, 主線程 使用moveToThread 將上述業(yè)務(wù)線程進(jìn)行封裝,然后主線程中 用信號,進(jìn)行觸發(fā) bool PressureSensor::open() { workerThread = new QThread(); modbusWorker = new ModbusWorkerPressure(); modbusWorker->moveToThread(workerThread); QObject::connect(workerThread, &QThread::started, modbusWorker, &ModbusWorkerPressure::open); QObject::connect(this, &PressureSensor::siClose, modbusWorker, &ModbusWorkerPressure::close); QObject::connect(this, &PressureSensor::siZero, modbusWorker, &ModbusWorkerPressure::zero); QObject::connect(modbusWorker, &ModbusWorkerPressure::siStatus, [&](int type, int result) { if (type == 0) { if (result == 0) { bOpened = true; } else { bOpened = false; } } }); workerThread->start(); return true; } bool PressureSensor::zero() { if(!bOpened) return false; emit siZero(); return true; } 5,此種方式 優(yōu)點(diǎn)就是Qt原生框架,但是缺點(diǎn)是 這種方式是異步的方式,想要做到同步調(diào)用,比如軸系運(yùn)動中,需要自己去同步,試過 QEventLoop的方式,但是不行,會丟失事件。 二,第三方庫 libmodbus 1,編譯及下載 Libmodbus在win11下的編譯與VS2019下的運(yùn)行_libmodbus vs2019-CSDN博客 2,寫bool auto home = [&](bool flag){ const int read_regAddress = 55; const int numBits = 1; uint16_t coilStatus[numBits]; int rc; { QMutexLocker locker(&mutex); rc = modbus_read_registers(modbusContext,read_regAddress,numBits,coilStatus); } if (rc == -1) { LOGE(QString("goHome Failed to read Modbus coils %1").arg(modbus_strerror(errno)).toUtf8()); } else { uint16_t value = coilStatus[0]; if(flag){ value|=0x80; }else{ value&=0xFF7F; } { QMutexLocker locker(&mutex); rc = modbus_write_register(modbusContext, read_regAddress, value); } if (rc == -1) { LOGE(QString("goHome Failed to write Modbus coil %1").arg(modbus_strerror(errno)).toUtf8()); } } }; home(false); DELAY(100); home(true); 3,讀取double while (!bExit){ const int read_regAddress = 1104; const int numBits = 4; uint16_t coilStatus[numBits]; int rc; { QMutexLocker locker(&mutex); rc = modbus_read_registers(modbusContext,read_regAddress,numBits,coilStatus); } if (rc == -1) { LOGE(QString("getPos Failed to read Modbus coils %1").arg(modbus_strerror(errno)).toUtf8()); } else { ::uint64_t combined = static_cast double realValue; std::memcpy(&realValue, &combined, sizeof(realValue)); DataManager::Instance().setCurZ(realValue*1000); } DELAY(5); } 4,寫double //位置 const int write_regAddress_pos = 1150; const int numReg = 4; ::uint64_t rawPos = *reinterpret_cast<::uint64_t*>(&ptpPos); ::uint16_t listPos[4]{static_cast int rc; { QMutexLocker locker(&mutex); rc = modbus_write_registers(modbusContext, write_regAddress_pos,numReg, listPos); } if (rc == -1) { LOGE(QString("setPos Failed to write Modbus coil %1").arg(modbus_strerror(errno)).toUtf8()); } 5,注意 modbus_t 并不是線程安全的,因此在使用的地方 需要加鎖。親測這個 比Qt原生的好用 柚子快報激活碼778899分享:Qt 使用modbus協(xié)議
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。