From 761ff946108e851f5ad5effc11ca07d1325376f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 9 Aug 2022 11:14:26 +0200 Subject: [PATCH] Introduce check reachability retries again --- huawei/huaweifusionsolar.cpp | 95 ++++++++++++++----- huawei/huaweifusionsolar.h | 11 +++ huawei/huaweifusionsolardiscovery.cpp | 25 +++-- huawei/huaweifusionsolardiscovery.h | 1 - .../tools/connectiontool/modbusrtu.py | 28 +++--- .../tools/connectiontool/modbustcp.py | 32 +++---- libnymea-modbus/tools/generate-connection.py | 91 +++++++++++++++++- 7 files changed, 215 insertions(+), 68 deletions(-) diff --git a/huawei/huaweifusionsolar.cpp b/huawei/huaweifusionsolar.cpp index 30b8dfb..c836159 100644 --- a/huawei/huaweifusionsolar.cpp +++ b/huawei/huaweifusionsolar.cpp @@ -37,6 +37,10 @@ NYMEA_LOGGING_CATEGORY(dcHuaweiFusionSolar, "HuaweiFusionSolar") HuaweiFusionSolar::HuaweiFusionSolar(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) : HuaweiFusionModbusTcpConnection(hostAddress, port, slaveId, parent) { + // Note: sometimes right after the discovery / setup the check fails the first time due to server busy error, + // this is a very slow or busy device since it returns quiet often that error. Don't faile with the first busy error... + setCheckReachableRetries(3); + connect(this, &HuaweiFusionModbusTcpConnection::connectionStateChanged, this, [=](bool connected){ if (!connected) { m_registersQueue.clear(); @@ -97,13 +101,14 @@ bool HuaweiFusionSolar::update() // Add the requests to queue, begin with power values, since they are most important m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterInverterActivePower); - m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterPowerMeterActivePower); if (m_battery1Available) m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery1Power); if (m_battery2Available) m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery2Power); + m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterPowerMeterActivePower); + m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterInverterEnergyProduced); m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterInverterDeviceStatus); m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery1Status); @@ -122,6 +127,11 @@ bool HuaweiFusionSolar::update() return true; } +double HuaweiFusionSolar::actualInverterPower() const +{ + return m_actualInverterPower; +} + void HuaweiFusionSolar::readNextRegister() { // Check if currently a reply is pending @@ -157,10 +167,11 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter active power\" register" << 32080 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 2) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 2 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 2)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 2 << "but received" << unit.values(); } else { processInverterActivePowerRegisterValues(unit.values()); + calculatActualInverterPower(); } } @@ -202,8 +213,8 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter device status\" register" << 32089 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 1) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 1 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 1)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 1 << "but received" << unit.values(); } else { processInverterDeviceStatusRegisterValues(unit.values()); } @@ -246,8 +257,8 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter energy produced\" register" << 32106 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 2) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 2 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 2)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 2 << "but received" << unit.values(); } else { processInverterEnergyProducedRegisterValues(unit.values()); } @@ -288,10 +299,9 @@ void HuaweiFusionSolar::readNextRegister() handleModbusError(reply->error()); if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); - qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Power meter active power\" register" << 37113 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 2) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 2 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 2)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 2 << "but received" << unit.values(); } else { processPowerMeterActivePowerRegisterValues(unit.values()); } @@ -334,8 +344,8 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 status\" register" << 37000 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 1) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 1 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 1)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 1 << "but received" << unit.values(); } else { processLunaBattery1StatusRegisterValues(unit.values()); } @@ -377,10 +387,11 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 power\" register" << 37001 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 2) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 2 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 2)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 2 << "but received" << unit.values(); } else { processLunaBattery1PowerRegisterValues(unit.values()); + calculatActualInverterPower(); } } finishRequest(); @@ -420,8 +431,8 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 state of charge\" register" << 37004 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 1) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 1 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 1)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 1 << "but received" << unit.values(); } else { processLunaBattery1SocRegisterValues(unit.values()); } @@ -464,8 +475,8 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 status\" register" << 37741 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 1) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 1 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 1)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 1 << "but received" << unit.values(); } else { processLunaBattery2StatusRegisterValues(unit.values()); } @@ -507,10 +518,11 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 power\" register" << 37743 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 2) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 2 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 1)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 2 << "but received" << unit.values(); } else { processLunaBattery2PowerRegisterValues(unit.values()); + calculatActualInverterPower(); } } finishRequest(); @@ -550,8 +562,8 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 state of charge\" register" << 37738 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count(); - if (unit.values().count() != 1) { - qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 1 << "but received" << unit.values().count(); + if (!valuesAreVaild(unit.values(), 1)) { + qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 1 << "but received" << unit.values(); } else { processLunaBattery2SocRegisterValues(unit.values()); } @@ -574,6 +586,45 @@ void HuaweiFusionSolar::readNextRegister() } } +bool HuaweiFusionSolar::valuesAreVaild(const QVector &values, int readSize) +{ + if (values.count() != readSize) + return false; + + // According to the documentation: + // 0x7FFF: invalid value of the floating point type returned by one register + // 0xFFFF: invalid value of a type other than the floating point type returned by one register + // 0xFFFFFFFF: invalid value returned by two registers + + if (values.count() == 2) { + bool floatingPointValid = (values.at(0) != 0x7fff && values.at(1) != 0xffff); + bool otherTypesValid = (values.at(0) != 0xffff && values.at(1) != 0xffff); + return floatingPointValid && otherTypesValid; + } + + if (values.count() == 1) + return values.at(0) != 0x7fff && values.at(0) != 0xffff; + + return true; +} + +void HuaweiFusionSolar::calculatActualInverterPower() +{ + double actualPower = m_inverterActivePower * -1000.0; + if (m_battery1Available) + actualPower += m_lunaBattery1Power; + + if (m_battery2Available) + actualPower += m_lunaBattery2Power; + + qCDebug(dcHuaweiFusionSolar()) << "Inverter power:" << m_inverterActivePower << "W Battery 1:" << m_lunaBattery1Power << "W Battery 2:" << m_lunaBattery2Power << "W -->" << "Actual inverter power:" << actualPower << "W"; + if (m_actualInverterPower != actualPower) + return; + + m_actualInverterPower = actualPower; + emit actualInverterPowerChanged(m_actualInverterPower); +} + void HuaweiFusionSolar::finishRequest() { m_currentRegisterRequest = -1; diff --git a/huawei/huaweifusionsolar.h b/huawei/huaweifusionsolar.h index 7c56ecf..cb32652 100644 --- a/huawei/huaweifusionsolar.h +++ b/huawei/huaweifusionsolar.h @@ -46,6 +46,13 @@ public: bool initialize() override; virtual bool update() override; + // The inverter shows the pv power AND the power of the connected batteries if they discharge. + // This power values represents the power taking the batteries into account. + double actualInverterPower() const; + +signals: + void actualInverterPowerChanged(double actualInverterPower); + private: QQueue m_registersQueue; QModbusReply *m_initReply = nullptr; @@ -56,10 +63,14 @@ private: bool m_battery1Available = true; bool m_battery2Available = true; + double m_actualInverterPower = 0; + QString exceptionToString(QModbusPdu::ExceptionCode exception); private slots: void readNextRegister(); + bool valuesAreVaild(const QVector &values, int readSize); + void calculatActualInverterPower(); }; diff --git a/huawei/huaweifusionsolardiscovery.cpp b/huawei/huaweifusionsolardiscovery.cpp index 10103d8..5443b5a 100644 --- a/huawei/huaweifusionsolardiscovery.cpp +++ b/huawei/huaweifusionsolardiscovery.cpp @@ -37,12 +37,7 @@ HuaweiFusionSolarDiscovery::HuaweiFusionSolarDiscovery(NetworkDeviceDiscovery *n m_port(port), m_modbusAddress(modbusAddress) { - m_gracePeriodTimer.setSingleShot(true); - m_gracePeriodTimer.setInterval(3000); - connect(&m_gracePeriodTimer, &QTimer::timeout, this, [this](){ - qCDebug(dcHuawei()) << "Discovery: Grace period timer triggered."; - finishDiscovery(); - }); + } @@ -71,7 +66,11 @@ void HuaweiFusionSolarDiscovery::startDiscovery() } } - m_gracePeriodTimer.start(); + // Finish with some delay so the last added network device information objects still can be checked. + QTimer::singleShot(3000, this, [this](){ + qCDebug(dcHuawei()) << "Discovery: Grace period timer triggered."; + finishDiscovery(); + }); }); } @@ -108,20 +107,18 @@ void HuaweiFusionSolarDiscovery::checkNetworkDevice(const NetworkDeviceInfo &net return; } - m_discoveryResults.append(networkDeviceInfo); - qCDebug(dcHuawei()) << "Discovery: --> Found" << networkDeviceInfo; + m_discoveryResults.append(networkDeviceInfo); // Done with this connection cleanupConnection(connection); }); + // Initializing... if (!connection->initialize()) { qCDebug(dcHuawei()) << "Discovery: Unable to initialize connection on" << networkDeviceInfo.address().toString() << "Continue...";; cleanupConnection(connection); } - - // Initializing... }); // If we get any error...skip this host... @@ -138,9 +135,12 @@ void HuaweiFusionSolarDiscovery::checkNetworkDevice(const NetworkDeviceInfo &net cleanupConnection(connection); }); + // Try to connect, maybe it works, maybe not, + // but retry only once to communicate with the device for reachability check... + connection->setCheckReachableRetries(1); + // Try to connect, maybe it works, maybe not... connection->connectDevice(); - } void HuaweiFusionSolarDiscovery::cleanupConnection(HuaweiFusionSolar *connection) @@ -160,7 +160,6 @@ void HuaweiFusionSolarDiscovery::finishDiscovery() qCInfo(dcHuawei()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count() << "inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz"); - m_gracePeriodTimer.stop(); emit discoveryFinished(); } diff --git a/huawei/huaweifusionsolardiscovery.h b/huawei/huaweifusionsolardiscovery.h index 9cecdf2..be3753a 100644 --- a/huawei/huaweifusionsolardiscovery.h +++ b/huawei/huaweifusionsolardiscovery.h @@ -55,7 +55,6 @@ private: quint16 m_port; quint16 m_modbusAddress; - QTimer m_gracePeriodTimer; QDateTime m_startDateTime; NetworkDeviceInfos m_networkDeviceInfos; diff --git a/libnymea-modbus/tools/connectiontool/modbusrtu.py b/libnymea-modbus/tools/connectiontool/modbusrtu.py index 09d74a1..0cb7630 100644 --- a/libnymea-modbus/tools/connectiontool/modbusrtu.py +++ b/libnymea-modbus/tools/connectiontool/modbusrtu.py @@ -278,35 +278,35 @@ def writeTestReachabilityImplementationsRtu(fileDescriptor, className, registerD writeLine(fileDescriptor, 'void %s::testReachability()' % (className)) writeLine(fileDescriptor, '{') - writeLine(fileDescriptor, ' if (m_testRechableReply)') + writeLine(fileDescriptor, ' if (m_checkRechableReply)') writeLine(fileDescriptor, ' return;') writeLine(fileDescriptor) writeLine(fileDescriptor, ' // Try to read the check reachability register %s in order to verify if the communication is working or not.' % checkReachableRegister['id']) writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Test reachability by reading \\"%s\\" register:" << %s << "size:" << %s;' % (className, checkReachableRegister['description'], checkReachableRegister['address'], checkReachableRegister['size'])) - writeLine(fileDescriptor, ' m_testRechableReply = read%s();' % (propertyName[0].upper() + propertyName[1:])) - writeLine(fileDescriptor, ' if (!m_testRechableReply) {') + writeLine(fileDescriptor, ' m_checkRechableReply = read%s();' % (propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, ' if (!m_checkRechableReply) {') writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Error occurred verifying reachability by reading \\"%s\\" register";' % (className, checkReachableRegister['description'])) - writeLine(fileDescriptor, ' emit checkReachabilityFailed();') + writeLine(fileDescriptor, ' onReachabilityCheckFailed();') writeLine(fileDescriptor, ' return;') writeLine(fileDescriptor, ' }') writeLine(fileDescriptor) - writeLine(fileDescriptor, ' if (m_testRechableReply->isFinished()) {') - writeLine(fileDescriptor, ' m_testRechableReply = nullptr;') - writeLine(fileDescriptor, ' emit checkReachabilityFailed();') + writeLine(fileDescriptor, ' if (m_checkRechableReply->isFinished()) {') + writeLine(fileDescriptor, ' m_checkRechableReply = nullptr;') + writeLine(fileDescriptor, ' onReachabilityCheckFailed();') writeLine(fileDescriptor, ' return;') writeLine(fileDescriptor, ' }') writeLine(fileDescriptor) - writeLine(fileDescriptor, ' connect(m_testRechableReply, &ModbusRtuReply::finished, this, [this](){') + writeLine(fileDescriptor, ' connect(m_checkRechableReply, &ModbusRtuReply::finished, this, [this](){') writeLine(fileDescriptor, ' // Note: we don\'t care about the result here, only the error') - writeLine(fileDescriptor, ' handleModbusError(m_testRechableReply->error());') - writeLine(fileDescriptor, ' if (m_testRechableReply->error() != ModbusRtuReply::NoError)') - writeLine(fileDescriptor, ' emit checkReachabilityFailed();') + writeLine(fileDescriptor, ' handleModbusError(m_checkRechableReply->error());') + writeLine(fileDescriptor, ' if (m_checkRechableReply->error() != ModbusRtuReply::NoError)') + writeLine(fileDescriptor, ' onReachabilityCheckFailed();') writeLine(fileDescriptor) - writeLine(fileDescriptor, ' m_testRechableReply = nullptr;') + writeLine(fileDescriptor, ' m_checkRechableReply = nullptr;') writeLine(fileDescriptor, ' });') writeLine(fileDescriptor) - writeLine(fileDescriptor, ' connect(m_testRechableReply, &ModbusRtuReply::errorOccurred, this, [this] (ModbusRtuReply::Error error){') - writeLine(fileDescriptor, ' qCDebug(dc%s()) << "ModbusRtu reply error occurred while verifying reachability by reading \\"%s\\" register" << error << m_testRechableReply->errorString();' % (className, checkReachableRegister['description'])) + writeLine(fileDescriptor, ' connect(m_checkRechableReply, &ModbusRtuReply::errorOccurred, this, [this] (ModbusRtuReply::Error error){') + writeLine(fileDescriptor, ' qCDebug(dc%s()) << "ModbusRtu reply error occurred while verifying reachability by reading \\"%s\\" register" << error << m_checkRechableReply->errorString();' % (className, checkReachableRegister['description'])) writeLine(fileDescriptor, ' });') writeLine(fileDescriptor, '}') writeLine(fileDescriptor) diff --git a/libnymea-modbus/tools/connectiontool/modbustcp.py b/libnymea-modbus/tools/connectiontool/modbustcp.py index dc2246f..6513107 100644 --- a/libnymea-modbus/tools/connectiontool/modbustcp.py +++ b/libnymea-modbus/tools/connectiontool/modbustcp.py @@ -280,36 +280,36 @@ def writeTestReachabilityImplementationsTcp(fileDescriptor, className, registerD writeLine(fileDescriptor, 'void %s::testReachability()' % (className)) writeLine(fileDescriptor, '{') - writeLine(fileDescriptor, ' if (m_testRechableReply)') + writeLine(fileDescriptor, ' if (m_checkRechableReply)') writeLine(fileDescriptor, ' return;') writeLine(fileDescriptor) writeLine(fileDescriptor, ' // Try to read the check reachability register %s in order to verify if the communication is working or not.' % checkReachableRegister['id']) writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Test reachability by reading \\"%s\\" register:" << %s << "size:" << %s;' % (className, checkReachableRegister['description'], checkReachableRegister['address'], checkReachableRegister['size'])) - writeLine(fileDescriptor, ' m_testRechableReply = read%s();' % (propertyName[0].upper() + propertyName[1:])) - writeLine(fileDescriptor, ' if (!m_testRechableReply) {') + writeLine(fileDescriptor, ' m_checkRechableReply = read%s();' % (propertyName[0].upper() + propertyName[1:])) + writeLine(fileDescriptor, ' if (!m_checkRechableReply) {') writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Error occurred verifying reachability by reading \\"%s\\" register";' % (className, checkReachableRegister['description'])) - writeLine(fileDescriptor, ' emit checkReachabilityFailed();') + writeLine(fileDescriptor, ' onReachabilityCheckFailed();') writeLine(fileDescriptor, ' return;') writeLine(fileDescriptor, ' }') writeLine(fileDescriptor) - writeLine(fileDescriptor, ' if (m_testRechableReply->isFinished()) {') - writeLine(fileDescriptor, ' m_testRechableReply->deleteLater(); // Broadcast reply returns immediatly') - writeLine(fileDescriptor, ' m_testRechableReply = nullptr;') - writeLine(fileDescriptor, ' emit checkReachabilityFailed();') + writeLine(fileDescriptor, ' if (m_checkRechableReply->isFinished()) {') + writeLine(fileDescriptor, ' m_checkRechableReply->deleteLater(); // Broadcast reply returns immediatly') + writeLine(fileDescriptor, ' m_checkRechableReply = nullptr;') + writeLine(fileDescriptor, ' onReachabilityCheckFailed();') writeLine(fileDescriptor, ' return;') writeLine(fileDescriptor, ' }') writeLine(fileDescriptor) - writeLine(fileDescriptor, ' connect(m_testRechableReply, &QModbusReply::finished, this, [this](){') - writeLine(fileDescriptor, ' handleModbusError(m_testRechableReply->error());') - writeLine(fileDescriptor, ' if (m_testRechableReply->error() != QModbusDevice::NoError)') - writeLine(fileDescriptor, ' emit checkReachabilityFailed();') + writeLine(fileDescriptor, ' connect(m_checkRechableReply, &QModbusReply::finished, this, [this](){') + writeLine(fileDescriptor, ' handleModbusError(m_checkRechableReply->error());') + writeLine(fileDescriptor, ' if (m_checkRechableReply->error() != QModbusDevice::NoError)') + writeLine(fileDescriptor, ' onReachabilityCheckFailed();') writeLine(fileDescriptor) - writeLine(fileDescriptor, ' m_testRechableReply->deleteLater();') - writeLine(fileDescriptor, ' m_testRechableReply = nullptr;') + writeLine(fileDescriptor, ' m_checkRechableReply->deleteLater();') + writeLine(fileDescriptor, ' m_checkRechableReply = nullptr;') writeLine(fileDescriptor, ' });') writeLine(fileDescriptor) - writeLine(fileDescriptor, ' connect(m_testRechableReply, &QModbusReply::errorOccurred, this, [this] (QModbusDevice::Error error){') - writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Modbus reply error occurred while verifying reachability by reading \\"%s\\" register" << error << m_testRechableReply->errorString();' % (className, checkReachableRegister['description'])) + writeLine(fileDescriptor, ' connect(m_checkRechableReply, &QModbusReply::errorOccurred, this, [this] (QModbusDevice::Error error){') + writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Modbus reply error occurred while verifying reachability by reading \\"%s\\" register" << error << m_checkRechableReply->errorString();' % (className, checkReachableRegister['description'])) writeLine(fileDescriptor, ' });') writeLine(fileDescriptor, '}') writeLine(fileDescriptor) diff --git a/libnymea-modbus/tools/generate-connection.py b/libnymea-modbus/tools/generate-connection.py index 257cd89..3e2a251 100644 --- a/libnymea-modbus/tools/generate-connection.py +++ b/libnymea-modbus/tools/generate-connection.py @@ -71,6 +71,9 @@ def writeTcpHeaderFile(): writeLine(headerFile, ' ModbusDataUtils::ByteOrder endianness() const;') writeLine(headerFile, ' void setEndianness(ModbusDataUtils::ByteOrder endianness);') writeLine(headerFile) + writeLine(headerFile, ' uint checkReachableRetries() const;') + writeLine(headerFile, ' void setCheckReachableRetries(uint checkReachableRetries);') + writeLine(headerFile) # Write registers get method declarations writePropertyGetSetMethodDeclarationsTcp(headerFile, registerJson['registers']) @@ -108,6 +111,7 @@ def writeTcpHeaderFile(): writeLine(headerFile, 'signals:') writeLine(headerFile, ' void reachableChanged(bool reachable);') writeLine(headerFile, ' void checkReachabilityFailed();') + writeLine(headerFile, ' void checkReachableRetriesChanged(uint checkReachableRetries);') writeLine(headerFile) writeLine(headerFile, ' void initializationFinished(bool success);') writeLine(headerFile, ' void updateFinished();') @@ -145,7 +149,11 @@ def writeTcpHeaderFile(): writeLine(headerFile, 'private:') writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_endianness = ModbusDataUtils::ByteOrder%s;' % endianness) writeLine(headerFile, ' quint16 m_slaveId = 1;') + writeLine(headerFile) writeLine(headerFile, ' bool m_reachable = false;') + writeLine(headerFile, ' QModbusReply *m_checkRechableReply = nullptr;') + writeLine(headerFile, ' uint m_checkReachableRetries = 0;') + writeLine(headerFile, ' uint m_checkReachableRetriesCount = 0;') writeLine(headerFile, ' bool m_communicationWorking = false;') writeLine(headerFile, ' quint8 m_communicationFailedMax = %s;' % (errorLimitUntilNotReachable)) writeLine(headerFile, ' quint8 m_communicationFailedCounter = 0;') @@ -159,7 +167,7 @@ def writeTcpHeaderFile(): writeLine(headerFile) writeLine(headerFile, ' void verifyUpdateFinished();') writeLine(headerFile) - writeLine(headerFile, ' QModbusReply *m_testRechableReply = nullptr;') + writeLine(headerFile, ' void onReachabilityCheckFailed();') writeLine(headerFile, ' void evaluateReachableState();') # End of class @@ -180,6 +188,8 @@ def writeTcpSourceFile(): writeLine(sourceFile) writeLine(sourceFile, '#include "%s"' % headerFileName) writeLine(sourceFile, '#include ') + writeLine(sourceFile, '#include ') + writeLine(sourceFile, '#include ') writeLine(sourceFile) writeLine(sourceFile, 'NYMEA_LOGGING_CATEGORY(dc%s, "%s")' % (className, className)) writeLine(sourceFile) @@ -197,11 +207,13 @@ def writeTcpSourceFile(): writeLine(sourceFile, ' m_pendingUpdateReplies.clear();') writeLine(sourceFile, ' m_communicationWorking = false;') writeLine(sourceFile, ' m_communicationFailedCounter = 0;') + writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;') writeLine(sourceFile, ' testReachability();') writeLine(sourceFile, ' } else {') writeLine(sourceFile, ' qCWarning(dc%s()) << "Modbus TCP connection diconnected from" << m_hostAddress.toString() << ". The connection is not reachable any more.";' % (className)) writeLine(sourceFile, ' m_communicationWorking = false;') writeLine(sourceFile, ' m_communicationFailedCounter = 0;') + writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;') writeLine(sourceFile, ' }') writeLine(sourceFile) writeLine(sourceFile, ' evaluateReachableState();') @@ -215,6 +227,22 @@ def writeTcpSourceFile(): writeLine(sourceFile, '}') writeLine(sourceFile) + writeLine(sourceFile, 'uint %s::checkReachableRetries() const' % (className)) + writeLine(sourceFile, '{') + writeLine(sourceFile, ' return m_checkReachableRetries;') + writeLine(sourceFile, '}') + writeLine(sourceFile) + + writeLine(sourceFile, 'void %s::setCheckReachableRetries(uint checkReachableRetries)' % (className)) + writeLine(sourceFile, '{') + writeLine(sourceFile, ' if (m_checkReachableRetries == checkReachableRetries)') + writeLine(sourceFile, ' return;') + writeLine(sourceFile) + writeLine(sourceFile, ' m_checkReachableRetries = checkReachableRetries;') + writeLine(sourceFile, ' emit checkReachableRetriesChanged(m_checkReachableRetries);') + writeLine(sourceFile, '}') + writeLine(sourceFile) + writeLine(sourceFile, 'ModbusDataUtils::ByteOrder %s::endianness() const' % (className)) writeLine(sourceFile, '{') writeLine(sourceFile, ' return m_endianness;') @@ -324,6 +352,21 @@ def writeTcpSourceFile(): writeLine(sourceFile, '}') writeLine(sourceFile) + writeLine(sourceFile, 'void %s::onReachabilityCheckFailed()' % (className)) + writeLine(sourceFile, '{') + writeLine(sourceFile, ' m_checkReachableRetriesCount++;') + writeLine(sourceFile) + writeLine(sourceFile, ' if (m_checkReachableRetriesCount <= m_checkReachableRetries) {') + writeLine(sourceFile, ' qCDebug(dc%s()) << "Reachability test failed. Retry in on second" << m_checkReachableRetriesCount << "/" << m_checkReachableRetries;' % (className)) + writeLine(sourceFile, ' QTimer::singleShot(1000, this, &%s::testReachability);' % (className)) + writeLine(sourceFile, ' return;') + writeLine(sourceFile, ' }') + writeLine(sourceFile) + writeLine(sourceFile, ' // The test reachability method failed, not retrying any more') + writeLine(sourceFile, ' emit checkReachabilityFailed();') + writeLine(sourceFile, '}') + writeLine(sourceFile) + writeLine(sourceFile, 'void %s::evaluateReachableState()' % (className)) writeLine(sourceFile, '{') writeLine(sourceFile, ' bool reachable = m_communicationWorking && connected();') @@ -332,6 +375,7 @@ def writeTcpSourceFile(): writeLine(sourceFile) writeLine(sourceFile, ' m_reachable = reachable;') writeLine(sourceFile, ' emit reachableChanged(m_reachable);') + writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;') writeLine(sourceFile, '}') writeLine(sourceFile) @@ -395,6 +439,9 @@ def writeRtuHeaderFile(): writeLine(headerFile) writeLine(headerFile, ' bool reachable() const;') writeLine(headerFile) + writeLine(headerFile, ' uint checkReachableRetries() const;') + writeLine(headerFile, ' void setCheckReachableRetries(uint checkReachableRetries);') + writeLine(headerFile) writeLine(headerFile, ' ModbusDataUtils::ByteOrder endianness() const;') writeLine(headerFile, ' void setEndianness(ModbusDataUtils::ByteOrder endianness);') writeLine(headerFile) @@ -433,6 +480,7 @@ def writeRtuHeaderFile(): writeLine(headerFile, 'signals:') writeLine(headerFile, ' void reachableChanged(bool reachable);') writeLine(headerFile, ' void checkReachabilityFailed();') + writeLine(headerFile, ' void checkReachableRetriesChanged(uint checkReachableRetries);') writeLine(headerFile) writeLine(headerFile, ' void initializationFinished(bool success);') writeLine(headerFile, ' void updateFinished();') @@ -471,7 +519,11 @@ def writeRtuHeaderFile(): writeLine(headerFile, ' ModbusRtuMaster *m_modbusRtuMaster = nullptr;') writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_endianness = ModbusDataUtils::ByteOrder%s;' % endianness) writeLine(headerFile, ' quint16 m_slaveId = 1;') + writeLine(headerFile) writeLine(headerFile, ' bool m_reachable = false;') + writeLine(headerFile, ' ModbusRtuReply *m_checkRechableReply = nullptr;') + writeLine(headerFile, ' uint m_checkReachableRetries = 0;') + writeLine(headerFile, ' uint m_checkReachableRetriesCount = 0;') writeLine(headerFile, ' bool m_communicationWorking = false;') writeLine(headerFile, ' quint8 m_communicationFailedMax = %s;' % (errorLimitUntilNotReachable)) writeLine(headerFile, ' quint8 m_communicationFailedCounter = 0;') @@ -485,7 +537,7 @@ def writeRtuHeaderFile(): writeLine(headerFile) writeLine(headerFile, ' void verifyUpdateFinished();') writeLine(headerFile) - writeLine(headerFile, ' ModbusRtuReply *m_testRechableReply = nullptr;') + writeLine(headerFile, ' void onReachabilityCheckFailed();') writeLine(headerFile, ' void evaluateReachableState();') @@ -508,6 +560,7 @@ def writeRtuSourceFile(): writeLine(sourceFile, '#include "%s"' % headerFileName) writeLine(sourceFile, '#include ') writeLine(sourceFile, '#include ') + writeLine(sourceFile, '#include ') writeLine(sourceFile) writeLine(sourceFile, 'NYMEA_LOGGING_CATEGORY(dc%s, "%s")' % (className, className)) writeLine(sourceFile) @@ -525,11 +578,13 @@ def writeRtuSourceFile(): writeLine(sourceFile, ' m_pendingUpdateReplies.clear();') writeLine(sourceFile, ' m_communicationWorking = false;') writeLine(sourceFile, ' m_communicationFailedCounter = 0;') + writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;') writeLine(sourceFile, ' testReachability();') writeLine(sourceFile, ' } else {') writeLine(sourceFile, ' qCWarning(dc%s()) << "Modbus RTU resource" << m_modbusRtuMaster->serialPort() << "disconnected. The connection is not reachable any more.";' % (className)) writeLine(sourceFile, ' m_communicationWorking = false;') writeLine(sourceFile, ' m_communicationFailedCounter = 0;') + writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;') writeLine(sourceFile, ' }') writeLine(sourceFile) writeLine(sourceFile, ' evaluateReachableState();') @@ -554,6 +609,22 @@ def writeRtuSourceFile(): writeLine(sourceFile, '}') writeLine(sourceFile) + writeLine(sourceFile, 'uint %s::checkReachableRetries() const' % (className)) + writeLine(sourceFile, '{') + writeLine(sourceFile, ' return m_checkReachableRetries;') + writeLine(sourceFile, '}') + writeLine(sourceFile) + + writeLine(sourceFile, 'void %s::setCheckReachableRetries(uint checkReachableRetries)' % (className)) + writeLine(sourceFile, '{') + writeLine(sourceFile, ' if (m_checkReachableRetries == checkReachableRetries)') + writeLine(sourceFile, ' return;') + writeLine(sourceFile) + writeLine(sourceFile, ' m_checkReachableRetries = checkReachableRetries;') + writeLine(sourceFile, ' emit checkReachableRetriesChanged(m_checkReachableRetries);') + writeLine(sourceFile, '}') + writeLine(sourceFile) + writeLine(sourceFile, 'ModbusDataUtils::ByteOrder %s::endianness() const' % (className)) writeLine(sourceFile, '{') writeLine(sourceFile, ' return m_endianness;') @@ -664,6 +735,21 @@ def writeRtuSourceFile(): writeLine(sourceFile, '}') writeLine(sourceFile) + writeLine(sourceFile, 'void %s::onReachabilityCheckFailed()' % (className)) + writeLine(sourceFile, '{') + writeLine(sourceFile, ' m_checkReachableRetriesCount++;') + writeLine(sourceFile) + writeLine(sourceFile, ' if (m_checkReachableRetriesCount <= m_checkReachableRetries) {') + writeLine(sourceFile, ' qCDebug(dc%s()) << "Reachability test failed. Retry in on second" << m_checkReachableRetriesCount << "/" << m_checkReachableRetries;' % (className)) + writeLine(sourceFile, ' QTimer::singleShot(1000, this, &%s::testReachability);' % (className)) + writeLine(sourceFile, ' return;') + writeLine(sourceFile, ' }') + writeLine(sourceFile) + writeLine(sourceFile, ' // The test reachability method failed, not retrying any more') + writeLine(sourceFile, ' emit checkReachabilityFailed();') + writeLine(sourceFile, '}') + writeLine(sourceFile) + writeLine(sourceFile, 'void %s::evaluateReachableState()' % (className)) writeLine(sourceFile, '{') writeLine(sourceFile, ' bool reachable = m_communicationWorking && m_modbusRtuMaster->connected();') @@ -672,6 +758,7 @@ def writeRtuSourceFile(): writeLine(sourceFile) writeLine(sourceFile, ' m_reachable = reachable;') writeLine(sourceFile, ' emit reachableChanged(m_reachable);') + writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;') writeLine(sourceFile, '}') writeLine(sourceFile)