From 8f046e4223d031bfba912b13e3bd8f50128ef329 Mon Sep 17 00:00:00 2001 From: jfmartel Date: Wed, 2 Oct 2019 14:13:14 -0400 Subject: [PATCH] Modbus dev... --- sources/GlobalDefine.h | 2 +- sources/Modbus/ModbusBackend.cpp | 35 ++++++++++--- sources/Modbus/ModbusBackend.h | 13 +++-- sources/Modbus/ModbusSEIDefs.h | 2 + sources/Modbus/ModbusSEIMgr.cpp | 84 ++++++++++++++++++++++++++++++-- sources/Modbus/ModbusSEIMgr.h | 18 ++++++- sources/Zonetest.cpp | 2 + 7 files changed, 138 insertions(+), 18 deletions(-) diff --git a/sources/GlobalDefine.h b/sources/GlobalDefine.h index 83ce5e8..14c9636 100644 --- a/sources/GlobalDefine.h +++ b/sources/GlobalDefine.h @@ -70,7 +70,7 @@ #define USE_NETWORKING #define TCP_SERVER_PORT 1234 #define CUSTOM_KERNEL_INSTALLED //Custom kernel with modified serial.c to disable hardware FIFO. -#define COMPILE_MODBUS_TEST_MODE //Utilisé seulement pour les tests d'intégration Modbus. +//#define COMPILE_MODBUS_TEST_MODE //Utilisé seulement pour les tests d'intégration Modbus. //Debug defs diff --git a/sources/Modbus/ModbusBackend.cpp b/sources/Modbus/ModbusBackend.cpp index 5c86495..70fff8b 100644 --- a/sources/Modbus/ModbusBackend.cpp +++ b/sources/Modbus/ModbusBackend.cpp @@ -350,8 +350,8 @@ int CModbusBackend::AnalyzeModbusResponse(CModbusTransaction Transaction) QByteArray RegisterValues = Transaction.mPDU.mData.right(ByteCount); - qDebug("Master Rx Read Holding Registers Response."); - qDebug("Data: %s",RegisterValues.toHex().data()); +// qDebug("Master Rx Read Holding Registers Response."); +// qDebug("Data: %s",RegisterValues.toHex().data()); mModbusRepo->WriteHRData(Request->mStartAddress,Request->mNbRegisters,RegisterValues); RegistersDatabaseUpdated(Request->mStartAddress, Request->mNbRegisters); @@ -374,8 +374,10 @@ int CModbusBackend::AnalyzeModbusResponse(CModbusTransaction Transaction) delete Request; return RET_ERROR; } - qDebug("Master Rx Write Single Register response. Address: %d,",RegAddress); - qDebug("Data: %s",Transaction.mPDU.mData.toHex().data()); +// qDebug("Master Rx Write Single Register response. Address: %d,",RegAddress); +// qDebug("Data: %s",Transaction.mPDU.mData.toHex().data()); + + ModbusWriteSingleRegsSuccess(); //Everything seems good. @@ -405,6 +407,7 @@ int CModbusBackend::AnalyzeModbusResponse(CModbusTransaction Transaction) // qDebug("Master Rx Write Multiple Registers response. Address: %d, Nb Reg: %d",StartAdress, NbRegisters); // qDebug("Data: %s",Transaction.mPDU.mData.toHex().data()); + ModbusWriteMultipleRegsSuccess(); //All is good. @@ -414,7 +417,8 @@ int CModbusBackend::AnalyzeModbusResponse(CModbusTransaction Transaction) { //Received "Illegal function code" response //TODO: Log this. - qDebug("Master received illegal function code 0x%x",Transaction.mPDU.mFunctionCode); + CEngLog::instance()->AddLogString(QString().sprintf("Modbus Maître, réception d'un code de fonction illégal: 0x%x",Transaction.mPDU.mFunctionCode)); +// qDebug("Master received illegal function code 0x%x",Transaction.mPDU.mFunctionCode); emit ModbusResponseException(MODBUS_EXCEPTION_ILLEGAL_FCT,MODBUS_FCT_WRITE_MULTIPLE_REGISTERS); break; } @@ -483,7 +487,7 @@ int CModbusBackend::SendReadHoldingRegistersRequest(quint16 StartAddress, quint1 //First, validate that the reading range is within our repo if(mModbusRepo->IsHRValid(StartAddress,RegisterCount) == false) { - CEngLog::instance()->AddLogString("Trying to send a read HR in an invalid range"); + CEngLog::instance()->AddLogString("ModbusBackend: Tentative de lecture d'un registre hors de portée."); return RET_ERROR; } @@ -647,12 +651,12 @@ void CModbusBackend::RequestTimerExpired() { //The max number of retry has been reached. The device is probably offline. - CZTLog::instance()->AddLogString("Modbus Maître: Nombre maximal de tentatives sans réponse atteint avec le partenaire",true); + // CZTLog::instance()->AddLogString("Modbus Maître: Nombre maximal de tentatives sans réponse atteint avec le partenaire",true); delete mRequestsList[i]; mRequestsList.removeAt(i); - + ModbusRequestMaxRetryReached(); //TODO: Manage this situation (log?) @@ -734,3 +738,18 @@ void CModbusBackend::ModbusRequestException(quint8 ExceptionCode, quint8 FctCode Q_UNUSED(FctCode) CEngLog::instance()->AddLogString("ModbusResponseException called from within master object... weird stuff!"); } + +void CModbusBackend::ModbusWriteMultipleRegsSuccess() +{ + qDebug("Weird! Default implementation of ModbusWriteMultipleRegsSuccess() called in CModbusBackend. This function should be reimplemented in the ModbusMaster child class"); +} + +void CModbusBackend::ModbusWriteSingleRegsSuccess() +{ + qDebug("Weird! Default implementation of ModbusWriteSingleRegsSuccess() called in CModbusBackend. This function should be reimplemented in the ModbusMaster child class"); +} + +void CModbusBackend::ModbusRequestMaxRetryReached() +{ + qDebug("Weird! Default implementation of ModbusRequestMaxRetryReached() called in CModbusBackend. This function should be reimplemented in the ModbusMaster child class"); +} diff --git a/sources/Modbus/ModbusBackend.h b/sources/Modbus/ModbusBackend.h index 5a10a18..42c8362 100644 --- a/sources/Modbus/ModbusBackend.h +++ b/sources/Modbus/ModbusBackend.h @@ -137,16 +137,21 @@ public: - virtual void RegistersDatabaseUpdated(quint16 StartAdderss, quint16 Length) = 0; //Fonction virtuelle à être implémentée par le parent + virtual void RegistersDatabaseUpdated(quint16 StartAdderss, quint16 Length) = 0; //Fonction virtuelle à être implémentée par la classe dérivée //Appelée lorsque de nouvelles données ont été reçues du réseau Modbus et écrites dans le registre + virtual void ModbusWriteMultipleRegsSuccess(); //Fonction virtuelle à être implémentée par la classe dérivée (Modbus Master seulement). Appelée suite à la réception d'une réponse à une requête d'écriture multiple. + virtual void ModbusWriteSingleRegsSuccess(); //Fonction virtuelle à être implémentée par la classe dérivée (Modbus Master seulement). Appelée suite à la réception d'une réponse à une requête d'écriture simple. + virtual void ModbusRequestMaxRetryReached(); //Fonction virtuelle à être implémentée par la classe dérivée (Modbus Master seulement). Appelée lors de l'atteinte du nombre maximal d'essai de transmission d'une requête. + + //Master Exception - virtual void ModbusResponseException(quint8 ExceptionCode, quint8 FctCode); //Fonction virtuelle à être implémentée par le parent - //Sert à informer le parent lorsqu'une exception se produit dans la communication Modbus (réponse) + virtual void ModbusResponseException(quint8 ExceptionCode, quint8 FctCode) = 0; //Fonction virtuelle à être implémentée par la classe dérivée + //Sert à informer la classe dérivée lorsqu'une exception se produit dans la communication Modbus (réponse) //Slave Exception - virtual void ModbusRequestException(quint8 ExceptionCode, quint8 FctCode); //Fonction virtuelle à être implémentée par le parent + virtual void ModbusRequestException(quint8 ExceptionCode, quint8 FctCode) = 0; //Fonction virtuelle à être implémentée par la classe dérivée //Sert à informer le parent lorsqu'une exception se produit dans la communication Modbus (requête) diff --git a/sources/Modbus/ModbusSEIDefs.h b/sources/Modbus/ModbusSEIDefs.h index de2f3cf..9475546 100644 --- a/sources/Modbus/ModbusSEIDefs.h +++ b/sources/Modbus/ModbusSEIDefs.h @@ -10,7 +10,9 @@ #define SEI_MODBUS_ZT_WATCHDOG_REG 2027 #define SEI_MODBUS_SEI_WATCHDOG_REG 2038 +#define SEI_MODBUS_SEI_WATCHDOG_MASK 0x0001 +#define SEI_MODBUS_SEI_ALARMS_RESET_REG 2037 #define SEI_MODBUS_SEI_ZT1_ALARM_RESET_MASK 0X0001 #define SEI_MODBUS_SEI_ZT2_ALARM_RESET_MASK 0X0002 diff --git a/sources/Modbus/ModbusSEIMgr.cpp b/sources/Modbus/ModbusSEIMgr.cpp index d90a79a..49004d1 100644 --- a/sources/Modbus/ModbusSEIMgr.cpp +++ b/sources/Modbus/ModbusSEIMgr.cpp @@ -25,9 +25,18 @@ CModbusSEIMgr::CModbusSEIMgr(CModbusRepository *SEIRepo, CModbusRepository *CCRe mSEIModbusUpdateTimer = new QTimer(); mSEIModbusUpdateTimer->setInterval(1000); - mSEIModbusUpdateTimer->setSingleShot(false); + mSEIModbusUpdateTimer->setSingleShot(true); mSEIModbusUpdateTimer->stop(); + mSEIWatchdogTimer = new QTimer(); + mSEIWatchdogTimer->setInterval(SEI_MODBUS_WATCHDOG_TIMEOUT); + mSEIWatchdogTimer->setSingleShot(true); + mSEIWatchdogTimer->stop(); + + + mSEILinkState = false; + mSEIWatchdogState = 0; + mModbusTCPSocketHandle = new QTcpSocket(); connect(mModbusTCPSocketHandle,SIGNAL(readyRead()),this,SLOT(ModbusDataReady())); @@ -35,6 +44,7 @@ CModbusSEIMgr::CModbusSEIMgr(CModbusRepository *SEIRepo, CModbusRepository *CCRe connect(mModbusTCPSocketHandle,SIGNAL(connected()),this,SLOT(SocketConnected())); connect(mConnectionTimer,SIGNAL(timeout()),this,SLOT(ConnectionTimerExpired())); connect(mSEIModbusUpdateTimer,SIGNAL(timeout()),this,SLOT(SEIModbusUpdateTimerExpired())); + connect(mSEIWatchdogTimer,SIGNAL(timeout()),this,SLOT(SEIModbusWatchdogtimerExpired())); } CModbusSEIMgr::~CModbusSEIMgr() @@ -50,6 +60,8 @@ CModbusSEIMgr::~CModbusSEIMgr() delete mConnectionTimer; if(mSEIModbusUpdateTimer) delete mSEIModbusUpdateTimer; + if(mSEIWatchdogTimer) + delete mSEIWatchdogTimer; } int CModbusSEIMgr::StartSEICommunication() @@ -85,16 +97,18 @@ void CModbusSEIMgr::SocketConnected() CZTLog::instance()->AddLogString("Connection Modbus (Ethernet) avec NetTrac établi",true); mConnectionTimer->stop(); mSEIModbusUpdateTimer->start(); + mSEIWatchdogTimer->start(SEI_MODBUS_WATCHDOG_TIMEOUT*2); //allow twice the time for the first watchdog to come in } void CModbusSEIMgr::SocketDisconnected() { ModbusLinkDisconnected(); emit ModbusMasterDisconnected(); + emit SEIModbusLinkLost(); mConnectionTimer->start(); mSEIModbusUpdateTimer->stop(); - // qDebug("Disconnected from NetTrac"); - // CZTLog::instance()->AddLogString("Connexion avec NetTrac établi",true); + mSEILinkState = false; + mSEIWatchdogState = 0xBEEF; CZTLog::instance()->AddLogString("Connection Modbus (Ethernet) avec NetTrac rompue",true); } @@ -133,6 +147,13 @@ int CModbusSEIMgr::SEISettingsChanged(QHostAddress ServerIP, int ModbusPort, int } void CModbusSEIMgr::SEIModbusUpdateTimerExpired() +{ + + //Read the SEI data... + SendReadHoldingRegistersRequest(SEI_MODBUS_SEI_DATA_BASE_REG,SEI_MODBUS_SEI_TABLE_DATA_SIZE); +} + +int CModbusSEIMgr::SendZTRegistersToSEI() { //The SEI Modbus table is updated on event, triggered by the TKTransport object (see ZTTKUpdated()). //Just update the watchdog and send the current table. @@ -149,6 +170,7 @@ void CModbusSEIMgr::SEIModbusUpdateTimerExpired() SendWriteHoldingRegistersRequest(SEI_MODBUS_ZT_DATA_BASE_REG,SEI_MODBUS_ZT_TABLE_DATA_SIZE); + return RET_OK; } @@ -175,11 +197,42 @@ void CModbusSEIMgr::ZTTKUpdated() } +void CModbusSEIMgr::SEIModbusWatchdogtimerExpired() +{ + CZTLog::instance()->AddLogString("Perte du lien de communication avec la NetTrac: Watchdog NetTrac Expiré",true); + + emit SEIModbusLinkLost(); + mSEILinkState = false; + // mSEIWatchdogState = 0; +} + void CModbusSEIMgr::RegistersDatabaseUpdated(quint16 StartAddress, quint16 Length) { Q_UNUSED(StartAddress) Q_UNUSED(Length) + qint16 WD = mModbusRepo->GetSingleReg(SEI_MODBUS_SEI_WATCHDOG_REG); + WD = (WD & SEI_MODBUS_SEI_WATCHDOG_MASK); +// qDebug("WD = %d",WD); + if(WD != mSEIWatchdogState) + { + mSEIWatchdogTimer->start(SEI_MODBUS_WATCHDOG_TIMEOUT); //The watchdog has toggled. Kick the timer. + mSEIWatchdogState = WD; + if(mSEILinkState == false) + { + mSEILinkState = true; + emit SEIModbusLinkRecovered(); + CZTLog::instance()->AddLogString("Lien de communication (Watchdog) avec NetTrac rétabli",true); + } + } + + if(mSEILinkState == true) //only consider data when link is healthy + { + + } + + SendZTRegistersToSEI(); + emit ModbusMasterRepositoryUpdated(); } @@ -187,3 +240,28 @@ void CModbusSEIMgr::ModbusResponseException(quint8 ExceptionCode, quint8 FctCode { qDebug("Modbus MASTER exception: code:%d Fct:%d",ExceptionCode,FctCode); } + +void CModbusSEIMgr::ModbusWriteMultipleRegsSuccess() +{ + if(mModbusTCPSocketHandle->state() == QAbstractSocket::ConnectedState) + { + mSEIModbusUpdateTimer->start(); + } +} + +void CModbusSEIMgr::ModbusWriteSingleRegsSuccess() +{ + if(mModbusTCPSocketHandle->state() == QAbstractSocket::ConnectedState) + { + mSEIModbusUpdateTimer->start(); + } +} + +void CModbusSEIMgr::ModbusRequestMaxRetryReached() +{ + //The server did not respond to last request... Retry... + if(mModbusTCPSocketHandle->state() == QAbstractSocket::ConnectedState) + { + mSEIModbusUpdateTimer->start(); + } +} diff --git a/sources/Modbus/ModbusSEIMgr.h b/sources/Modbus/ModbusSEIMgr.h index fc8a3c7..768ceb0 100644 --- a/sources/Modbus/ModbusSEIMgr.h +++ b/sources/Modbus/ModbusSEIMgr.h @@ -8,7 +8,7 @@ #include #include - +#define SEI_MODBUS_WATCHDOG_TIMEOUT 7000 //ms class CModbusSEIMgr : public CModbusBackend @@ -23,13 +23,17 @@ public: int ReadModbusRegisters(); int StartSEICommunication(); int SEISettingsChanged(QHostAddress ServerIP, int ModbusPort, int DevID); + int SendZTRegistersToSEI(); virtual void RegistersDatabaseUpdated(quint16 StartAddress, quint16 Length); virtual void ModbusResponseException(quint8 ExceptionCode, quint8 FctCode); + virtual void ModbusWriteMultipleRegsSuccess(); + virtual void ModbusWriteSingleRegsSuccess(); + virtual void ModbusRequestMaxRetryReached(); QTimer *mConnectionTimer; -// QTimer *mSEIWatchdogTimer; + QTimer *mSEIWatchdogTimer; QTimer *mSEIModbusUpdateTimer; private: @@ -37,11 +41,19 @@ private: QHostAddress mSEIIPAddress; CModbusRepository *mCCRepoHandle; bool mZTWatchdog; + bool mSEILinkState; + + int mSEIWatchdogState; + + + signals: void ModbusMasterConnected(qint32 LocalIP, qint32 RemoteIP); void ModbusMasterDisconnected(); void ModbusMasterRepositoryUpdated(); + void SEIModbusLinkLost(); + void SEIModbusLinkRecovered(); public slots: void SocketConnected(); @@ -49,6 +61,8 @@ public slots: void ConnectionTimerExpired(); void SEIModbusUpdateTimerExpired(); void ZTTKUpdated(); + void SEIModbusWatchdogtimerExpired(); + }; #endif // CMODBUSSEIMGR_H diff --git a/sources/Zonetest.cpp b/sources/Zonetest.cpp index 715581a..e17cd8f 100644 --- a/sources/Zonetest.cpp +++ b/sources/Zonetest.cpp @@ -606,6 +606,8 @@ unsigned int CZoneTest::InitZT() connect(mModbusSEIMgr,SIGNAL(ModbusMasterConnected(qint32,qint32)),panel.mSEISettingsPage,SLOT(ModbusSEIConnected(qint32,qint32))); connect(mModbusSEIMgr,SIGNAL(ModbusMasterDisconnected()),panel.mSEISettingsPage,SLOT(ModbusSEIDisconnected())); + connect(mModbusSEIMgr,SIGNAL(SEIModbusLinkLost()),panel.mSEISettingsPage,SLOT(ModbusSEILinkDown())); + connect(mModbusSEIMgr,SIGNAL(SEIModbusLinkRecovered()),panel.mSEISettingsPage,SLOT(ModbusSEILinkUP())); panel.mZTMainPage->ModbusSEIDisconnected();