From 11f62d05558feff3f5d212c938d333dd540e0770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 26 Apr 2021 15:57:58 +0200 Subject: [PATCH] Add timeout and number of retries to the modbus RTU master and propagate the settings to the API --- libnymea-core/jsonrpc/modbusrtuhandler.cpp | 34 +++++++++++++--- libnymea-core/modbus/modbusrtumanager.cpp | 15 +++++-- libnymea-core/modbus/modbusrtumanager.h | 5 ++- libnymea-core/modbus/modbusrtumasterimpl.cpp | 41 +++++++++++++++++++- libnymea-core/modbus/modbusrtumasterimpl.h | 10 ++++- libnymea/hardware/modbus/modbusrtumaster.h | 8 +++- 6 files changed, 97 insertions(+), 16 deletions(-) diff --git a/libnymea-core/jsonrpc/modbusrtuhandler.cpp b/libnymea-core/jsonrpc/modbusrtuhandler.cpp index 5f0f702f..19d43772 100644 --- a/libnymea-core/jsonrpc/modbusrtuhandler.cpp +++ b/libnymea-core/jsonrpc/modbusrtuhandler.cpp @@ -53,6 +53,9 @@ ModbusRtuHandler::ModbusRtuHandler(ModbusRtuManager *modbusRtuManager, QObject * modbusRtuMasterDescription.insert("parity", enumRef()); modbusRtuMasterDescription.insert("stopBits", enumRef()); modbusRtuMasterDescription.insert("dataBits", enumRef()); + modbusRtuMasterDescription.insert("numberOfRetries", enumValueName(Uint)); + modbusRtuMasterDescription.insert("timeout", enumValueName(Uint)); + registerObject("ModbusRtuMaster", modbusRtuMasterDescription); QVariantMap params, returns; @@ -91,24 +94,26 @@ ModbusRtuHandler::ModbusRtuHandler(ModbusRtuManager *modbusRtuManager, QObject * // ModbusRtuMasterRemoved notification params.clear(); - description = "Emitted whenever a new modbus RTU master has been removed from the system."; + description = "Emitted whenever a modbus RTU master has been removed from the system."; params.insert("modbusUuid", enumValueName(Uuid)); registerNotification("ModbusRtuMasterRemoved", description, params); // ModbusRtuMasterChanged notification params.clear(); - description = "Emitted whenever a new modbus RTU master has been changed to the system."; + description = "Emitted whenever a modbus RTU master has been changed in the system."; params.insert("modbusRtuMaster", objectRef("ModbusRtuMaster")); registerNotification("ModbusRtuMasterChanged", description, params); // AddModbusRtuMaster params.clear(); returns.clear(); - description = "Add a new modbus RTU master with the given configuration."; + description = "Add a new modbus RTU master with the given configuration. The timeout value is in milli seconds and the minimum value is 10 ms."; params.insert("serialPort", enumValueName(String)); params.insert("baudrate", enumValueName(Uint)); params.insert("parity", enumRef()); params.insert("dataBits", enumRef()); params.insert("stopBits", enumRef()); + params.insert("numberOfRetries", enumValueName(Uint)); + params.insert("timeout", enumValueName(Uint)); returns.insert("o:modbusUuid", enumValueName(Uuid)); returns.insert("modbusError", enumRef()); registerMethod("AddModbusRtuMaster", description, params, returns); @@ -129,6 +134,8 @@ ModbusRtuHandler::ModbusRtuHandler(ModbusRtuManager *modbusRtuManager, QObject * params.insert("parity", enumRef()); params.insert("dataBits", enumRef()); params.insert("stopBits", enumRef()); + params.insert("numberOfRetries", enumValueName(Uint)); + params.insert("timeout", enumValueName(Uint)); returns.insert("modbusError", enumRef()); registerMethod("ReconfigureModbusRtuMaster", description, params, returns); @@ -211,10 +218,16 @@ JsonReply *ModbusRtuHandler::AddModbusRtuMaster(const QVariantMap ¶ms) QSerialPort::Parity parity = static_cast(enumNameToValue(params.value("parity").toString())); QSerialPort::StopBits stopBits = static_cast(enumNameToValue(params.value("stopBits").toString())); QSerialPort::DataBits dataBits = static_cast(enumNameToValue(params.value("dataBits").toString())); - - QPair result = m_modbusRtuManager->addNewModbusRtuMaster(serialPort, baudrate, parity, dataBits, stopBits); + uint numberOfRetries = params.value("numberOfRetries").toUInt(); + uint timeout = params.value("timeout").toUInt(); QVariantMap returnMap; + if (timeout < 10) { + returnMap.insert("modbusError", enumValueName(ModbusRtuManager::ModbusRtuErrorInvalidTimeoutValue)); + return createReply(returnMap); + } + + QPair result = m_modbusRtuManager->addNewModbusRtuMaster(serialPort, baudrate, parity, dataBits, stopBits, numberOfRetries, timeout); returnMap.insert("modbusError", enumValueName(result.first)); if (result.first == ModbusRtuManager::ModbusRtuErrorNoError) { returnMap.insert("modbusUuid", result.second); @@ -240,9 +253,16 @@ JsonReply *ModbusRtuHandler::ReconfigureModbusRtuMaster(const QVariantMap ¶m QSerialPort::Parity parity = static_cast(enumNameToValue(params.value("parity").toString())); QSerialPort::StopBits stopBits = static_cast(enumNameToValue(params.value("stopBits").toString())); QSerialPort::DataBits dataBits = static_cast(enumNameToValue(params.value("dataBits").toString())); + uint numberOfRetries = params.value("numberOfRetries").toUInt(); + uint timeout = params.value("timeout").toUInt(); - ModbusRtuManager::ModbusRtuError result = m_modbusRtuManager->reconfigureModbusRtuMaster(modbusUuid, serialPort, baudrate, parity, dataBits, stopBits); QVariantMap returnMap; + if (timeout < 10) { + returnMap.insert("modbusError", enumValueName(ModbusRtuManager::ModbusRtuErrorInvalidTimeoutValue)); + return createReply(returnMap); + } + + ModbusRtuManager::ModbusRtuError result = m_modbusRtuManager->reconfigureModbusRtuMaster(modbusUuid, serialPort, baudrate, parity, dataBits, stopBits, numberOfRetries, timeout); returnMap.insert("modbusError", enumValueName(result)); return createReply(returnMap); } @@ -258,6 +278,8 @@ QVariantMap ModbusRtuHandler::packModbusRtuMaster(ModbusRtuMaster *modbusRtuMast modbusRtuMasterMap.insert("parity", enumValueName(static_cast(modbusRtuMaster->parity()))); modbusRtuMasterMap.insert("stopBits", enumValueName(static_cast(modbusRtuMaster->stopBits()))); modbusRtuMasterMap.insert("dataBits", enumValueName(static_cast(modbusRtuMaster->dataBits()))); + modbusRtuMasterMap.insert("numberOfRetries", modbusRtuMaster->numberOfRetries()); + modbusRtuMasterMap.insert("timeout", modbusRtuMaster->timeout()); return modbusRtuMasterMap; } diff --git a/libnymea-core/modbus/modbusrtumanager.cpp b/libnymea-core/modbus/modbusrtumanager.cpp index 0ac63f46..3e0c6f13 100644 --- a/libnymea-core/modbus/modbusrtumanager.cpp +++ b/libnymea-core/modbus/modbusrtumanager.cpp @@ -98,7 +98,7 @@ ModbusRtuMaster *ModbusRtuManager::getModbusRtuMaster(const QUuid &modbusUuid) return nullptr; } -QPair ModbusRtuManager::addNewModbusRtuMaster(const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits) +QPair ModbusRtuManager::addNewModbusRtuMaster(const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout) { if (!supported()) { qCWarning(dcModbusRtu()) << "Cannot add new modbus RTU master because serialbus is not suppoerted on this platform."; @@ -112,7 +112,7 @@ QPair ModbusRtuManager::addNewModbusRt } QUuid modbusUuid = QUuid::createUuid(); - ModbusRtuMasterImpl *modbusMaster = new ModbusRtuMasterImpl(modbusUuid, serialPort, baudrate, parity, dataBits, stopBits, this); + ModbusRtuMasterImpl *modbusMaster = new ModbusRtuMasterImpl(modbusUuid, serialPort, baudrate, parity, dataBits, stopBits, numberOfRetries, timeout, this); ModbusRtuMaster *modbus = qobject_cast(modbusMaster); qCDebug(dcModbusRtu()) << "Adding new" << modbus << parity << dataBits << stopBits; @@ -130,7 +130,7 @@ QPair ModbusRtuManager::addNewModbusRt return QPair(ModbusRtuErrorNoError, modbusUuid); } -ModbusRtuManager::ModbusRtuError ModbusRtuManager::reconfigureModbusRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits) +ModbusRtuManager::ModbusRtuError ModbusRtuManager::reconfigureModbusRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout) { if (!supported()) { qCWarning(dcModbusRtu()) << "Cannot reconfigure modbus RTU master because serialbus is not suppoerted on this platform."; @@ -142,6 +142,7 @@ ModbusRtuManager::ModbusRtuError ModbusRtuManager::reconfigureModbusRtuMaster(co return ModbusRtuErrorUuidNotFound; } + // Take the modbus masters ModbusRtuMasterImpl *modbusMaster = qobject_cast(m_modbusRtuMasters.value(modbusUuid)); // Disconnect @@ -153,6 +154,8 @@ ModbusRtuManager::ModbusRtuError ModbusRtuManager::reconfigureModbusRtuMaster(co modbusMaster->setParity(parity); modbusMaster->setDataBits(dataBits); modbusMaster->setStopBits(stopBits); + modbusMaster->setNumberOfRetries(numberOfRetries); + modbusMaster->setTimeout(timeout); // Connect again if (!modbusMaster->connectDevice()) { @@ -214,9 +217,11 @@ void ModbusRtuManager::loadRtuMasters() QSerialPort::Parity parity = static_cast(settings.value("parity").toInt()); QSerialPort::DataBits dataBits = static_cast(settings.value("dataBits").toInt()); QSerialPort::StopBits stopBits = static_cast(settings.value("stopBits").toInt()); + int numberOfRetries = settings.value("numberOfRetries").toInt(); + int timeout = settings.value("timeout").toInt(); settings.endGroup(); // uuid - addModbusRtuMasterInternally(new ModbusRtuMasterImpl(QUuid(uuidString), serialPort, baudrate, parity, dataBits, stopBits, this)); + addModbusRtuMasterInternally(new ModbusRtuMasterImpl(QUuid(uuidString), serialPort, baudrate, parity, dataBits, stopBits, numberOfRetries, timeout, this)); } settings.endGroup(); // ModbusRtuMasters @@ -233,6 +238,8 @@ void ModbusRtuManager::saveModbusRtuMaster(ModbusRtuMaster *modbusRtuMaster) settings.setValue("parity", static_cast(modbusRtuMaster->parity())); settings.setValue("dataBits", static_cast(modbusRtuMaster->dataBits())); settings.setValue("stopBits", static_cast(modbusRtuMaster->stopBits())); + settings.setValue("numberOfRetries", modbusRtuMaster->numberOfRetries()); + settings.setValue("timeout", modbusRtuMaster->timeout()); settings.endGroup(); // uuid settings.endGroup(); // ModbusRtuMasters } diff --git a/libnymea-core/modbus/modbusrtumanager.h b/libnymea-core/modbus/modbusrtumanager.h index 67adf82f..837188c6 100644 --- a/libnymea-core/modbus/modbusrtumanager.h +++ b/libnymea-core/modbus/modbusrtumanager.h @@ -54,6 +54,7 @@ public: ModbusRtuErrorHardwareNotFound, ModbusRtuErrorResourceBusy, ModbusRtuErrorNotSupported, + ModbusRtuErrorInvalidTimeoutValue, ModbusRtuErrorConnectionFailed }; Q_ENUM(ModbusRtuError) @@ -69,8 +70,8 @@ public: bool hasModbusRtuMaster(const QUuid &modbusUuid) const; ModbusRtuMaster *getModbusRtuMaster(const QUuid &modbusUuid); - QPair addNewModbusRtuMaster(const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits); - ModbusRtuError reconfigureModbusRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits); + QPair addNewModbusRtuMaster(const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout); + ModbusRtuError reconfigureModbusRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout); ModbusRtuError removeModbusRtuMaster(const QUuid &modbusUuid); signals: diff --git a/libnymea-core/modbus/modbusrtumasterimpl.cpp b/libnymea-core/modbus/modbusrtumasterimpl.cpp index 3656b267..3d18eb4e 100644 --- a/libnymea-core/modbus/modbusrtumasterimpl.cpp +++ b/libnymea-core/modbus/modbusrtumasterimpl.cpp @@ -42,14 +42,16 @@ Q_DECLARE_LOGGING_CATEGORY(dcModbusRtu) namespace nymeaserver { -ModbusRtuMasterImpl::ModbusRtuMasterImpl(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, QObject *parent) : +ModbusRtuMasterImpl::ModbusRtuMasterImpl(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout, QObject *parent) : ModbusRtuMaster(parent), m_modbusUuid(modbusUuid), m_serialPort(serialPort), m_baudrate(baudrate), m_parity(parity), m_dataBits(dataBits), - m_stopBits(stopBits) + m_stopBits(stopBits), + m_numberOfRetries(numberOfRetries), + m_timeout(timeout) { #ifdef WITH_QTSERIALBUS m_modbus = new QModbusRtuSerialMaster(this); @@ -58,6 +60,8 @@ ModbusRtuMasterImpl::ModbusRtuMasterImpl(const QUuid &modbusUuid, const QString m_modbus->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, m_dataBits); m_modbus->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_stopBits); m_modbus->setConnectionParameter(QModbusDevice::SerialParityParameter, m_parity); + m_modbus->setNumberOfRetries(m_numberOfRetries); + m_modbus->setTimeout(m_timeout); connect(m_modbus, &QModbusTcpClient::stateChanged, this, [=](QModbusDevice::State state){ qCDebug(dcModbusRtu()) << "Connection state changed" << m_modbusUuid.toString() << m_serialPort << state; @@ -171,6 +175,8 @@ bool ModbusRtuMasterImpl::connectDevice() m_modbus->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, m_dataBits); m_modbus->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_stopBits); m_modbus->setConnectionParameter(QModbusDevice::SerialParityParameter, m_parity); + m_modbus->setNumberOfRetries(m_numberOfRetries); + m_modbus->setTimeout(m_timeout); return m_modbus->connectDevice(); #else qCWarning(dcModbusRtu()) << "Modbus is not available on this platform."; @@ -187,6 +193,37 @@ void ModbusRtuMasterImpl::disconnectDevice() #endif } +int ModbusRtuMasterImpl::numberOfRetries() const +{ + return m_numberOfRetries; +} + +void ModbusRtuMasterImpl::setNumberOfRetries(int numberOfRetries) +{ + if (m_numberOfRetries == numberOfRetries) + return; + + m_numberOfRetries = numberOfRetries; + emit numberOfRetriesChanged(m_numberOfRetries); + + m_modbus->setNumberOfRetries(m_numberOfRetries); +} + +int ModbusRtuMasterImpl::timeout() const +{ + return m_timeout; +} + +void ModbusRtuMasterImpl::setTimeout(int timeout) +{ + if (m_timeout == timeout) + return; + + m_timeout = timeout; + emit timeoutChanged(m_timeout); + m_modbus->setTimeout(m_timeout); +} + ModbusRtuReply *ModbusRtuMasterImpl::readCoil(int slaveAddress, int registerAddress, quint16 size) { #ifdef WITH_QTSERIALBUS diff --git a/libnymea-core/modbus/modbusrtumasterimpl.h b/libnymea-core/modbus/modbusrtumasterimpl.h index d9eadd8c..6df0749f 100644 --- a/libnymea-core/modbus/modbusrtumasterimpl.h +++ b/libnymea-core/modbus/modbusrtumasterimpl.h @@ -46,7 +46,7 @@ class ModbusRtuMasterImpl : public ModbusRtuMaster { Q_OBJECT public: - explicit ModbusRtuMasterImpl(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, QObject *parent = nullptr); + explicit ModbusRtuMasterImpl(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout, QObject *parent = nullptr); ~ModbusRtuMasterImpl() override = default; QUuid modbusUuid() const override; @@ -71,6 +71,12 @@ public: bool connectDevice(); void disconnectDevice(); + int numberOfRetries() const override; + void setNumberOfRetries(int numberOfRetries); + + int timeout() const override; + void setTimeout(int timeout); + // Requests ModbusRtuReply *readCoil(int slaveAddress, int registerAddress, quint16 size = 1) override; ModbusRtuReply *readDiscreteInput(int slaveAddress, int registerAddress, quint16 size = 1) override; @@ -93,6 +99,8 @@ private: QSerialPort::Parity m_parity; QSerialPort::DataBits m_dataBits; QSerialPort::StopBits m_stopBits; + int m_numberOfRetries = 3; + int m_timeout = 100; }; } diff --git a/libnymea/hardware/modbus/modbusrtumaster.h b/libnymea/hardware/modbus/modbusrtumaster.h index 3be61a6b..4be61031 100644 --- a/libnymea/hardware/modbus/modbusrtumaster.h +++ b/libnymea/hardware/modbus/modbusrtumaster.h @@ -49,6 +49,8 @@ public: virtual QSerialPort::Parity parity() const = 0; virtual QSerialPort::DataBits dataBits() const = 0; virtual QSerialPort::StopBits stopBits() = 0; + virtual int numberOfRetries() const = 0; + virtual int timeout() const = 0; virtual bool connected() const = 0; @@ -72,6 +74,8 @@ signals: void parityChanged(QSerialPort::Parity parity); void dataBitsChanged(QSerialPort::DataBits dataBits); void stopBitsChanged(QSerialPort::StopBits stopBits); + void numberOfRetriesChanged(int numberOfRetries); + void timeoutChanged(int timeout); }; @@ -81,7 +85,9 @@ inline QDebug operator<<(QDebug debug, ModbusRtuMaster *modbusRtuMaster) { debug.nospace() << ", BaudRate: " << modbusRtuMaster->baudrate(); debug.nospace() << ", " << modbusRtuMaster->dataBits(); debug.nospace() << ", " << modbusRtuMaster->stopBits(); - debug.nospace() << ", " << modbusRtuMaster->parity() << ") "; + debug.nospace() << ", " << modbusRtuMaster->parity(); + debug.nospace() << ", Retries:" << modbusRtuMaster->numberOfRetries(); + debug.nospace() << ", Timeout:" << modbusRtuMaster->numberOfRetries() << "ms)"; return debug.space(); };