#include "ModbusSEIMgr.h" #include #include "ModbusCCDefs.h" #include "ModbusSEIDefs.h" #include "GlobalDefine.h" #include CModbusSEIMgr::CModbusSEIMgr(CModbusRepository *SEIRepo, CModbusRepository *CCRepo, QHostAddress ServerIP, int ModbusPort, int DevID): CModbusBackend(SEIRepo) { // connect(mModbusTCPSocketHandle,SIGNAL(readyRead()),this,SLOT(ModbusDataReady())); mModbusMode = MODBUS_MASTER_MODE; mModbusPort = ModbusPort; mDeviceID = DevID; mSEIIPAddress = ServerIP; mCCRepoHandle = CCRepo; mZTWatchdog = false; mConnectionTimer = new QTimer(); mConnectionTimer->setInterval(SEI_CLIENT_RECONNECT_TIMEOUT); mConnectionTimer->setSingleShot(false); mConnectionTimer->stop(); mSEIModbusUpdateTimer = new QTimer(); mSEIModbusUpdateTimer->setInterval(1000); 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())); connect(mModbusTCPSocketHandle,SIGNAL(disconnected()),this,SLOT(SocketDisconnected())); 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() { if(mModbusTCPSocketHandle->state() != QAbstractSocket::UnconnectedState) { mModbusTCPSocketHandle->disconnectFromHost(); mModbusTCPSocketHandle->waitForDisconnected(1000); } delete mModbusTCPSocketHandle; if(mConnectionTimer) delete mConnectionTimer; if(mSEIModbusUpdateTimer) delete mSEIModbusUpdateTimer; if(mSEIWatchdogTimer) delete mSEIWatchdogTimer; } int CModbusSEIMgr::StartSEICommunication() { mConnectionTimer->start(); return RET_OK; } int CModbusSEIMgr::ConnectToSlave(QHostAddress SlaveIP, int SlavePort) { if(mModbusTCPSocketHandle->state() != QAbstractSocket::UnconnectedState) { return RET_ERROR; } mModbusTCPSocketHandle->connectToHost(SlaveIP,SlavePort); return RET_OK; } int CModbusSEIMgr::DisconnectFromSlave() { mModbusTCPSocketHandle->flush(); mModbusTCPSocketHandle->disconnectFromHost(); mConnectionTimer->start(); return 1; } void CModbusSEIMgr::SocketConnected() { emit ModbusMasterConnected(mModbusTCPSocketHandle->localAddress().toIPv4Address(),mModbusTCPSocketHandle->peerAddress().toIPv4Address()); // qDebug("Master: Connection established with NetTrac"); // CZTLog::instance()->AddLogString("Connection Modbus (Ethernet) avec NetTrac établi",true); CZTLog::instance()->AddLogString(QString("Connection Modbus (Ethernet) avec la NetTrac établie. IP locale [%1] - IP NetTrac [%2]").arg(mModbusTCPSocketHandle->localAddress().toString()).arg(mModbusTCPSocketHandle->peerAddress().toString()),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(); mSEILinkState = false; mSEIWatchdogState = 0xBEEF; CZTLog::instance()->AddLogString("Connection Modbus (Ethernet) avec NetTrac rompue",true); } int CModbusSEIMgr::ReadModbusRegisters() { //return SendReadHoldingRegistersRequest(ZT_DATA_BASE_REGISTER_ADDRESS,4); //Read all 3 registers from ZT (2000 - 2003) return RET_OK; } void CModbusSEIMgr::ConnectionTimerExpired() { //Establish connection only if not connected if(mModbusTCPSocketHandle->state() == QAbstractSocket::UnconnectedState) { ConnectToSlave(mSEIIPAddress,mModbusPort); } } int CModbusSEIMgr::SEISettingsChanged(QHostAddress ServerIP, int ModbusPort, int DevID) { if(DevID > 255) { return RET_ERROR; } mSEIIPAddress = ServerIP; mModbusPort = ModbusPort; mDeviceID = DevID; //Make shure we are disconnected. Connection timer will do the reconnection. DisconnectFromSlave(); return RET_OK; } 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. mZTWatchdog = !mZTWatchdog; if(mZTWatchdog) { mModbusRepo->WriteSingleReg(SEI_MODBUS_ZT_WATCHDOG_REG,0x01); } else { mModbusRepo->WriteSingleReg(SEI_MODBUS_ZT_WATCHDOG_REG,0); } SendWriteHoldingRegistersRequest(SEI_MODBUS_ZT_DATA_BASE_REG,SEI_MODBUS_ZT_TABLE_DATA_SIZE); return RET_OK; } //This signal is emitted by TKTransport when a changed happened in the TK after the CC modbus table has been updated. //We just need to copy the relevant info from the CC repo... //As per specification, we must also send an update on change to the SEI void CModbusSEIMgr::ZTTKUpdated() { //Copy data from CC modbus table to SEI modbus table. mModbusRepo->WriteHRData(SEI_MODBUS_ZT_DATA_BASE_REG,SEI_MODBUS_ZT_TABLE_DATA_SIZE,mCCRepoHandle->GetHRData(MODBUS_ZT_DATA_BASE_REG,SEI_MODBUS_ZT_TABLE_DATA_SIZE)); //Keep our actual watchdog value.. if(mZTWatchdog) { mModbusRepo->WriteSingleReg(SEI_MODBUS_ZT_WATCHDOG_REG,0x01); } else { mModbusRepo->WriteSingleReg(SEI_MODBUS_ZT_WATCHDOG_REG,0); } //Send the data now to NetTrac SendWriteHoldingRegistersRequest(SEI_MODBUS_ZT_DATA_BASE_REG,SEI_MODBUS_ZT_TABLE_DATA_SIZE); } void CModbusSEIMgr::SEIModbusWatchdogtimerExpired() { CZTLog::instance()->AddLogString("Perte du lien de communication avec la NetTrac: Watchdog NetTrac Expiré",true); emit SEIModbusLinkLost(); mSEILinkState = false; DisconnectFromSlave(); //JFM 2021-03-12. Ajout suite aux essais site. La connexion doit etre coupee manuellement pour les raisons obscures. // mSEIWatchdogState = 0; } void CModbusSEIMgr::RegistersDatabaseUpdated(quint16 StartAddress, quint16 Length) { Q_UNUSED(StartAddress) Q_UNUSED(Length) static bool FirstUpdate = true; qint16 WD = mModbusRepo->GetSingleReg(SEI_MODBUS_SEI_WATCHDOG_REG); WD = (WD & SEI_MODBUS_SEI_WATCHDOG_MASK); // qDebug("WD = %d",WD); if(FirstUpdate) { mSEIWatchdogState = WD; FirstUpdate = false; } else 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 { bool OK; qint16 AlarmsReg = mModbusRepo->GetSingleReg(SEI_MODBUS_SEI_ALARMS_RESET_REG,&OK); bool ZT1Clear, ZT2Clear; if(OK) { ZT1Clear = (AlarmsReg & SEI_MODBUS_SEI_ZT1_ALARM_RESET_MASK) != 0; ZT2Clear = (AlarmsReg & SEI_MODBUS_SEI_ZT2_ALARM_RESET_MASK) != 0; emit SEIAlarmClearUpdate(ZT1Clear,ZT2Clear); } else { CEngLog::instance()->AddLogString(qPrintable(QString("Erreur de logique dans ModbusSEIMgr::RegistersDatabaseUpdated, impossible de lire la table modbus à l'adresse %1").arg(SEI_MODBUS_SEI_ALARMS_RESET_REG))); } } SendZTRegistersToSEI(); } 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(); } }