/*******************************************************************************
*                                                                              *
* Société de Transports de Montréal.                                    	   *
* 2012 - 2013                                                                        *
*                                                                              *
* Projet Zones Tests                                                           *
*                                                                              *
*                                                                              *
*                                                                              *
*******************************************************************************/
/*
    Description:
    Classe qui implémente le protocole Modbus.

*/

/* ************************************************************************** */
/* Revision:
### YYYMMDD JFM
    Verision d'origine.

### YYYYMMDD Description du besoin ou du bug
    Description du changement.
 */

/* ************************************************************************** */


#include "ModbusBackend.h"
#include "QBuffer"
#include <QDataStream>
//#include "EngLog.h"
//#include "ZTLog.h"

CModbusBackend::CModbusBackend(CModbusRepository *Repo)
{
    mModbusTCPSocketHandle = 0;
    mDataLinkValid = false;
    mModbusRepo = Repo;
    mModbusMode = MODBUS_INVALID_MODE;
    mTransactionIDCounter = 0;
    mDeviceID = 9;//0xFF;

    mModbusMaxRetry = MODBUS_RETRY_MAX_COUNT;
    mModbusRequestTimeout = MODBUS_RETRY_DELAY;
}

CModbusBackend::~CModbusBackend()
{
    for(int i= 0; i < mRequestsList.size(); i++)
    {
        delete mRequestsList[i];
    }
    mRequestsList.clear();
}

void CModbusBackend::ModbusDataReady()
{

    CModbusTransaction Transaction;
    QByteArray InData = mModbusTCPSocketHandle->readAll();

    QBuffer FileBuffer(&InData);
    FileBuffer.open(QIODevice::ReadOnly);
    FileBuffer.seek(0);
    QDataStream *TransactionDataStrm = new QDataStream(&FileBuffer);
    *TransactionDataStrm >>  Transaction.mHeader;
    *TransactionDataStrm >> Transaction.mPDU.mFunctionCode;
    Transaction.mPDU.mData = InData.right(Transaction.mHeader.mMessageLength - 2);    //-2 to remove Device ID and Function Code.
  //

//    qDebug("---------------------------------------");
//    qDebug("modbus data received %s",InData.toHex().data());
//    qDebug("Transaction ID 0x%X",Transaction.mHeader.mTransactionID);
//    qDebug("Message Length %d",Transaction.mHeader.mMessageLength);
//    qDebug("Protocol ID 0x%X",Transaction.mHeader.mProtocolID);
//    qDebug("Unit ID 0x%X",Transaction.mHeader.mUnitID);
//    qDebug("Function Code 0x%X",Transaction.mPDU.mFunctionCode);
//    qDebug("Data %s",Transaction.mPDU.mData.toHex().data());
//    qDebug("---------------------------------------");

    if(mModbusMode == MODBUS_MASTER_MODE)
    {
        AnalyzeModbusResponse(Transaction); //Le maître recoit des réponses
    }
    else if( mModbusMode == MODBUS_SLAVE_MODE)
    {
        AnalyzeModbusRequest(Transaction);  //L'esclave recoit des requêtes
    }
    else
    {
      //  CEngLog::instance()->AddLogString("Erreur Modbus: <Illegal modbus backend mode...>");   //Erreur de logique interne.  Ne doit pas survenir normalement
    }

    delete TransactionDataStrm;
}

void CModbusBackend::ModbusLinkDisconnected()
{
//    qDebug("Modbus link disconnected");
    mDataLinkValid = false;
    mModbusTCPSocketHandle->flush();
}


