This commit is contained in:
2021-05-26 04:15:57 -04:00
parent 0ae56e062c
commit 75f1a4d1c3
74 changed files with 5086 additions and 42 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ int C232NetworkCommIF::NewFrameReceived(int DeviceID, int DeviceAddress, int Mes
// return 1;
//}
int C232NetworkCommIF::SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data)
int C232NetworkCommIF::SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray *Data)
{
Q_UNUSED(DeviceID)
Q_UNUSED(DeviceAddress)
+1 -1
View File
@@ -15,7 +15,7 @@ public:
int NewFrameReceived(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data);
//NetworkCommIF implementation
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data);
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray *Data);
private:
CNetworkDevice *mDeviceHandle;
+1 -1
View File
@@ -28,7 +28,7 @@ int C485NetworkCommIF::RegisterNewDevice(CNetworkDevice *NewDevice)
return 1;
}
int C485NetworkCommIF::SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data)
int C485NetworkCommIF::SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray *Data)
{
Q_UNUSED(DeviceID)
Q_UNUSED(DeviceAddress)
+1 -1
View File
@@ -16,7 +16,7 @@ public:
virtual int NewFrameReceived(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data);
//NetworkCommIF implementation
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data);
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray *Data);
private:
QList<CNetworkDevice *> mDevicesList;
+1 -1
View File
@@ -12,7 +12,7 @@ public:
CNetworkDevice *mDevicePtr;
CAbstractNetworkCommIF(){mDevicePtr = 0;}
virtual ~CAbstractNetworkCommIF() {}
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data) = 0;
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray *Data) = 0;
};
#endif // ABSTRACTNETWORKINTERFACE_H
+1 -1
View File
@@ -24,7 +24,7 @@ int CAvReceiverInterface::NewDeviceFrameReceived(int DeviceID, int DeviceAddress
case AV_RECEIVER_INTERFACE_GENERAL_STATUS_REQUEST:
{
QByteArray data = mAvReceiverDevice->GetReceiverStatus().ToByteArray();
mNetworkInterfacePtr->SendNetworkMessage(ID_AVRECEIVER_INTERFACE,mDeviceAddress,AV_RECEIVER_INTERFACE_GENERAL_STATUS_RESPONSE,data.size(),data);
mNetworkInterfacePtr->SendNetworkMessage(ID_AVRECEIVER_INTERFACE,mDeviceAddress,AV_RECEIVER_INTERFACE_GENERAL_STATUS_RESPONSE,data.size(),&data);
break;
}
+5
View File
@@ -11,6 +11,7 @@ CChaletMainStatus::CChaletMainStatus()
mIsOnline = false;
mHarakiriDone = false;
mStatusToggleBit = 0;
mLostRequestPercentage = 0;
}
@@ -52,6 +53,10 @@ QByteArray CChaletMainStatus::ToByteArray()
Strm << mBatterySOC;
Strm << mIsOnline;
Strm << mCurrentSensorStatus;
Strm << mLostRequestPercentage;
Strm << mThisStatusDateTime;
Strm << mLastLoraStatus;
Strm << mStatusToggleBit;
return Data;
}
+5
View File
@@ -30,6 +30,11 @@ public:
bool mStatusToggleBit;
QDateTime mLastLoraStatus;
QDateTime mThisStatusDateTime;
float mLostRequestPercentage;
};
+215 -5
View File
@@ -1,36 +1,231 @@
#include "ChaletDataLogger.h"
#include <QDateTime>
#include <QString>
#include <QStringList>
#include <QDataStream>
#include <QFileInfo>
CChaletDataLogger::CChaletDataLogger(QObject *parent) : QObject(parent)
{
mChaletLogFile = 0;
OpenTodaysFile();
mChaletNewLogFileTimer = new QTimer;
connect(mChaletNewLogFileTimer,SIGNAL(timeout()),this,SLOT(NewLogFileTimerExpired()));
mChaletNewLogFileTimer->setInterval(1000);
mChaletNewLogFileTimer->setSingleShot(false);
mChaletNewLogFileTimer->start();
}
int CChaletDataLogger::OpenTodaysFile()
{
mTodaysDate = QDateTime::currentDateTime().date();
bool create = false;
mChaletLogFile = new QFile(CHALET_LORA_DATA_LOG_FILENAME);
if(QFile::exists(CHALET_LORA_DATA_LOG_FILENAME) == false)
QString LogFileName(CHALET_LORA_DATA_LOG_DIR);
LogFileName.append("ChaletLora-");
LogFileName.append(mTodaysDate.toString("yyyy-MM-dd"));
LogFileName.append(".csv");
if(mChaletLogFile != 0)
{
if(mChaletLogFile->isOpen())
{
mChaletLogFile->close();
}
mChaletLogFile->setFileName(LogFileName);
}
else
{
mChaletLogFile = new QFile(LogFileName);
}
//Check if today's file already exist, otherwise create it...
if(QFile::exists(LogFileName) == false)
create = true;
if(mChaletLogFile->open(QIODevice::ReadWrite|QIODevice::Append/*|QIODevice::Text*/) == false)
if(mChaletLogFile->open(QIODevice::ReadWrite|QIODevice::Append|QIODevice::Unbuffered) == false)
{
delete mChaletLogFile;
mChaletLogFile = 0;
qDebug("Cannot open chalet LORA log file...");
return;
return RET_ERROR;
}
if(create)
{
qDebug("Creating new chalet LORA log file...");
QString Header("Date;Heure;Courant Batterie;Tension Batterie;Inverter;Wifi\n");
int bytes = mChaletLogFile->write(qPrintable(Header));
mChaletLogFile->write(qPrintable(Header));
mChaletLogFile->flush();
}
// mChaletLogFile->close();
qDebug("Chalet LORA log file created successfully");
return RET_OK;
}
QByteArray *CChaletDataLogger::GetChaletData(QDate StartDate)
{
if(StartDate > QDate::currentDate())
{
return 0;
}
QFile *LogFile =0;
bool done = false;
QByteArray WorkArray, *DataArray;
DataArray = new QByteArray;
int NbReadings = 0;
DataArray->clear();
while(!done)
{
if(StartDate > QDate::currentDate())
{
done = true;
break;
}
// else if(StartDate == QDate::currentDate())
// {
// LogFile = mChaletLogFile;
// }
else
{
QString LogFileName(CHALET_LORA_DATA_LOG_DIR);
LogFileName.append("ChaletLora-");
LogFileName.append(StartDate.toString("yyyy-MM-dd"));
LogFileName.append(".csv");
if(QFileInfo::exists(LogFileName))
{
LogFile = new QFile(LogFileName);
if(LogFile->open(QIODevice::ReadOnly|QIODevice::Unbuffered) == false)
{
delete LogFile;
LogFile = 0;
}
}
else
{
LogFile = 0;
}
}
// if(mChaletLogFile == 0)
// {
// return 0;
// }
// if(mChaletLogFile->isOpen() == false)
// {
// return 0;
// }
if(LogFile != 0)
{
LogFile->seek(0); //seek to the beginning of the log file
WorkArray = LogFile->readLine(); //read and discard the header line.
bool Finished = false;
while(!Finished)
{
WorkArray.clear();
WorkArray = LogFile->readLine();
if(WorkArray.isEmpty())
{
Finished = true;
break;
}
QStringList LineData = QString(WorkArray).remove("\n").split(QChar(';'));
if(LineData.size() == 6) //if we don't have 6 parameters, something went very wrong
{ //"Date;Heure;Courant Batterie;Tension Batterie;Inverter;Wifi\n");
QString date = LineData.at(0);
// QDate Date = QDate::fromString("yyyy-MM-dd",LineData.at(0));
QDate Date = QDate::currentDate();
Date = QDate::fromString(date,"yyyy-MM-dd");
QString time = LineData.at(1);
// QTime Time = QTime::fromString("hh:mm:ss",LineData.at(1));
QTime Time = QTime::currentTime();
Time= QTime::fromString(time,"hh:mm:ss");
QDateTime DateTime(Date,Time);
// qDebug("Date: %s",qPrintable(Date.toString("yyyy-MM-dd")));
// qDebug("Time: %s",qPrintable(Time.toString("hh:mm:ss")));
// qDebug("Date Time: %s",qPrintable(DateTime.toString("yyyy-MM-dd-hh:mm:ss")));
// time.setDate(QDate().fromString("yyyy-MM-dd",LineData.at(0)));
// time.setTime(QTime().fromString("hh:mm:ss",LineData.at(1)));
QString temp = LineData.at(2);
bool OK;
qint16 BattCurrent = temp.toInt(&OK);
temp = LineData.at(3);
float BattVoltage = temp.toFloat(&OK);
temp = LineData.at(4);
quint8 InverterStatus = 0;
if(temp == "ON")
{
InverterStatus = 1;
}
temp = LineData.at(5);
quint8 WiFiStatus = 0;
if(temp == "ON")
{
WiFiStatus = 1;
}
CChaletMainStatus CurBlob;
CurBlob.mThisStatusDateTime = DateTime;
CurBlob.mBatteryCurrent = BattCurrent;
CurBlob.mBatteryVoltage = BattVoltage;
CurBlob.mInverterRelayStatus = InverterStatus;
CurBlob.mWiFiModuleStatus = WiFiStatus;
DataArray->append(CurBlob.ToByteArray());
NbReadings++;
}
else
{
qDebug("Invalid nb. of parameters in today's data extraction");
}
}
}
if(LogFile != mChaletLogFile && LogFile != 0)
{
LogFile->close();
delete LogFile;
}
LogFile = 0;
StartDate = StartDate.addDays(1);
}
QByteArray Readings;
QDataStream strm(&Readings,QIODevice::WriteOnly);
strm.device()->seek(0);
strm << NbReadings;
DataArray->prepend(Readings);
return DataArray; //Don't forget to delete!!!
}
QByteArray *CChaletDataLogger::GetTodaysData()
{
return GetChaletData(QDate::currentDate());
}
CChaletDataLogger::~CChaletDataLogger()
{
if(mChaletLogFile)
@@ -38,6 +233,7 @@ CChaletDataLogger::~CChaletDataLogger()
mChaletLogFile->close();
delete mChaletLogFile;
}
}
bool CChaletDataLogger::LogChaletLORAData(CChaletMainStatus *Data)
@@ -82,7 +278,21 @@ bool CChaletDataLogger::LogChaletLORAData(CChaletMainStatus *Data)
mChaletLogFile->write(qPrintable(CSVData));
mChaletLogFile->flush();
// mChaletLogFile->close();
return true;
}
void CChaletDataLogger::NewLogFileTimerExpired()
{
if(QDateTime::currentDateTime().date() > mTodaysDate) //If it's midnight...
{
qDebug("It's midnight... creating today's file...");
OpenTodaysFile();
}
}
+15 -1
View File
@@ -4,9 +4,11 @@
#include <QObject>
#include <QFile>
#include "ChaletData.h"
#include <QTimer>
//#define CHALET_LORA_DATA_LOG_FILENAME "./ChaletLogs/ChaletLora.csv"
#define CHALET_LORA_DATA_LOG_FILENAME "D:/Main/Chalet/LoraLogs/ChaletLora.csv"
#define CHALET_LORA_DATA_LOG_DIR "D:/Main/Chalet/LoraLogs/"
class CChaletMainStatus;
@@ -18,13 +20,25 @@ public:
~CChaletDataLogger();
bool LogChaletLORAData(CChaletMainStatus *Data);
QByteArray *GetTodaysData();
QByteArray *GetChaletData(QDate StartDate);
QFile *mChaletLogFile;
QTimer *mChaletNewLogFileTimer;
QDate mTodaysDate;
private:
int OpenTodaysFile();
signals:
public slots:
void NewLogFileTimerExpired();
};
#endif // CHALETDATALOGGER_H
+28 -1
View File
@@ -1,9 +1,12 @@
#include "ChaletInterface.h"
#include "ChaletDataLogger.h"
CChaletInterface::CChaletInterface(int Address, CAbstractNetworkCommIF *NetworkInterface, CChaletLoraDevice *DevicePtr):
CNetworkDevice(ID_CHALET_INTERFACE,Address,NetworkInterface)
{
mChaletLoraDevice = DevicePtr;
mChaletDataLogger = mChaletLoraDevice->GetDataLoggerHandle();
}
@@ -24,7 +27,7 @@ int CChaletInterface::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, in
case CHALET_INTERFACE_GENERAL_STATUS_REQUEST:
{
QByteArray data = mChaletLoraDevice->GetChaletMainStatus().ToByteArray();
mNetworkInterfacePtr->SendNetworkMessage(ID_CHALET_INTERFACE,mDeviceAddress,CHALET_INTERFACE_GENERAL_STATUS_RESPONSE,data.size(),data);
mNetworkInterfacePtr->SendNetworkMessage(ID_CHALET_INTERFACE,mDeviceAddress,CHALET_INTERFACE_GENERAL_STATUS_RESPONSE,data.size(),&data);
break;
}
@@ -74,6 +77,25 @@ int CChaletInterface::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, in
mChaletLoraDevice->SendRebootCmd();
break;
}
case CHALET_INTERFACE_GET_TODAYS_DATA_LOG_REQUEST:
{
QByteArray *data = mChaletDataLogger->GetTodaysData();
mNetworkInterfacePtr->SendNetworkMessage(ID_CHALET_INTERFACE,mDeviceAddress,CHALET_INTERFACE_GET_TODAYS_DATA_LOG_RESPONSE,data->size(),data);
delete data;
break;
}
case CHALET_INTERFACE_GET_DATA_LOG_REQUEST:
{
QDate StartDate;
QDataStream Strm(Data);
Strm >> StartDate;
QByteArray *data = mChaletDataLogger->GetChaletData(StartDate);
mNetworkInterfacePtr->SendNetworkMessage(ID_CHALET_INTERFACE,mDeviceAddress,CHALET_INTERFACE_GET_DATA_LOG_RESPONSE,data->size(),data);
delete data;
break;
}
case CHALET_INTERFACE_GET_DATA_LOG_RESPONSE:
case CHALET_INTERFACE_GET_TODAYS_DATA_LOG_RESPONSE:
case CHALET_INTERFACE_GENERAL_STATUS_RESPONSE:
case CHALET_INTERFACE_AC_POWER_STATE_STATUS_RESPONSE:
case CHALET_INTERFACE_AC_POWER_SET_STATE_RESPONSE:
@@ -93,3 +115,8 @@ int CChaletInterface::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, in
return RET_OK;
}
void CChaletInterface::SendChaletCommActivityBeacon()
{
mNetworkInterfacePtr->SendNetworkMessage(ID_CHALET_INTERFACE,mDeviceAddress,CHALET_INTERFACE_CHALET_ACTIVITY_RESPONSE,0,0);
}
+3 -1
View File
@@ -2,8 +2,8 @@
#define CCHALETINTERFACE_H
#include "NetworkDevice.h"
#include "ChaletLoraDevice.h"
#include "ChaletLoraDevice.h"
class CChaletInterface: public QObject, public CNetworkDevice
{
@@ -13,8 +13,10 @@ public:
CChaletInterface(int Address, CAbstractNetworkCommIF *NetworkInterface, CChaletLoraDevice *DevicePtr);
virtual int NewDeviceFrameReceived(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data);
void SendChaletCommActivityBeacon();
private:
CChaletLoraDevice *mChaletLoraDevice;
CChaletDataLogger *mChaletDataLogger;
};
+44 -4
View File
@@ -1,5 +1,6 @@
#include "ChaletLoraDevice.h"
#include "GlobalDefine.h"
#include "ChaletInterface.h"
@@ -10,11 +11,16 @@ CChaletLoraDevice::CChaletLoraDevice(int Address, CAbstractNetworkCommIF *Networ
mDeviceAddress = Address;
mDeviceID = ID_CHALET_DEVICE;
mChaletStatusTimer = new QTimer();
mChaletStatusTimer->setInterval(1000);
mChaletStatusTimer->setSingleShot(true);
connect(mChaletStatusTimer,SIGNAL(timeout()),this,SLOT(CommTimerExpired()));
mJFUbidotsInterface = new CChaletJFUbidotsInterface("BBFF-tEq4lGAegEyP1H4EaYckNQ7ZHdZYzI");
mThingsBoardInterface.mChaletLoraDevice = this;
ResetCommStats();
}
CChaletLoraDevice::~CChaletLoraDevice()
@@ -26,11 +32,16 @@ int CChaletLoraDevice::Start()
{
ScheduleChaletStatusRequest();
mChaletStatusTimer->start(1000);
mThingsBoardInterface.RegisterThingsboardRPC();
return RET_OK;
}
int CChaletLoraDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data)
{
Q_UNUSED(DeviceID)
Q_UNUSED(DeviceAddress)
Q_UNUSED(DataSize)
switch(MessageID)
{
case CHALET_ACK:
@@ -69,20 +80,28 @@ int CChaletLoraDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, i
mChaletMainStatus.mBatterySOC = temp2;
mChaletMainStatus.mStatusToggleBit = !mChaletMainStatus.mStatusToggleBit;
mChaletMainStatus.mLastLoraStatus = QDateTime::currentDateTime();
CmdResponseReceived(CHALET_GENERAL_STATUS_REQUEST);
qDebug("voltage: %f",mChaletMainStatus.mBatteryVoltage);
qDebug("Current: %d",mChaletMainStatus.mBatteryCurrent);
qDebug("SOC: %d",mChaletMainStatus.mBatterySOC);
// qDebug("Current: %d",mChaletMainStatus.mBatteryCurrent);
// qDebug("SOC: %d",mChaletMainStatus.mBatterySOC);
qDebug("Inverter: %d",mChaletMainStatus.mInverterRelayStatus);
mChaletDataLogger.LogChaletLORAData(&mChaletMainStatus);
mJFUbidotsInterface->LogUbidotsChaletData(&mChaletMainStatus);
mBlynkInterface.UpdateChaletCurrent(mChaletMainStatus.mBatteryCurrent);
mBlynkInterface.UpdateChaletVoltage(mChaletMainStatus.mBatteryVoltage);
// mBlynkInterface.UpdateChaletWifiStatus((int)mChaletMainStatus.mInverterRelayStatus);
mBlynkInterface.UpdateChaletInverterStatus((int)mChaletMainStatus.mInverterRelayStatus);
mBlynkInterface.UpdateChaletStatusLed(((int)mChaletMainStatus.mStatusToggleBit)*255);
// mThingsBoardInterface.UpdateChaletVoltage(mChaletMainStatus.mBatteryVoltage);
mThingsBoardInterface.UpdateChaletData(mChaletMainStatus.mInverterRelayStatus,mChaletMainStatus.mWiFiModuleStatus,mChaletMainStatus.mCurrentSensorStatus,mChaletMainStatus.mBatteryVoltage,mChaletMainStatus.mBatteryCurrent,mChaletMainStatus.mBatterySOC,mChaletMainStatus.mIsOnline);
break;
}
case CHALET_AC_POWER_STATE_STATUS_RESPONSE:
@@ -185,6 +204,8 @@ void CChaletLoraDevice::CommTimerExpired()
}
}
mPendingNetworkMsgList[0].ResendCounter++;
mNbLostRequest++;
ComputeCommStats();
}
qDebug("Sending chalet request 0x%x",mPendingNetworkMsgList.at(0).mMessageID);
@@ -262,7 +283,9 @@ int CChaletLoraDevice::SendRebootCmd()
int CChaletLoraDevice::SendChaletCommand(int CmdID, int DataSize, QByteArray Data)
{
mNetworkInterfacePtr->SendNetworkMessage(ID_CHALET_DEVICE,mDeviceAddress,CmdID,DataSize,Data);
mNetworkInterfacePtr->SendNetworkMessage(ID_CHALET_DEVICE,mDeviceAddress,CmdID,DataSize,&Data);
mTotalNbRequest++;
ComputeCommStats();
return RET_OK;
}
@@ -351,3 +374,20 @@ int CChaletLoraDevice::ScheduleChaletCommand(int CmdID, QByteArray Data)
return RET_OK;
}
int CChaletLoraDevice::ResetCommStats()
{
mLostRequestPercentage = 0.0;
mTotalNbRequest = 0;
mNbLostRequest = 0;
return RET_OK;
}
int CChaletLoraDevice::ComputeCommStats()
{
mLostRequestPercentage = ((float)mNbLostRequest/(float)mTotalNbRequest)*100;
mChaletMainStatus.mLostRequestPercentage = mLostRequestPercentage;
// qDebug("Percent lost request: %f%%",mLostRequestPercentage);
return RET_OK;
}
+15
View File
@@ -11,6 +11,9 @@
#include <QList>
#include "BlynkCloudClient.h"
#include "ChaletDataLogger.h"
#include "ChaletUbidotsInterface.h"
#include "ThingsBoardInterface.h"
#define LORA_MAGIC_WORD 0xBAADCAFE
#define LORA_NORMAL_REQUEST_TIMEOUT 3000
@@ -37,22 +40,34 @@ public:
CChaletMainStatus GetChaletMainStatus() {return mChaletMainStatus;}
CBlynkCloudClient mBlynkInterface;
CThingsBoardInterface mThingsBoardInterface;
CChaletMainStatus mChaletMainStatus;
QList<CChaletNetworkMessage> mPendingNetworkMsgList;
CChaletDataLogger mChaletDataLogger;
CChaletJFUbidotsInterface *mJFUbidotsInterface;
unsigned long long int mNbLostRequest, mTotalNbRequest;
float mLostRequestPercentage;
CChaletDataLogger *GetDataLoggerHandle() {return &mChaletDataLogger;} //for Interface use...
int SendWiFiModuleSetState(bool State);
int SendInverterPowerRelayState(bool State);
int SendDOHarakiri();
int SendRebootCmd();
int SendChaletCommand(int CmdID,int DataSize, QByteArray Data);
int ResetCommStats();
int ComputeCommStats();
int CmdResponseReceived(int CmdID);
int ScheduleChaletStatusRequest();
int ScheduleChaletCommand(int CmdID, int DataSize, QByteArray Data);
int ScheduleChaletCommand(int CmdID,QByteArray Data);
public slots:
void CommTimerExpired();
+7
View File
@@ -0,0 +1,7 @@
#include "ChaletModbusServer.h"
CChaletModbusServer::CChaletModbusServer(CModbusRepository *Repo, int ModbusPort, int DevID) :
CModbusBackend(Repo)
{
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef CHALETMODBUSSERVER_H
#define CHALETMODBUSSERVER_H
#include <QObject>
#include <ModbusBackend.h>
class CChaletModbusServer : public CModbusBackend
{
Q_OBJECT
public:
CChaletModbusServer(CModbusRepository *Repo, int ModbusPort, int DevID);
virtual ~CChaletModbusServer();
int StartChaletModbusServer();
int CloseChaletModbusServer();
bool IsModbusConnected();
virtual void RegistersDatabaseUpdated(quint16 StartAddress, quint16 Length);
virtual void ModbusRequestException(quint8 ExceptionCode, quint8 FctCode);
};
#endif // CHALETMODUBSERVER_H
+89
View File
@@ -0,0 +1,89 @@
#include "ChaletUbidotsInterface.h"
CChaletUbidotsInterface::CChaletUbidotsInterface(QString UbidotsUsername, QObject *parent) : QObject(parent)
{
mUbidotsUsername = UbidotsUsername;
// mUbidotsPascalChaletClient = new QMqttClient(this);
// mUbidotsPascalChaletClient->setHostname("things.ubidots.com");
// mUbidotsPascalChaletClient->setPort(1883);
// mUbidotsPascalChaletClient->setUsername("BBFF-tEq4lGAegEyP1H4EaYckNQ7ZHdZYzI");
// mUbidotsPascalChaletClient->setPassword("12");
// mUbidotsPascalChaletClient->connectToHost();
mUbidotsChaletClient= new QMqttClient(this);
mUbidotsChaletClient->setHostname(UBIDOTS_URL);
mUbidotsChaletClient->setPort(UBIDOTS_PORT);
mUbidotsChaletClient->setUsername(mUbidotsUsername);//"BBFF-tEq4lGAegEyP1H4EaYckNQ7ZHdZYzI");
mUbidotsChaletClient->setPassword("12");
connect(mUbidotsChaletClient,SIGNAL(stateChanged(ClientState)),this,SLOT(UbidotsStateChanged(ClientState)));
mUbidotsChaletClient->connectToHost();
}
CChaletUbidotsInterface::~CChaletUbidotsInterface()
{
mUbidotsChaletClient->disconnectFromHost();
delete mUbidotsChaletClient;
}
int CChaletUbidotsInterface::LogUbidotsChaletData(CChaletMainStatus *Data)
{
Q_UNUSED(Data)
qDebug("LogUbidotsChaletData() should only be called from derived class...");
return RET_OK;
}
void CChaletUbidotsInterface::UbidotsStateChanged(QMqttClient::ClientState state)
{
qDebug("MQTT client %s is now in state %d",qPrintable(mInterfaceOwnerName),state);
//MQTTStateChanged(state);
}
//////////////////////implementation
CChaletJFUbidotsInterface::CChaletJFUbidotsInterface(QString Username) : CChaletUbidotsInterface(Username)
{
mInterfaceOwnerName = "J-F";
//mUbidotsChaletClient->subscribe(QMqttTopicFilter("/v1.6/devices/ChaletDuino"))
//connect(mInverterBtnSubscritption,SIGNAL(messageReceived(QMqttMessage)),this,SLOT(InverterBtnSubscriptionMsgReceived(QMqttMessage)));
qDebug("JF Ubidots interface created");
}
int CChaletJFUbidotsInterface::LogUbidotsChaletData(CChaletMainStatus *Data)
{
QString MQTTMsg = QString("{\"BattVoltage\": %1}").arg(Data->mBatteryVoltage);
mUbidotsChaletClient->publish(QMqttTopicName("/v1.6/devices/ChaletDuino"),MQTTMsg.toUtf8());
MQTTMsg.clear();
MQTTMsg = QString("{\"InverterSwitch\": %1}").arg(Data->mWiFiModuleStatus !=0);
mUbidotsChaletClient->publish(QMqttTopicName("/v1.6/devices/ChaletDuino"),MQTTMsg.toUtf8(),0,true);
return RET_OK;
}
void CChaletJFUbidotsInterface::InverterBtnSubscriptionMsgReceived(QMqttMessage msg)
{
qDebug("subscription msg. Topic name: %s",qPrintable(msg.topic().name()));
}
void CChaletJFUbidotsInterface::WiFiBtnSubscriptionMsgReceived(QMqttMessage msg)
{
}
void CChaletJFUbidotsInterface::MQTTStateChanged(QMqttClient::ClientState)
{
}
+48
View File
@@ -0,0 +1,48 @@
#ifndef CHALETUBIDOTSINTERFACE_H
#define CHALETUBIDOTSINTERFACE_H
#include <QObject>
#include <QtMqtt/QtMqtt>
#include <QMqttClient>
#include "ChaletData.h"
#include <QString>
#define UBIDOTS_URL "things.ubidots.com"
#define UBIDOTS_PORT 1883
class CChaletUbidotsInterface : public QObject
{
Q_OBJECT
public:
explicit CChaletUbidotsInterface(QString UbidotsUsername, QObject *parent = 0);
~CChaletUbidotsInterface();
virtual int LogUbidotsChaletData(CChaletMainStatus *Data);
virtual void MQTTStateChanged(QMqttClient::ClientState)=0;
QMqttClient *mUbidotsChaletClient;
QString mUbidotsUsername;
QString mInterfaceOwnerName;
public slots:
void UbidotsStateChanged(QMqttClient::ClientState);
};
class CChaletJFUbidotsInterface: public CChaletUbidotsInterface
{
Q_OBJECT
public:
explicit CChaletJFUbidotsInterface(QString Username);
virtual int LogUbidotsChaletData(CChaletMainStatus *Data);
void MQTTStateChanged(QMqttClient::ClientState);
QMqttSubscription *mInverterBtnSubscritption, *mWiFiBtnSubscription;
public slots:
void InverterBtnSubscriptionMsgReceived(QMqttMessage);
void WiFiBtnSubscriptionMsgReceived(QMqttMessage);
};
#endif // CHALETUBIDOTSINTERFACE_H
+285
View File
@@ -0,0 +1,285 @@
#include "ThingsBoardInterface.h"
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include "ChaletLoraDevice.h"
CThingsBoardInterface::CThingsBoardInterface()
{
mThingsBoardSocket = new QNetworkAccessManager;
mThingsBoardRPCSocket = new QNetworkAccessManager;
connect(mThingsBoardSocket,SIGNAL(finished(QNetworkReply*)),this,SLOT(ThingsBoardServerFinished(QNetworkReply*)));
connect(mThingsBoardSocket,SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),this,SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
connect(mThingsBoardRPCSocket,SIGNAL(finished(QNetworkReply*)),this,SLOT(ThingsBoardRPCFinished(QNetworkReply*)));
connect(mThingsBoardRPCSocket,SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),this,SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
}
CThingsBoardInterface::~CThingsBoardInterface()
{
mThingsBoardRPCReply->deleteLater();
delete mThingsBoardRPCSocket;
delete mThingsBoardSocket;
}
int CThingsBoardInterface::RegisterThingsboardRPC()
{
QNetworkRequest Req;
Req.setUrl(QUrl("http://192.168.50.100:8085/api/v1/aQIocLkJM2ECSqVT2VRx/rpc"));
mThingsBoardRPCReply = mThingsBoardRPCSocket->get(Req);
// connect(mThingsBoardRPCReply,SIGNAL(readyRead()),this,SLOT(RPCData()));
return 1;
}
void CThingsBoardInterface::UpdateChaletVoltage(float Voltage)
{
// "{\"temperature\": 25}" http://localhost:8080/api/v1/$ACCESS_TOKEN/telemetry --header "Content-Type:application/json"
//{"doubleKey":42.0}
QByteArray JsonByteArray = "{\"Bat_Voltage\":";
JsonByteArray.append((QString("%1}").arg(Voltage)).toUtf8().data());
QNetworkRequest Req;
Req.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
Req.setUrl(QUrl("http://192.168.50.100:8085/api/v1/aQIocLkJM2ECSqVT2VRx/telemetry"));
mThingsBoardSocket->post(Req,JsonByteArray);
}
void CThingsBoardInterface::UpdateChaletData(quint8 mInverterRelayStatus, quint8 mWiFiModuleStatus, qint8 mCurrentSensorStatus, float mBatteryVoltage, qint16 mBatteryCurrent, qint16 mBatterySOC, qint8 LoraState)
{
//{"key1":"value1", "key2":"value2"}
QByteArray JsonByteArray = "{\"Bat_Voltage\":";
JsonByteArray.append((QString("%1, ").arg(mBatteryVoltage)).toUtf8().data());
JsonByteArray.append("\"Bat_SOC\":");
JsonByteArray.append((QString("%1, ").arg(mBatterySOC)).toUtf8().data());
JsonByteArray.append("\"WiFi_State\":");
JsonByteArray.append((QString("%1, ").arg(mWiFiModuleStatus)).toUtf8().data());
JsonByteArray.append("\"CurrentSensor_State\":");
JsonByteArray.append((QString("%1, ").arg(mCurrentSensorStatus)).toUtf8().data());
JsonByteArray.append("\"Bat_Current\":");
JsonByteArray.append((QString("%1, ").arg(mBatteryCurrent)).toUtf8().data());
JsonByteArray.append("\"LoRa_State\":");
JsonByteArray.append((QString("%1, ").arg(LoraState)).toUtf8().data());
JsonByteArray.append("\"Inverter_State\":");
JsonByteArray.append((QString("%1}").arg(mInverterRelayStatus)).toUtf8().data());
QNetworkRequest Req;
Req.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
Req.setUrl(QUrl("http://192.168.50.100:8085/api/v1/aQIocLkJM2ECSqVT2VRx/telemetry"));
mThingsBoardSocket->post(Req,JsonByteArray);
// if(mThingsBoardRPCReply->isRunning() == true)
// {
// qDebug("RPC socket running...");
// }
// else
// {
// qDebug("RPC socket not runnging");
// }
}
void CThingsBoardInterface::ThingsBoardServerFinished(QNetworkReply *NetworkReply)
{
if(NetworkReply->isFinished() == false)
return;
if(NetworkReply->error() != QNetworkReply::NoError)
{
qDebug("Network error... %d", NetworkReply->error());
return;
}
QByteArray Reply = NetworkReply->readAll();
if(Reply.isEmpty())
return;
NetworkReply->deleteLater();
QString ReplyString(Reply);
qDebug("ThingsBoard Server Reply: %s",ReplyString.toUtf8().data());
ReplyString.remove("[\"");
ReplyString.remove("\"]");
QJsonValue Result(ReplyString);
// QJsonParseError error;
// QJsonDocument JsonReply = QJsonDocument::fromJson(Reply,&error);
// QJsonObject JsonObject = JsonReply.object();
// QStringList Keys = JsonObject.keys();
// int toto = ReplyString.toInt();
// qDebug("Switch = %d",toto);
}
void CThingsBoardInterface::sslErrors(QNetworkReply *ServerReply, QList<QSslError> ErrorList)
{
qDebug("Ssl errors...");
ServerReply->ignoreSslErrors();
}
void CThingsBoardInterface::ThingsBoardRPCFinished(QNetworkReply *NetworkReply)
{
if(mThingsBoardRPCReply->error() != QNetworkReply::NoError)
{
qDebug("Network error (ThingsBoardRPC)... %d", mThingsBoardRPCReply->error());
RegisterThingsboardRPC();
return;
}
QByteArray Reply = mThingsBoardRPCReply->readAll();
if(Reply.isEmpty())
{
qDebug("Received an empty RPC reply in ThiungsBoard...");
RegisterThingsboardRPC();
return;
}
mThingsBoardRPCReply->deleteLater();
QString ReplyString(Reply);
qDebug("ThingsBoard RPC Reply: %s",ReplyString.toUtf8().data());
QJsonDocument JSonReply = QJsonDocument::fromJson(Reply);
QJsonObject RPCCmd = JSonReply.object();
QJsonValue RequestedCmd = RPCCmd.value("method");
{
if(RequestedCmd.toString().contains("setChaletInverterState"))
{
//Fetch the required state.
bool RequestedState = RPCCmd.value("params").toBool();
if(RequestedState == true)
{
qDebug("Server Inverter TURN ON request received.");
mChaletLoraDevice->SendInverterPowerRelayState(true);
}
if(RequestedState == false)
{
qDebug("Server Inverter TURN OFF request received.");
mChaletLoraDevice->SendInverterPowerRelayState(false);
}
}
else if(RequestedCmd.toString().contains("setWiFiState"))
{
//Fetch the required state.
QString RequestedState = RPCCmd.value("params").toString();
if(RequestedState == "true")
{
qDebug("Server WiFi TURN ON request received.");
}
if(RequestedState == "false")
{
qDebug("Server WiFi TURN OFF request received.");
}
}
else
{
qDebug("Received an unknown RPC call from ThingsBoard server");
}
}
//Re-register for next request..
RegisterThingsboardRPC();
}
void CThingsBoardInterface::RPCsslErrors(QNetworkReply *ServerReply, QList<QSslError> ErrorList)
{
qDebug("Ssl errors...");
ServerReply->ignoreSslErrors();
}
void CThingsBoardInterface::RPCData()
{
// if(mThingsBoardRPCReply->isFinished() == false)
// return;
// if(mThingsBoardRPCReply->error() != QNetworkReply::NoError)
// {
// qDebug("Network error (RPCData)... %d", mThingsBoardRPCReply->error());
// RegisterThingsboardRPC();
// return;
// }
// QByteArray Reply = mThingsBoardRPCReply->readAll();
// if(Reply.isEmpty())
// {
// qDebug("Received an empty RPC reply in ThiungsBoard...");
// RegisterThingsboardRPC();
// return;
// }
// mThingsBoardRPCReply->deleteLater();
// QString ReplyString(Reply);
// qDebug("ThingsBoard RPC Reply: %s",ReplyString.toUtf8().data());
// QJsonDocument JSonReply = QJsonDocument::fromJson(Reply);
// QJsonObject RPCCmd = JSonReply.object();
// QJsonValue RequestedCmd = RPCCmd.value("method");
// {
// if(RequestedCmd.toString().contains("setChaletInverterState"))
// {
// //Fetch the required state.
// bool RequestedState = RPCCmd.value("params").toBool();
// if(RequestedState == true)
// {
// qDebug("Server Inverter TURN ON request received.");
// //mChaletLoraDevice->SendInverterPowerRelayState(true);
// }
// if(RequestedState == false)
// {
// qDebug("Server Inverter TURN OFF request received.");
// // mChaletLoraDevice->SendInverterPowerRelayState(false);
// }
// }
// else if(RequestedCmd.toString().contains("setWiFiState"))
// {
// //Fetch the required state.
// QString RequestedState = RPCCmd.value("params").toString();
// if(RequestedState == "true")
// {
// qDebug("Server WiFi TURN ON request received.");
// }
// if(RequestedState == "false")
// {
// qDebug("Server WiFi TURN OFF request received.");
// }
// }
// else
// {
// qDebug("Received an unknown RPC call from ThingsBoard server");
// }
// }
// //Re-register for next request..
// RegisterThingsboardRPC();
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef THINGSBOARDINTERFACE_H
#define THINGSBOARDINTERFACE_H
class CChaletLoraDevice;
#include <QObject>
#include <QNetworkAccessManager>
class CThingsBoardInterface : public QObject
{
Q_OBJECT
public:
CThingsBoardInterface();
~CThingsBoardInterface();
CChaletLoraDevice *mChaletLoraDevice;
int RegisterThingsboardRPC();
void UpdateChaletVoltage(float Voltage);
void UpdateChaletData(quint8 mInverterRelayStatus, quint8 mWiFiModuleStatus, qint8 mCurrentSensorStatus, float mBatteryVoltage, qint16 mBatteryCurrent, qint16 mBatterySOC, qint8 LoraState);
private:
QNetworkAccessManager *mThingsBoardSocket, *mThingsBoardRPCSocket;
QNetworkReply *mThingsBoardRPCReply;
public slots:
void ThingsBoardServerFinished(QNetworkReply*);
void sslErrors(QNetworkReply*,QList<QSslError>);
void ThingsBoardRPCFinished(QNetworkReply*);
void RPCsslErrors(QNetworkReply*,QList<QSslError>);
void RPCData();
};
#endif // THINGSBOARDINTERFACE_H
+10 -2
View File
@@ -17,7 +17,7 @@ CEthernetNetworkCommIF::~CEthernetNetworkCommIF()
}
int CEthernetNetworkCommIF::SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data)
int CEthernetNetworkCommIF::SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray *Data)
{
// Q_UNUSED(DeviceID)
// Q_UNUSED(DeviceAddress)
@@ -25,7 +25,15 @@ int CEthernetNetworkCommIF::SendNetworkMessage(int DeviceID, int DeviceAddress,
// Q_UNUSED(DataSize)
// Q_UNUSED(Data)
QByteArray Packet = GetTxPacket(MessageID,0,Data.data(),DataSize,DeviceAddress,DeviceID);
QByteArray Packet;
if(Data != 0)
{
Packet = GetTxPacket(MessageID,0,Data->data(),DataSize,DeviceAddress,DeviceID);
}
else
{
Packet = GetTxPacket(MessageID,0,0,0,DeviceAddress,DeviceID);
}
mTCPSocket->write(Packet);
return RET_OK;
+1 -1
View File
@@ -21,7 +21,7 @@ public:
QTcpSocket* mTCPSocket;
//CNetworkCommIF implementation
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data);
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray *Data);
//CNetworkProtocol implementation
virtual int NewFrameReceived(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data);
+20
View File
@@ -1,5 +1,25 @@
#include "HttpServer.h"
CHttpServer::CHttpServer()
{
mHttpServer = new QHttpServer;
mHttpServer->route("/", []()
{
qDebug("Test");
return "Ja Mon CB!!!!";
});
const auto port = mHttpServer->listen(QHostAddress::Any,8080);
qDebug("Server Opened");
}
CHttpServer::~CHttpServer()
{
delete mHttpServer;
}
+6 -1
View File
@@ -1,11 +1,16 @@
#ifndef HTTPSERVER_H
#define HTTPSERVER_H
#include <QtHttpServer/QHttpServer>
class CHttpServer
{
public:
QHttpServer *mHttpServer;
CHttpServer();
~CHttpServer();
};
#endif // HTTPSERVER_H
#endif // HTTPSERVER_H
+2 -2
View File
@@ -104,9 +104,9 @@ int CLoraNetworkCommIF::NewFrameReceived(int DeviceID, int DeviceAddress, int Me
return RET_OK;
}
int CLoraNetworkCommIF::SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data)
int CLoraNetworkCommIF::SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray *Data)
{
QByteArray Packet = GetTxPacket(MessageID,0,Data.data(),DataSize,DeviceAddress,DeviceID);
QByteArray Packet = GetTxPacket(MessageID,0,Data->data(),DataSize,DeviceAddress,DeviceID);
SendLoraFrame(6,4,Packet);
// SendLoraFrame(4,4,Packet);
return RET_OK;
+1 -1
View File
@@ -31,7 +31,7 @@ public:
int NewFrameReceived(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data);
//NetworkCommIF implementation
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray Data);
virtual int SendNetworkMessage(int DeviceID, int DeviceAddress, int MessageID, int DataSize, QByteArray *Data);
private:
QByteArray GetLoraFrame(unsigned short DestAddress,unsigned char DestChannel,QByteArray Payload);
+1
View File
@@ -92,6 +92,7 @@ void CMasterCtrl::Start()
mAVReceiverDevice->Start();
mChaletLoraNetworkCommInterface->SetLoraSerialPortSettings("COM3",QSerialPort::Baud9600);
// mChaletLoraNetworkCommInterface->SetLoraSerialPortSettings("COM5",QSerialPort::Baud9600);
mChaletLoraDevice->Start();
+756
View File
@@ -0,0 +1,756 @@
/*******************************************************************************
* *
* 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");
}
+203
View File
@@ -0,0 +1,203 @@
/*******************************************************************************/
/*
Description:
Classe qui implémente le protocole Modbus.
Cette classe est héritée par la classe serveur (CModbusCCMgr) ou client (CModbusMaster).
Elle sert à interpréter ou formatter des paquets Modbus provenant ou destinés au partenaire.
Elle peut être héritée soit un parent qui implémente une instance maître ou client mais pas les deux à la fois.
sur le réseau.
*/
/* ************************************************************************** */
/* Revision:
### YYYMMDD JFM
Verision d'origine.
### YYYYMMDD Description du besoin ou du bug
Description du changement.
*/
/* ************************************************************************** */
#ifndef CMODBUSBACKEND_H
#define CMODBUSBACKEND_H
#include <QObject>
#include <QTcpSocket>
#include "ModbusRepository.h"
#include <QTimer>
#define MODBUS_EXCEPTION_FCT_MASK 0x80
#define MODBUS_RETRY_DELAY 2000 //millisecs
#define MODBUS_RETRY_MAX_COUNT 2 //resend an unanswered request this many times
#define MODBUS_MAX_NB_REGISTERS 0x7D
enum eModbusFunctions
{
MODBUS_FCT_READ_HOLDING_REGISTERS = 3,
MODBUS_WRITE_SINGLE_REGISTER = 6,
MODBUS_FCT_WRITE_MULTIPLE_REGISTERS = 16
};
enum eModbusExceptionCodes
{
MODBUS_EXCEPTION_ILLEGAL_FCT = 1,
MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS = 2,
MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE = 3,
MODBUS_EXCEPTION_SERVER_DEVICE_FAILURE = 4,
MODBUS_EXCEPTION_ACKNOWLEDGE = 5,
MODBUS_EXCEPTION_SERVER_DEVICE_BUSY = 6,
MODBUS_EXCEPTION_MEMORY_PARITY_ERROR = 8,
MODBUS_EXCEPTION_GATEWAY_PATH_UNAVAILABLE = 10,
MODBUS_EXCEPTION_GATEWAY_TARGET_DEV_NOT_RESPONDING = 11
};
//Énumération des différents modes possibles
enum eModbusModes
{
MODBUS_MASTER_MODE,
MODBUS_SLAVE_MODE,
MODBUS_INVALID_MODE
};
class CModbusException
{
public:
};
class CModbusPDU
{
public:
qint8 mFunctionCode; //Type de transaction (voir structure eModbusFunctions) pour les fonctions implémentées par le présent module
QByteArray mData; //Les données de la transaction
};
class CModbusHeader //Objet contenant les données d'un header Modbus (selon la spécification)
{
public:
qint16 mTransactionID;
qint16 mProtocolID;
qint16 mMessageLength;
qint8 mUnitID;
};
class CModbusTransaction //Objet contenant les données d'une transaction Modbus
{
public:
CModbusHeader mHeader; //Le header de la transaction
CModbusPDU mPDU; //Le PDU (fonction & données) de la transaction (voir classe CModbusPDU)
};
class CModbusRequest : public CModbusTransaction //Objet représentant une transaction (mode maître)
{
public:
CModbusRequest();
~CModbusRequest();
int mRetries; //nombre de fois que la transaction a été retransmise
QTimer *mRequestTimer; //Timer servant à retransmettre la requête si aucune réponse ne survient
quint16 mStartAddress, mNbRegisters; //For convinience...
};
class CModbusBackend : public QObject
{
Q_OBJECT
private:
//enum eModbusMasterSMStates
//{
// MODBUS_SM_WAIT_FOR__STATE
//};
//enum eModbusSlaveSMStates
//{
//};
public:
CModbusBackend(CModbusRepository *Repo); //Le pointeur vers le registre des données est obligatoire.
virtual ~CModbusBackend();
QTcpSocket *mModbusTCPSocketHandle; //Pointeur vers le Socket TCP du réseau Modbus.
CModbusRepository *mModbusRepo; //Pointeur vers le registre de données Modbus
bool mDataLinkValid; //Flag indiquant si le lien de communication est en santé
int mModbusMode; //Indique si le lien modbus de cette instance est exploitée en "slave" (serveur) ou un "master" (client) (voir struct eModbusModes ci-haut).
qint8 mDeviceID; //Le device ID Modbus de cette instance.
void ModbusLinkDisconnected(); //Fonction appelée par le parent lors d'une perte de la connection.
void SetDeviceID(qint8 ID){mDeviceID = ID;}
//Master (client)
int SendReadHoldingRegistersRequest(quint16 StartAddress, quint16 RegisterCount); //Fonction servant à transmettre la fonction modbus "Read HR" au partenaire
int SendWriteHoldingRegistersRequest(quint16 StartAddress, quint16 RegisterCount); //Fonction servant à transmettre la fonction modbus "Write HR" au partenaire
int SendWriteSingleRegisterRequest(quint16 Address); //Fonction servant à transmettre la fonction modbus "Write Single Register" au partenaire
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
//Master virtual functions
virtual void ModbusResponseException(quint8 ExceptionCode, quint8 FctCode); //Fonction virtuelle à être implémentée par la classe dérivée
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.
//Sert à informer la classe dérivée lorsqu'une exception se produit dans la communication Modbus (réponse)
//Slave virtual function
virtual void ModbusRequestException(quint8 ExceptionCode, quint8 FctCode); //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)
private:
//Sur un réseau Modbus, l'eslcave (slave) est un serveur et le maître (master) est un client.
//Slave (server)
int AnalyzeModbusRequest(CModbusTransaction Transaction); //Exécute l'analyse des données reçues d'une requête Modbus
int SendModbusResponse(CModbusTransaction RequestTransaction, QByteArray Data); //Fonction servant à composer et transmettre une réponse à une requête.
int SendErrorResponse(CModbusTransaction RequestTransaction, quint8 ErrorCode); //Fonction servant à composer et transmettre une réponse d'erreur à une requête.
//Master (client)
QList<CModbusRequest*> mRequestsList; //Liste des requêtes courantes en attente d'une réponse du serveur
int SendModbusRequest(CModbusRequest *Request); //Construit et transmet une nouvelle requête Modbus au partenaire.
quint16 GetNewTransactionID(); //Fonction (helper) servant à générer un nouvel ID de transaction nécessaire pour transmettre une nouvelle requête.
int AnalyzeModbusResponse(CModbusTransaction Transaction); //Exécute l'analyse des données reçues d'une réponse Modbus
quint16 mTransactionIDCounter; //Compteur interne des ID de transactions Modbus
int mModbusRequestTimeout; //Valeur du délai d'attente maximal avant de recevoir une réponse à une requête. À l'expiration du timer, la requête est renvoyée.
int mModbusMaxRetry; //Valeur du nombre maximal de renvoi d'une requête avant d'invalider la requête.
signals:
// void RegistersDatabaseUpdated(quint16 StartAdderss, quint16 Length);
// //Master signals
// void ModbusResponseException(quint8 ExceptionCode, quint8 FctCode);
//// //Slave signals
// void ModbusRequestException(quint8 ExceptionCode, quint8 FctCode);
public slots:
void ModbusDataReady(); //Slot indiquant au module que des données sont arrivées et sont attente dans le Socket.
void RequestTimerExpired(); //Slot indiquant qu'un timer de requête a expiré.
};
QDataStream &operator<<(QDataStream &out, const CModbusHeader &source);
QDataStream &operator>>(QDataStream &in, CModbusHeader &dest);
#endif // CMODBUSBACKEND_H
+220
View File
@@ -0,0 +1,220 @@
#include "ModbusRepository.h"
#include <QByteArray>
#include <QBuffer>
#include <QDataStream>
//#include "EngLog.h"
CModbusRepository::CModbusRepository()
{
}
int CModbusRepository::AddHRDataMap(quint16 StartAddress, quint16 Length)
{
mMutex.lockForWrite();
for(int i= 0; i < mHoldingRegisters.size(); i++)
{
if((StartAddress >= mHoldingRegisters.at(i).mStartAddress && StartAddress <= mHoldingRegisters.at(i).mEndAddress) ||
(StartAddress + Length - 1 >= mHoldingRegisters.at(i).mStartAddress && StartAddress + Length - 1 <= mHoldingRegisters.at(i).mEndAddress))
{
mMutex.unlock();
return RET_ERROR; //The data map overlaps an existing map.
}
}
CHRDataMap NewHRMap(StartAddress,Length);
mHoldingRegisters.append(NewHRMap);
mMutex.unlock();
// CEngLog::instance()->AddLogString(QString("Création d'une zone registre modbus à l'adresse: %1 de taille %2").arg(StartAddress).arg(Length));
return RET_OK;
}
bool CModbusRepository::IsHRValid(quint16 StartAddress, quint16 Length, int *index)
{
mMutex.lockForRead();
for(int i= 0; i < mHoldingRegisters.size(); i++)
{
if((StartAddress >= mHoldingRegisters.at(i).mStartAddress && StartAddress + Length - 1 <= mHoldingRegisters.at(i).mEndAddress))
{
if(index != 0)
{
*index = i;
}
mMutex.unlock();
return true;
}
}
mMutex.unlock();
return false;
}
QByteArray CModbusRepository::GetHRData(quint16 StartAddress, quint16 Length, bool *OK)
{
QByteArray Data;
int RegisterIndex = 0;
if(IsHRValid(StartAddress,Length, &RegisterIndex) == false)
{
if(OK != 0)
{
*OK = false;
}
}
else
{
mMutex.lockForRead();
int DataIndex = StartAddress - mHoldingRegisters.at(RegisterIndex).mStartAddress;
for(int i = 0; i < Length; i++)
{
quint16 CurReg = mHoldingRegisters.at(RegisterIndex).mRegistersData.at(DataIndex++);
quint8 HighByte, LowByte;
LowByte = CurReg & 0x00FF;
HighByte = (CurReg >> 8) & 0x00FF;
Data.append(HighByte);
Data.append(LowByte);
}
mMutex.unlock();
if(OK != 0)
*OK = true;
}
return Data;
}
int CModbusRepository::WriteHRData(quint16 StartAddress, quint16 Length, QByteArray Data)
{
int RegisterIndex;
if(IsHRValid(StartAddress,Length, &RegisterIndex) == false)
{
return RET_ERROR;
}
if(Length*2 != Data.size())
{
return RET_ERROR;
}
mMutex.lockForWrite();
int DataIndex = StartAddress - mHoldingRegisters.at(RegisterIndex).mStartAddress;
for(int i = 0; i < Length; i++)
{
quint8 HighByte = Data.at(i*2);
quint8 LowByte = Data.at(i*2 + 1);
qint16 Word = HighByte;
Word <<= 8;
Word += LowByte;
Word = Word &0xFFFF;
mHoldingRegisters[RegisterIndex].mRegistersData[DataIndex++] = Word;
}
mMutex.unlock();
emit RepoChanged(StartAddress,Length);
return RET_OK;
}
int CModbusRepository::WriteSingleReg(quint16 Address, quint16 Value)
{
int RegisterIndex;
if(IsHRValid(Address,1, &RegisterIndex) == false)
{
return RET_ERROR;
}
mMutex.lockForWrite();
int DataIndex = Address - mHoldingRegisters.at(RegisterIndex).mStartAddress;
mHoldingRegisters[RegisterIndex].mRegistersData[DataIndex] = Value;
mMutex.unlock();
emit RepoChanged(Address,1);
return RET_OK;
}
int CModbusRepository::WriteMultipleRegs(quint16 StartAddress, QList<qint16> Data)
{
int RegisterIndex;
if(IsHRValid(StartAddress,Data.size(), &RegisterIndex) == false)
{
return RET_ERROR;
}
mMutex.lockForWrite();
int DataIndex = StartAddress - mHoldingRegisters.at(RegisterIndex).mStartAddress;
for(int i = 0; i < Data.size(); i++)
{
mHoldingRegisters[RegisterIndex].mRegistersData[DataIndex++] = Data.at(i);
}
mMutex.unlock();
emit RepoChanged(StartAddress,Data.size());
return RET_OK;
}
QList<qint16> CModbusRepository::GetRegs(quint16 StartAddress, quint16 Length, bool *OK)
{
int RegisterIndex;
if(IsHRValid(StartAddress,Length, &RegisterIndex) == false)
{
if(OK != 0)
{
*OK = false;
}
return QList<qint16>();
}
if(OK != 0)
{
*OK = true;
}
int DataIndex = StartAddress - mHoldingRegisters.at(RegisterIndex).mStartAddress;
return mHoldingRegisters[RegisterIndex].mRegistersData.mid(DataIndex,Length);
}
quint16 CModbusRepository::GetSingleReg(quint16 Address, bool *OK)
{
int RegisterIndex;
if(IsHRValid(Address,1, &RegisterIndex) == false)
{
if(OK != 0)
*OK = false;
return 0;
}
if(OK != 0)
*OK = true;
mMutex.lockForRead();
int DataIndex = Address - mHoldingRegisters.at(RegisterIndex).mStartAddress;
quint16 Data = mHoldingRegisters[RegisterIndex].mRegistersData[DataIndex];
mMutex.unlock();
return Data;
}
CHRDataMap::CHRDataMap(quint16 StartAddress, quint16 Length)
{
mStartAddress = StartAddress;
mLength = Length;
for(int i = 0; i < Length; i++)
{
mRegistersData.append(0);
}
mEndAddress = StartAddress + Length - 1;
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef MODBUSREPOSITORY_H
#define MODBUSREPOSITORY_H
#include "GlobalDefine.h"
#include <QList>
#include <QReadWriteLock>
class CHRDataMap ///Holding Register data map.
{
public:
CHRDataMap(quint16 StartAddress, quint16 Length);
quint16 mStartAddress,mEndAddress;
quint16 mLength;
QList<qint16> mRegistersData;
};
class CModbusRepository : public QObject
{
Q_OBJECT
public:
CModbusRepository();
//Holding Registers
int AddHRDataMap(quint16 StartAddress, quint16 Length);
bool IsHRValid(quint16 StartAddress, quint16 Length, int *index = 0);
QByteArray GetHRData(quint16 StartAddress, quint16 Length, bool *OK = 0);
quint16 GetSingleReg(quint16 Address, bool *OK = 0);
QList<qint16> GetRegs(quint16 StartAddress, quint16 Length, bool *OK = 0);
int WriteHRData(quint16 StartAddress, quint16 Length, QByteArray Data);
int WriteSingleReg(quint16 Address, quint16 Value);
int WriteMultipleRegs(quint16 StartAddress, QList<qint16> Data);
const QList<CHRDataMap> *GetRepo(){return &mHoldingRegisters;}
private:
QList<CHRDataMap> mHoldingRegisters;
QReadWriteLock mMutex;
signals:
void RepoChanged(quint16 StartAddress, quint16 Length);
};
#endif // MODBUSREPOSITORY_H
+1 -1
View File
@@ -13,7 +13,7 @@ CNetworkDevice::~CNetworkDevice()
{
if(mNetworkInterfacePtr != 0)
{
delete mNetworkInterfacePtr;
// delete mNetworkInterfacePtr;
}
}
+5
View File
@@ -265,6 +265,11 @@ enum CHALET_INTERFACE_CMDS
CHALET_INTERFACE_DO_HARAKIRI_RESPONSE,
CHALET_INTERFACE_REBOOT_CPU_REQUEST,
CHALET_INTERFACE_REBOOT_CPU_RESPONSE,
CHALET_INTERFACE_GET_TODAYS_DATA_LOG_REQUEST,
CHALET_INTERFACE_GET_TODAYS_DATA_LOG_RESPONSE,
CHALET_INTERFACE_CHALET_ACTIVITY_RESPONSE,
CHALET_INTERFACE_GET_DATA_LOG_REQUEST,
CHALET_INTERFACE_GET_DATA_LOG_RESPONSE,
MAX_CHALET_INTERFACE_CMD
};
+2 -2
View File
@@ -69,7 +69,7 @@ int CSprinklerDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, in
}
case SPRINKLER_DEVICE_SET_SPRINKLER_STATE_ACK:
{
mNetworkInterfacePtr->SendNetworkMessage(ID_SPRINKLER_DEVICE,1,SPRINKLER_DEVICE_GET_SPRINKLER_STATE_REQUEST,0,QByteArray());
mNetworkInterfacePtr->SendNetworkMessage(ID_SPRINKLER_DEVICE,1,SPRINKLER_DEVICE_GET_SPRINKLER_STATE_REQUEST,0,0);
break;
}
case SPRINKLER_DEVICE_GET_SPRINKLER_STATE_RESPONSE:
@@ -143,5 +143,5 @@ int CSprinklerDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, in
void CSprinklerDevice::StatusTimerExpired()
{
mNetworkInterfacePtr->SendNetworkMessage(ID_SPRINKLER_DEVICE,1,SPRINKLER_DEVICE_STATUS_REQUEST,0,QByteArray());
mNetworkInterfacePtr->SendNetworkMessage(ID_SPRINKLER_DEVICE,1,SPRINKLER_DEVICE_STATUS_REQUEST,0,0);
}
+1 -1
View File
@@ -63,7 +63,7 @@ int CSprinklerInterface::NewDeviceFrameReceived(int DeviceID, int DeviceAddress,
MsgData.append(Sprinkler->mDeviceAddress); //1st device address
MsgData.append((unsigned char)Sprinkler->mSprinklerState);
mNetworkInterfacePtr->SendNetworkMessage(ID_SPRINKLER_INTERFACE,mDeviceAddress,SPRINKLER_INTERFACE_GET_SPRINKLER_STATE_RESPONSE,MsgData.size(),MsgData);
mNetworkInterfacePtr->SendNetworkMessage(ID_SPRINKLER_INTERFACE,mDeviceAddress,SPRINKLER_INTERFACE_GET_SPRINKLER_STATE_RESPONSE,MsgData.size(),&MsgData);
break;
}
+6 -6
View File
@@ -61,7 +61,7 @@ int CSMSDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, int Mess
*FrameDataStrm << DID;
FrameBuffer.seek(0);
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_DID_INFO_RESPONSE,FrameData.size(),FrameData);
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_DID_INFO_RESPONSE,FrameData.size(),&FrameData);
delete FrameDataStrm;
break;
@@ -81,7 +81,7 @@ int CSMSDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, int Mess
}
FrameBuffer.seek(0);
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_GET_ALL_MSG_RESPONSE,FrameData.size(),FrameData);
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_GET_ALL_MSG_RESPONSE,FrameData.size(),&FrameData);
FrameBuffer.close();
delete FrameDataStrm;
@@ -131,7 +131,7 @@ int CSMSDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, int Mess
}
FrameBuffer.seek(0);
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_GET_CONTACTS_RESPONSE,FrameData.size(),FrameData);
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_GET_CONTACTS_RESPONSE,FrameData.size(),&FrameData);
FrameBuffer.close();
delete FrameDataStrm;
@@ -159,7 +159,7 @@ int CSMSDevice::NewDeviceFrameReceived(int DeviceID, int DeviceAddress, int Mess
void CSMSDevice::RequestStatus()
{
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_STATUS_REQUEST,0,QByteArray());
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_STATUS_REQUEST,0,0);
}
void CSMSDevice::SendSMSSentAck(bool Ack)
@@ -176,7 +176,7 @@ void CSMSDevice::SendSMSSentAck(bool Ack)
}
*FrameDataStrm << ACKData;
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_SEND_SMS_ACK,FrameData.size(),FrameData);
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_SEND_SMS_ACK,FrameData.size(),&FrameData);
FrameBuffer.close();
@@ -198,7 +198,7 @@ void CSMSDevice::SendNewSMSReceived(QList<CSMSMessage> NewMessages)
}
FrameBuffer.seek(0);
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_NEW_MSG_NOTIFICATION,FrameData.size(),FrameData);
mNetworkInterfacePtr->SendNetworkMessage(mDeviceID,mDeviceAddress,SMS_CLIENT_DEVICE_NEW_MSG_NOTIFICATION,FrameData.size(),&FrameData);
FrameBuffer.close();
delete FrameDataStrm;