From f8bc0784370c1976e39e7462ea7185bd95a4cc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 27 May 2021 10:32:04 +0200 Subject: [PATCH] Extend modbus tcp class --- modbus/modbustcpmaster.cpp | 215 +++++++++++++++++++++++-------------- modbus/modbustcpmaster.h | 32 ++++-- 2 files changed, 157 insertions(+), 90 deletions(-) diff --git a/modbus/modbustcpmaster.cpp b/modbus/modbustcpmaster.cpp index 9d37566..e7bef77 100644 --- a/modbus/modbustcpmaster.cpp +++ b/modbus/modbustcpmaster.cpp @@ -34,13 +34,15 @@ NYMEA_LOGGING_CATEGORY(dcModbusTCP, "ModbusTCP") ModbusTCPMaster::ModbusTCPMaster(const QHostAddress &hostAddress, uint port, QObject *parent) : - QObject(parent) + QObject(parent), + m_hostAddress(hostAddress), + m_port(port) { m_modbusTcpClient = new QModbusTcpClient(this); - m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, port); - m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, hostAddress.toString()); - m_modbusTcpClient->setTimeout(1000); - m_modbusTcpClient->setNumberOfRetries(3); + m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_port); + m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_hostAddress.toString()); + m_modbusTcpClient->setTimeout(m_timeout); + m_modbusTcpClient->setNumberOfRetries(m_numberOfRetries); connect(m_modbusTcpClient, &QModbusTcpClient::stateChanged, this, &ModbusTCPMaster::onModbusStateChanged); connect(m_modbusTcpClient, &QModbusRtuSerialMaster::errorOccurred, this, &ModbusTCPMaster::onModbusErrorOccurred); @@ -52,38 +54,87 @@ ModbusTCPMaster::ModbusTCPMaster(const QHostAddress &hostAddress, uint port, QOb ModbusTCPMaster::~ModbusTCPMaster() { - if (!m_modbusTcpClient) { - m_modbusTcpClient->disconnectDevice(); - m_modbusTcpClient->deleteLater(); - } - if (!m_reconnectTimer) { + if (m_reconnectTimer) { m_reconnectTimer->stop(); - m_reconnectTimer->deleteLater(); + delete m_reconnectTimer; + m_reconnectTimer = nullptr; } + + if (m_modbusTcpClient) { + m_modbusTcpClient->disconnectDevice(); + delete m_modbusTcpClient; + m_modbusTcpClient = nullptr; + } +} + +QHostAddress ModbusTCPMaster::hostAddress() const +{ + return m_hostAddress; +} + +uint ModbusTCPMaster::port() const +{ + return m_port; +} + +bool ModbusTCPMaster::setPort(uint port) +{ + m_port = port; + return connectDevice(); +} + +bool ModbusTCPMaster::setHostAddress(const QHostAddress &hostAddress) +{ + m_hostAddress = hostAddress; + return connectDevice(); } bool ModbusTCPMaster::connectDevice() { // TCP connection to target device - qCDebug(dcModbusTCP()) << "Setting up TCP connecion"; - + qCDebug(dcModbusTCP()) << "Setting up TCP connecion" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port); if (!m_modbusTcpClient) return false; + m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_port); + m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_hostAddress.toString()); + m_modbusTcpClient->setTimeout(m_timeout); + m_modbusTcpClient->setNumberOfRetries(m_numberOfRetries); + return m_modbusTcpClient->connectDevice(); } -bool ModbusTCPMaster::connected() +void ModbusTCPMaster::disconnectDevice() +{ + if (!m_modbusTcpClient) + return; + + m_modbusTcpClient->disconnectDevice(); +} + +bool ModbusTCPMaster::connected() const { return (m_modbusTcpClient->state() == QModbusDevice::State::ConnectedState); } +int ModbusTCPMaster::numberOfRetries() const +{ + return m_modbusTcpClient->numberOfRetries(); +} + void ModbusTCPMaster::setNumberOfRetries(int number) { + m_numberOfRetries = number; m_modbusTcpClient->setNumberOfRetries(number); } +int ModbusTCPMaster::timeout() const +{ + return m_modbusTcpClient->timeout(); +} + void ModbusTCPMaster::setTimeout(int timeout) { + m_timeout = timeout; m_modbusTcpClient->setTimeout(timeout); } @@ -97,74 +148,51 @@ QModbusDevice::Error ModbusTCPMaster::error() const return m_modbusTcpClient->error(); } -uint ModbusTCPMaster::port() -{ - return m_modbusTcpClient->connectionParameter(QModbusDevice::NetworkPortParameter).toUInt(); -} - -bool ModbusTCPMaster::setHostAddress(const QHostAddress &hostAddress) -{ - m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, hostAddress.toString()); - return connectDevice(); -} - -bool ModbusTCPMaster::setPort(uint port) -{ - m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, port); - return connectDevice(); -} - void ModbusTCPMaster::onReconnectTimer() { - if(!m_modbusTcpClient->connectDevice()) { + if (!m_modbusTcpClient->connectDevice()) { m_reconnectTimer->start(10000); } } -QHostAddress ModbusTCPMaster::hostAddress() -{ - return QHostAddress(m_modbusTcpClient->connectionParameter(QModbusDevice::NetworkAddressParameter).toString()); -} - QUuid ModbusTCPMaster::readCoil(uint slaveAddress, uint registerAddress, uint size) { if (!m_modbusTcpClient) { - return ""; + return QUuid(); } - QUuid requestId = QUuid::createUuid(); + QUuid requestId = QUuid::createUuid(); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, size); if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); connect(reply, &QModbusReply::finished, this, [reply, requestId, this] { - if (reply->error() == QModbusDevice::NoError) { emit readRequestExecuted(requestId, true); const QModbusDataUnit unit = reply->result(); uint modbusAddress = unit.startAddress(); emit receivedCoil(reply->serverAddress(), modbusAddress, unit.values()); - } else { emit readRequestExecuted(requestId, false); qCWarning(dcModbusTCP()) << "Read response error:" << reply->error(); } }); - connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ + connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ qCWarning(dcModbusTCP()) << "Modbus reply error:" << error; emit readRequestError(requestId, reply->errorString()); - reply->finished(); // To make sure it will be deleted + emit reply->finished(); // To make sure it will be deleted }); + QTimer::singleShot(200, reply, &QModbusReply::deleteLater); } else { delete reply; // broadcast replies return immediately - return ""; + return QUuid(); } } else { qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString(); - return ""; + return QUuid(); } return requestId; } @@ -172,51 +200,70 @@ QUuid ModbusTCPMaster::readCoil(uint slaveAddress, uint registerAddress, uint si QUuid ModbusTCPMaster::writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector &values) { if (!m_modbusTcpClient) { - return ""; + return QUuid(); } + QUuid requestId = QUuid::createUuid(); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, values.length()); request.setValues(values); - if (QModbusReply *reply = m_modbusTcpClient->sendWriteRequest(request, slaveAddress)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); connect(reply, &QModbusReply::finished, this, [reply, requestId, this] { - if (reply->error() == QModbusDevice::NoError) { - writeRequestExecuted(requestId, true); + emit writeRequestExecuted(requestId, true); const QModbusDataUnit unit = reply->result(); uint modbusAddress = unit.startAddress(); emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.values()); - } else { - writeRequestExecuted(requestId, false); + emit writeRequestExecuted(requestId, false); qCWarning(dcModbusTCP()) << "Read response error:" << reply->error(); } reply->deleteLater(); }); - connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ + connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ qCWarning(dcModbusTCP()) << "Modbus replay error:" << error; emit writeRequestError(requestId, reply->errorString()); - reply->finished(); // To make sure it will be deleted + emit reply->finished(); // To make sure it will be deleted }); + QTimer::singleShot(2000, reply, &QModbusReply::deleteLater); } else { delete reply; // broadcast replies return immediately - return ""; + return QUuid(); } } else { qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString(); - return ""; + return QUuid(); } return requestId; } +QModbusReply *ModbusTCPMaster::sendRawRequest(const QModbusRequest &request, int serverAddress) +{ + return m_modbusTcpClient->sendRawRequest(request, serverAddress); +} + +QModbusReply *ModbusTCPMaster::sendReadRequest(const QModbusDataUnit &read, int serverAddress) +{ + return m_modbusTcpClient->sendReadRequest(read, serverAddress); +} + +QModbusReply *ModbusTCPMaster::sendReadWriteRequest(const QModbusDataUnit &read, const QModbusDataUnit &write, int serverAddress) +{ + return m_modbusTcpClient->sendReadWriteRequest(read, write, serverAddress); +} + +QModbusReply *ModbusTCPMaster::sendWriteRequest(const QModbusDataUnit &write, int serverAddress) +{ + return m_modbusTcpClient->sendWriteRequest(write, serverAddress); +} + QUuid ModbusTCPMaster::readDiscreteInput(uint slaveAddress, uint registerAddress, uint size) { if (!m_modbusTcpClient) { - return ""; + return QUuid(); } QUuid requestId = QUuid::createUuid(); @@ -226,33 +273,32 @@ QUuid ModbusTCPMaster::readDiscreteInput(uint slaveAddress, uint registerAddress if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); connect(reply, &QModbusReply::finished, this, [reply, requestId, this] { - if (reply->error() == QModbusDevice::NoError) { emit readRequestExecuted(requestId, true); const QModbusDataUnit unit = reply->result(); uint modbusAddress = unit.startAddress(); emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.values()); - } else { emit readRequestExecuted(requestId, false); qCWarning(dcModbusTCP()) << "Read response error:" << reply->error(); } }); - connect(reply, &QModbusReply::errorOccurred, this, [requestId, this] (QModbusDevice::Error error){ + connect(reply, &QModbusReply::errorOccurred, this, [requestId, this] (QModbusDevice::Error error){ qCWarning(dcModbusTCP()) << "Modbus replay error:" << error; QModbusReply *reply = qobject_cast(sender()); emit readRequestError(requestId, reply->errorString()); - reply->finished(); // To make sure it will be deleted + emit reply->finished(); // To make sure it will be deleted }); + QTimer::singleShot(2000, reply, &QModbusReply::deleteLater); } else { delete reply; // broadcast replies return immediately - return ""; + return QUuid(); } } else { qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString(); - return ""; + return QUuid(); } return requestId; } @@ -260,10 +306,10 @@ QUuid ModbusTCPMaster::readDiscreteInput(uint slaveAddress, uint registerAddress QUuid ModbusTCPMaster::readInputRegister(uint slaveAddress, uint registerAddress, uint size) { if (!m_modbusTcpClient) { - return ""; + return QUuid(); } - QUuid requestId = QUuid::createUuid(); + QUuid requestId = QUuid::createUuid(); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, size); if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) { @@ -276,26 +322,27 @@ QUuid ModbusTCPMaster::readInputRegister(uint slaveAddress, uint registerAddress const QModbusDataUnit unit = reply->result(); uint modbusAddress = unit.startAddress(); emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.values()); - } else { emit readRequestExecuted(requestId, false); qCWarning(dcModbusTCP()) << "Read response error:" << reply->error(); } }); - connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ + connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ qCWarning(dcModbusTCP()) << "Modbus reply error:" << error; emit readRequestError(requestId, reply->errorString()); - reply->finished(); // To make sure it will be deleted + emit reply->finished(); // To make sure it will be deleted }); + QTimer::singleShot(2000, reply, &QModbusReply::deleteLater); + } else { delete reply; // broadcast replies return immediately - return ""; + return QUuid(); } } else { qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString(); - return ""; + return QUuid(); } return requestId; } @@ -303,10 +350,10 @@ QUuid ModbusTCPMaster::readInputRegister(uint slaveAddress, uint registerAddress QUuid ModbusTCPMaster::readHoldingRegister(uint slaveAddress, uint registerAddress, uint size) { if (!m_modbusTcpClient) { - return ""; + return QUuid(); } - QUuid requestId = QUuid::createUuid(); + QUuid requestId = QUuid::createUuid(); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, size); if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) { @@ -327,20 +374,22 @@ QUuid ModbusTCPMaster::readHoldingRegister(uint slaveAddress, uint registerAddre } reply->deleteLater(); }); + connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ qCWarning(dcModbusTCP()) << "Modbus reply error:" << error; emit readRequestError(requestId, reply->errorString()); - reply->finished(); // To make sure it will be deleted + emit reply->finished(); // To make sure it will be deleted }); + QTimer::singleShot(2000, reply, &QModbusReply::deleteLater); } else { delete reply; // broadcast replies return immediately - return ""; + return QUuid(); } } else { qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString(); - return ""; + return QUuid(); } return requestId; } @@ -353,10 +402,10 @@ QUuid ModbusTCPMaster::writeCoil(uint slaveAddress, uint registerAddress, bool v QUuid ModbusTCPMaster::writeCoils(uint slaveAddress, uint registerAddress, const QVector &values) { if (!m_modbusTcpClient) { - return ""; + return QUuid(); } - QUuid requestId = QUuid::createUuid(); + QUuid requestId = QUuid::createUuid(); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, values.length()); request.setValues(values); @@ -377,20 +426,21 @@ QUuid ModbusTCPMaster::writeCoils(uint slaveAddress, uint registerAddress, const } reply->deleteLater(); }); - connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ + connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ qCWarning(dcModbusTCP()) << "Modbus reply error:" << error; emit writeRequestError(requestId, reply->errorString()); - reply->finished(); // To make sure it will be deleted + emit reply->finished(); // To make sure it will be deleted }); + QTimer::singleShot(2000, reply, &QModbusReply::deleteLater); } else { delete reply; // broadcast replies return immediately - return ""; + return QUuid(); } } else { qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString(); - return ""; + return QUuid(); } return requestId; } @@ -400,19 +450,20 @@ QUuid ModbusTCPMaster::writeHoldingRegister(uint slaveAddress, uint registerAddr return writeHoldingRegisters(slaveAddress, registerAddress, QVector() << value); } - void ModbusTCPMaster::onModbusErrorOccurred(QModbusDevice::Error error) { qCWarning(dcModbusTCP()) << "An error occured" << error; } - void ModbusTCPMaster::onModbusStateChanged(QModbusDevice::State state) { + qCDebug(dcModbusTCP()) << "Connection state changed for" << m_hostAddress << state; + bool connected = (state == QModbusDevice::ConnectedState); if (!connected) { //try to reconnect in 10 seconds m_reconnectTimer->start(10000); } + emit connectionStateChanged(connected); } diff --git a/modbus/modbustcpmaster.h b/modbus/modbustcpmaster.h index 4852a74..92a7372 100644 --- a/modbus/modbustcpmaster.h +++ b/modbus/modbustcpmaster.h @@ -37,8 +37,6 @@ #include #include -Q_DECLARE_LOGGING_CATEGORY(dcModbusTcp) - class ModbusTCPMaster : public QObject { Q_OBJECT @@ -46,9 +44,21 @@ public: explicit ModbusTCPMaster(const QHostAddress &hostAddress, uint port, QObject *parent = nullptr); ~ModbusTCPMaster(); + QHostAddress hostAddress() const; + bool setHostAddress(const QHostAddress &hostAddress); + + uint port() const; + bool setPort(uint port); + bool connectDevice(); - bool connected(); + void disconnectDevice(); + + bool connected() const; + + int numberOfRetries() const; void setNumberOfRetries(int number); + + int timeout() const; void setTimeout(int timeout); QString errorString() const; @@ -65,14 +75,20 @@ public: QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, quint16 value); QUuid writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector &values); - QHostAddress hostAddress(); - uint port(); - bool setHostAddress(const QHostAddress &hostAddress); - bool setPort(uint port); + // Generic requests + QModbusReply *sendRawRequest(const QModbusRequest &request, int serverAddress); + QModbusReply *sendReadRequest(const QModbusDataUnit &read, int serverAddress); + QModbusReply *sendReadWriteRequest(const QModbusDataUnit &read, const QModbusDataUnit &write, int serverAddress); + QModbusReply *sendWriteRequest(const QModbusDataUnit &write, int serverAddress); private: QTimer *m_reconnectTimer = nullptr; - QModbusTcpClient *m_modbusTcpClient; + QModbusTcpClient *m_modbusTcpClient = nullptr; + + QHostAddress m_hostAddress; + uint m_port; + int m_timeout = 1000; + int m_numberOfRetries = 3; private slots: void onReconnectTimer();