//In client mode.  This is the request from the master.
int CModbusBackend::AnalyzeModbusRequest(CModbusTransaction Transaction)
{
    if(Transaction.mHeader.mProtocolID != 0)
    {
        //Invalid protocol... what can we do?
        return 0;
    }

    switch(Transaction.mPDU.mFunctionCode)
    {
        case MODBUS_FCT_READ_HOLDING_REGISTERS:
        {
            bool ok = true;
            unsigned short StartAdress = 0;
            StartAdress = Transaction.mPDU.mData[0]&0xFF;
            StartAdress <<= 8;
            StartAdress += Transaction.mPDU.mData[1]&0xFF;
            unsigned short NbRegisters = 0;
            NbRegisters = Transaction.mPDU.mData[2]&0xFF;
            NbRegisters <<= 8;
            NbRegisters += Transaction.mPDU.mData[3]&0xFF;

            //Validate nb of registers
            if(NbRegisters < 1 || NbRegisters > MODBUS_MAX_NB_REGISTERS)
            {
                SendErrorResponse(Transaction,MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE);
                ModbusRequestException(MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,MODBUS_FCT_READ_HOLDING_REGISTERS);  //Fonction à être implémentée par le parent
                return 0;
            }

            //Validate data range
            if(!mModbusRepo->IsHRValid(StartAdress,NbRegisters))
            {
                qDebug("Reg invalid");
                //Send negative response
                SendErrorResponse(Transaction,MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
                ModbusRequestException(MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS,MODBUS_FCT_READ_HOLDING_REGISTERS);
                return 0;
            }

            QByteArray data = mModbusRepo->GetHRData(StartAdress,NbRegisters,&ok);

   //         qDebug("Slave Rx Read Holding Registers.  Address: %d, Nb Reg: %d",StartAdress, NbRegisters);
   //         qDebug("Data: %s",data.toHex().data());

            //The response to a HR reading needs the byte count before the data.
            quint8 ByteCount = data.size();
            data.prepend(ByteCount);
            SendModbusResponse(Transaction, data);

            //All OK

            break;
        }
        case MODBUS_WRITE_SINGLE_REGISTER:
        {
            unsigned short StartAdress = 0;

            StartAdress = Transaction.mPDU.mData[0]&0xFF;
            StartAdress <<= 8;
            StartAdress += Transaction.mPDU.mData[1]&0xFF;

            //Validate data range
            if(!mModbusRepo->IsHRValid(StartAdress,1))
            {
                qDebug("Reg invalid");
                //Send negative response
                SendErrorResponse(Transaction,MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
                ModbusRequestException(MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS,MODBUS_WRITE_SINGLE_REGISTER);
                return 0;
            }

            //Extract data.
            QByteArray data = Transaction.mPDU.mData.right(2);

            //Write register data
            mModbusRepo->WriteHRData(StartAdress,1,data);
//            if(StartAdress == 2000)
//            {
//                qDebug("debug... 2000");
//            }

//            qDebug("Slave Rx Write Single Register.  Address: %d, Value: 0x%s",StartAdress, data.toHex().data());
//            qDebug("Data: %s",data.toHex().data());

            data = Transaction.mPDU.mData.left(4);   //The response corresponds to the Reg. Address & the value.  Which is the first 4 bytes of the initial request.
            SendModbusResponse(Transaction, data);
            RegistersDatabaseUpdated(StartAdress,1);

            break;
        }
        case MODBUS_FCT_WRITE_MULTIPLE_REGISTERS:
        {
            unsigned short StartAdress = 0;
            StartAdress = Transaction.mPDU.mData[0]&0xFF;
            StartAdress <<= 8;
            StartAdress += Transaction.mPDU.mData[1]&0xFF;

            unsigned short NbRegisters = 0;
            NbRegisters = Transaction.mPDU.mData[2]&0xFF;
            NbRegisters <<= 8;
            NbRegisters += Transaction.mPDU.mData[3]&0xFF;

            quint8 ByteCount = Transaction.mPDU.mData[4];

            //Validate nb of registers
            if(NbRegisters < 1 || NbRegisters > 0x7D || ByteCount != (NbRegisters*2))
            {
                qDebug("Invalid register number or byte count ");
                SendErrorResponse(Transaction,MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE);
                ModbusRequestException(MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,MODBUS_FCT_WRITE_MULTIPLE_REGISTERS);
                return 0;
            }

            //Validate data range
            if(!mModbusRepo->IsHRValid(StartAdress,NbRegisters))
            {
                qDebug("Reg invalid");
                //Send negative response
                SendErrorResponse(Transaction,MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
                ModbusRequestException(MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS,MODBUS_FCT_WRITE_MULTIPLE_REGISTERS);
                return 0;
            }

            //Extract data.
            QByteArray data = Transaction.mPDU.mData.right(ByteCount);

            //Write register data
            mModbusRepo->WriteHRData(StartAdress,NbRegisters,data);

 //           qDebug("\nSlave Rx Write Multiple Registers.  Address: %d, Nb Reg: %d",StartAdress, NbRegisters);
   //         qDebug("Data: %s\n",data.toHex().data());

            data = Transaction.mPDU.mData.left(4);   //The response corresponds to the Start Adress and Nb of Regs.  Which is the first 4 bytes of the initial request.
            SendModbusResponse(Transaction, data);

            RegistersDatabaseUpdated(StartAdress,NbRegisters);

            break;
        }
        default:
        {
            //Received "Illegal function code".  Send the exception code to master
            //TODO: Log this.
            qDebug("Slave received illegal function code from master:  0x%x",Transaction.mPDU.mFunctionCode);
            SendErrorResponse(Transaction,MODBUS_EXCEPTION_ILLEGAL_FCT);
            ModbusRequestException(MODBUS_EXCEPTION_ILLEGAL_FCT,Transaction.mPDU.mFunctionCode);
            break;
        }
    }


    return 1;
}

int CModbusBackend::SendModbusResponse(CModbusTransaction RequestTransaction, QByteArray Data)
{
    QByteArray ModbusPacket;

    QBuffer Buffer(&ModbusPacket);
    Buffer.open(QIODevice::WriteOnly|QIODevice::Unbuffered);
    Buffer.seek(0);
    QDataStream *PacketDataStrm = new QDataStream(&Buffer);

    //For a response, the header will be the same as the original request, except for the msg. length.
    //Set the appropriate msg length.
    RequestTransaction.mHeader.mMessageLength = Data.size() + 2;    //+2 to add function code & Unit ID.
    RequestTransaction.mPDU.mData = Data;

    *PacketDataStrm << RequestTransaction.mHeader;
    *PacketDataStrm << RequestTransaction.mPDU.mFunctionCode;
    Buffer.close();
    ModbusPacket.append(Data);


 //   qDebug("Response packet: %s",ModbusPacket.toHex().data());

    mModbusTCPSocketHandle->write(ModbusPacket);

    delete PacketDataStrm;

    return RET_OK;
}

//In Master mode.  This is the response from slave to a previously sent request.
int CModbusBackend::AnalyzeModbusResponse(CModbusTransaction Transaction)
{
    if(Transaction.mHeader.mProtocolID != 0)
    {
        //Invalid protocol... what can we do?
        return RET_ERROR;
    }

    //Find matching request and remove it from the queue...
    CModbusRequest *Request;
    bool Found = false;
    for(int i = 0; i < mRequestsList.size(); i++)
    {
        if(mRequestsList.at(i)->mHeader.mTransactionID == Transaction.mHeader.mTransactionID)
        {
            Request = mRequestsList.takeAt(i);  //Remove from queue and keep a copy
            Request->mRequestTimer->stop();  //Stop the resend timer
            Found = true;
        }
    }

    if(Found == false)
    {
        //Invalid request number.  This should happen only if a very long delay exists in the comm.
        //TODO: Log this...
        qDebug("Master received response to a non existent request!!!");
        return RET_ERROR;
    }

    //check if we have an exception response
    if((Transaction.mPDU.mFunctionCode & MODBUS_EXCEPTION_FCT_MASK) != 0)
    {
        //we have an exception response...  something went wrong.
        quint8 ExceptionCode = Transaction.mPDU.mData[0];

        //TODO: Manage this!
        qDebug("Master Rx exception code %d to request %d",ExceptionCode,Request->mPDU.mFunctionCode);
        emit ModbusResponseException(ExceptionCode,Request->mPDU.mFunctionCode);
        delete Request;
        return RET_ERROR;
    }

    switch(Transaction.mPDU.mFunctionCode)
    {
        case MODBUS_FCT_READ_HOLDING_REGISTERS:
        {
            quint8 ByteCount = 0;
            ByteCount = Transaction.mPDU.mData.at(0);

            if((Request->mNbRegisters*2) != ByteCount)
            {
                //Inconsistency between the data range and the data count.
                //TODO: Log the error.
                qDebug("Master eceived a wrong data size in response for a MODBUS_FCT_READ_HOLDING_REGISTERS request");
                emit ModbusResponseException(MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,MODBUS_FCT_READ_HOLDING_REGISTERS);
                delete Request;
                return RET_ERROR;
            }

            QByteArray RegisterValues = Transaction.mPDU.mData.right(ByteCount);


//            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);

            break;
        }
        case MODBUS_WRITE_SINGLE_REGISTER:
        {
            quint16 RegAddress = 0;
            RegAddress = Transaction.mPDU.mData[0]&0xFF;
            RegAddress <<= 8;
            RegAddress += Transaction.mPDU.mData[1]&0xFF;

            if(Request->mStartAddress != RegAddress)
            {
                //Inconsistency between the request Adress and response Adress.
                //TODO: Log the error.
                qDebug("Master  received a wrong Register Adress in response for a MODBUS_WRITE_SINGLE_REGISTER request");
                emit ModbusResponseException(MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,MODBUS_WRITE_SINGLE_REGISTER);
                delete Request;
                return RET_ERROR;
            }
//            qDebug("Master Rx Write Single Register response.  Address: %d,",RegAddress);
//            qDebug("Data: %s",Transaction.mPDU.mData.toHex().data());

            ModbusWriteSingleRegsSuccess();

            //Everything seems good.

            break;
        }
        case MODBUS_FCT_WRITE_MULTIPLE_REGISTERS:
        {
            unsigned short StartAdress = 0;
            StartAdress = Transaction.mPDU.mData[0]&0xFF;
            StartAdress <<= 8;
            StartAdress += Transaction.mPDU.mData[1]&0xFF;

            unsigned short NbRegisters = 0;
            NbRegisters = Transaction.mPDU.mData[2]&0xFF;
            NbRegisters <<= 8;
            NbRegisters += Transaction.mPDU.mData[3]&0xFF;

            if(StartAdress != Request->mStartAddress || NbRegisters != Request->mNbRegisters)
            {
                //Inconsistency between the request Adress or NbRegisters and response.
                //TODO: Log the error.
                qDebug("Master Received a wrong Register Adress or NbRegisters in response for a MODBUS_FCT_WRITE_MULTIPLE_REGISTERS request");
                emit ModbusResponseException(MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE,MODBUS_FCT_WRITE_MULTIPLE_REGISTERS);
                delete Request;
                return RET_ERROR;
            }

//            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.

            break;
        }
        default:
        {
            //Received "Illegal function code" response
            //TODO: Log this.
            //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;
        }
    }


    delete Request;
    return 1;
}

int CModbusBackend::SendModbusRequest(CModbusRequest *Request)
{
    QByteArray ModbusPacket;

    QBuffer Buffer(&ModbusPacket);
    Buffer.open(QIODevice::WriteOnly|QIODevice::Unbuffered);
    Buffer.seek(0);
    QDataStream *PacketDataStrm = new QDataStream(&Buffer);

    *PacketDataStrm << Request->mHeader;
    *PacketDataStrm << Request->mPDU.mFunctionCode;
    Buffer.close();
    ModbusPacket.append(Request->mPDU.mData);


//    qDebug("Request packet: %s",ModbusPacket.toHex().data());

    mModbusTCPSocketHandle->write(ModbusPacket);
    delete PacketDataStrm;
    return RET_OK;
}

int CModbusBackend::SendErrorResponse(CModbusTransaction RequestTransaction, quint8 ErrorCode)
{
    QByteArray ModbusPacket;

    QBuffer Buffer(&ModbusPacket);
    Buffer.open(QIODevice::WriteOnly|QIODevice::Unbuffered);
    Buffer.seek(0);
    QDataStream *PacketDataStrm = new QDataStream(&Buffer);

    //For a response, the header will be the same as the original request, except for the msg. length.
    //Set the appropriate msg length.
    RequestTransaction.mHeader.mMessageLength = 3;    //Unit ID, function code & Exception code.

    *PacketDataStrm << RequestTransaction.mHeader;
    Buffer.close();
    ModbusPacket.append(RequestTransaction.mPDU.mFunctionCode + 0x80);
    ModbusPacket.append(ErrorCode);



    //CEngLog::instance()->AddLogString(QString("Modbus: Envoi d'un code d'un code d'erreur.  Error Code %1, Error Packet: %2").arg(ErrorCode).arg(ModbusPacket.toHex().data()));
   // qDebug("Sending error code %d.  Error packet: %s",ErrorCode,ModbusPacket.toHex().data());

    mModbusTCPSocketHandle->write(ModbusPacket);

    delete PacketDataStrm;

    return RET_OK;
}


int CModbusBackend::SendReadHoldingRegistersRequest(quint16 StartAddress, quint16 RegisterCount)
{
    //First, validate that the reading range is within our repo
    if(mModbusRepo->IsHRValid(StartAddress,RegisterCount) == false)
    {
        //CEngLog::instance()->AddLogString("ModbusBackend: Tentative de lecture d'un registre hors de portée.");
        return RET_ERROR;
    }

    //Create a request.
    CModbusRequest *NewRequest = new CModbusRequest;
    NewRequest->mStartAddress = StartAddress;
    NewRequest->mNbRegisters = RegisterCount;
    connect(NewRequest->mRequestTimer,SIGNAL(timeout()),this,SLOT(RequestTimerExpired()));

    NewRequest->mPDU.mData.clear();
    NewRequest->mPDU.mFunctionCode = MODBUS_FCT_READ_HOLDING_REGISTERS;

    quint8 HighByte, LowByte;
    LowByte = StartAddress & 0x00FF;
    HighByte = (StartAddress >> 8) & 0x00FF;
    NewRequest->mPDU.mData.append(HighByte);
    NewRequest->mPDU.mData.append(LowByte);

    LowByte = RegisterCount & 0x00FF;
    HighByte = (RegisterCount >> 8) & 0x00FF;
    NewRequest->mPDU.mData.append(HighByte);
    NewRequest->mPDU.mData.append(LowByte);

    NewRequest->mHeader.mMessageLength = NewRequest->mPDU.mData.size() + 2;
    NewRequest->mHeader.mProtocolID = 0;
    NewRequest->mHeader.mTransactionID = (qint16)GetNewTransactionID();
    NewRequest->mHeader.mUnitID = mDeviceID;

    mRequestsList.append(NewRequest);

    SendModbusRequest(NewRequest);

    NewRequest->mRequestTimer->start(mModbusRequestTimeout);


    return RET_OK;

}

int CModbusBackend::SendWriteHoldingRegistersRequest(quint16 StartAddress, quint16 RegisterCount)
{
    //First, validate that the reading range is within our repo
    if(mModbusRepo->IsHRValid(StartAddress,RegisterCount) == false)
    {
        //CEngLog::instance()->AddLogString("Trying to send a Write HR in an invalid range SendWriteHoldingRegistersRequest()");
        return RET_ERROR;
    }

    if(RegisterCount > MODBUS_MAX_NB_REGISTERS)
    {
        return RET_ERROR;
    }

    //Get data.
    bool OK;
    QByteArray RegData = mModbusRepo->GetHRData(StartAddress,RegisterCount,&OK);
    if(OK == false)
    {
        return RET_ERROR;
    }

    //Create a request.
    CModbusRequest *NewRequest = new CModbusRequest;
    NewRequest->mStartAddress = StartAddress;
    NewRequest->mNbRegisters = RegisterCount;
    connect(NewRequest->mRequestTimer,SIGNAL(timeout()),this,SLOT(RequestTimerExpired()));

    NewRequest->mPDU.mData.clear();
    NewRequest->mPDU.mFunctionCode = MODBUS_FCT_WRITE_MULTIPLE_REGISTERS;

    //Start address
    quint8 HighByte, LowByte;
    LowByte = StartAddress & 0x00FF;
    HighByte = (StartAddress >> 8) & 0x00FF;
    NewRequest->mPDU.mData.append(HighByte);
    NewRequest->mPDU.mData.append(LowByte);

    //Nb Registers
    LowByte = RegisterCount & 0x00FF;
    HighByte = (RegisterCount >> 8) & 0x00FF;
    NewRequest->mPDU.mData.append(HighByte);
    NewRequest->mPDU.mData.append(LowByte);

    //Byte Count
    NewRequest->mPDU.mData.append(RegData.size());

    NewRequest->mPDU.mData.append(RegData);

    NewRequest->mHeader.mMessageLength = NewRequest->mPDU.mData.size() + 2;
    NewRequest->mHeader.mProtocolID = 0;
    NewRequest->mHeader.mTransactionID = (qint16)GetNewTransactionID();
    NewRequest->mHeader.mUnitID = mDeviceID;

    mRequestsList.append(NewRequest);

    SendModbusRequest(NewRequest);

    NewRequest->mRequestTimer->start(mModbusRequestTimeout);


    return RET_OK;
}

int CModbusBackend::SendWriteSingleRegisterRequest(quint16 Address)
{
    //First, validate that the reading range is within our repo
    if(mModbusRepo->IsHRValid(Address,1) == false)
    {
        //CEngLog::instance()->AddLogString("Trying to send a Write HR in an invalid range SendWriteSingleRegisterRequest()");
        return RET_ERROR;
    }

    //Get data.
    bool OK;
    QByteArray RegData = mModbusRepo->GetHRData(Address,1,&OK);
    if(OK == false)
    {
        return RET_ERROR;
    }

    //Create a request.
    CModbusRequest *NewRequest = new CModbusRequest;
    NewRequest->mStartAddress = Address;
    NewRequest->mNbRegisters = 1;
    connect(NewRequest->mRequestTimer,SIGNAL(timeout()),this,SLOT(RequestTimerExpired()));

    NewRequest->mPDU.mData.clear();
    NewRequest->mPDU.mFunctionCode = MODBUS_WRITE_SINGLE_REGISTER;

    quint8 HighByte, LowByte;
    LowByte = Address & 0x00FF;
    HighByte = (Address >> 8) & 0x00FF;
    NewRequest->mPDU.mData.append(HighByte);
    NewRequest->mPDU.mData.append(LowByte);

    NewRequest->mPDU.mData.append(RegData);

    NewRequest->mHeader.mMessageLength = NewRequest->mPDU.mData.size() + 2;
    NewRequest->mHeader.mProtocolID = 0;
    NewRequest->mHeader.mTransactionID = (qint16)GetNewTransactionID();
    NewRequest->mHeader.mUnitID = mDeviceID;

    mRequestsList.append(NewRequest);

    SendModbusRequest(NewRequest);

    NewRequest->mRequestTimer->start(mModbusRequestTimeout);


    return RET_OK;
}

void CModbusBackend::RequestTimerExpired()
{
    //find the expired request
    for(int i = 0; i < mRequestsList.size(); i++)
    {
        if(mRequestsList.at(i)->mRequestTimer->isActive() == false)
        {
            if(mRequestsList.at(i)->mRetries >= mModbusMaxRetry)
            {
                //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);


                delete mRequestsList[i];
                mRequestsList.removeAt(i);
                ModbusRequestMaxRetryReached();

                //TODO: Manage this situation (log?)

                return;
            }
            else
            {
                SendModbusRequest(mRequestsList[i]);
                mRequestsList.at(i)->mRequestTimer->start(mModbusRequestTimeout);
                mRequestsList[i]->mRetries++;
            }
        }
    }
}

quint16 CModbusBackend::GetNewTransactionID()
{
    quint16 ID = mTransactionIDCounter++;

    if(mTransactionIDCounter == 0xFFFF - 10)
    {
        mTransactionIDCounter = 0;
    }

    return ID;
}



CModbusRequest::CModbusRequest():
    mRetries(0)
{
    mRequestTimer = new QTimer;
    mRequestTimer->setSingleShot(true);
}
CModbusRequest::~CModbusRequest()
{
    delete mRequestTimer;
}





QDataStream &operator<<(QDataStream &out, const CModbusHeader &source)
{
    out << source.mTransactionID
        << source.mProtocolID
        << source.mMessageLength
        << source.mUnitID
           ;

    return out;
}

QDataStream &operator>>(QDataStream &in, CModbusHeader &dest)
{
        in >> dest.mTransactionID
           >> dest.mProtocolID
           >> dest.mMessageLength
           >> dest.mUnitID
              ;

        return in;
}

//Virtual function that should not even get called...
void CModbusBackend::ModbusResponseException(quint8 ExceptionCode, quint8 FctCode)
{
    Q_UNUSED(ExceptionCode)
    Q_UNUSED(FctCode)
    //CEngLog::instance()->AddLogString("ModbusResponseException called from within slave object... weird stuff!");
}

//Virtual function that should not even get called...
void CModbusBackend::ModbusRequestException(quint8 ExceptionCode, quint8 FctCode)
{
    Q_UNUSED(ExceptionCode)
    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");
}
