760 lines
27 KiB
C++
760 lines
27 KiB
C++
/*******************************************************************************
|
|
* *
|
|
* 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 "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");
|
|
}
|
|
|
|
CEngLog::instance()->AddLogString(QString().sprintf("Slave Rx Write Single Register. Address: %d, Value: 0x%s",StartAdress, data.toHex().data()));
|
|
CEngLog::instance()->AddLogString(QString().sprintf("Data: %s\n",data.toHex().data()));
|
|
// 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);
|
|
|
|
CEngLog::instance()->AddLogString(QString().sprintf("Slave Rx Write Multiple Registers. Address: %d, Nb Reg: %d",StartAdress, NbRegisters));
|
|
CEngLog::instance()->AddLogString(QString().sprintf("Data: %s\n",data.toHex().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");
|
|
}
|