#include "ChaletLoraDevice.h" #include "GlobalDefine.h" CChaletLoraDevice::CChaletLoraDevice(int Address, CAbstractNetworkCommIF *NetworkInterface): CNetworkDevice(ID_CHALET_DEVICE,Address,NetworkInterface) { NetworkInterface->mDevicePtr = this; 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; mChaletModbusRepo.AddHRDataMap(0x2000,50); mChaletModbusServer = new CChaletModbusServer(&mChaletModbusRepo,512,1); mDeviceWiFiParameters.fill(0xFF); ResetCommStats(); } CChaletLoraDevice::~CChaletLoraDevice() { delete mChaletStatusTimer; } 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: { CmdResponseReceived(CHALET_ACK); break; } case CHALET_GENERAL_STATUS_RESPONSE: { float temp; qint16 temp2; qDebug("Chalet Status RX"); char VoltageArray[4]; char TemperatureArray[4]; char SolarPanelCurrentArray[2]; char BatterySOCArray[2]; mChaletMainStatus.mInverterRelayStatus = ((Data[0] & LORA_CHALET_STATUS_POWER_RELAY_MASK) != 0); mChaletMainStatus.mCurrentSensorStatus = ((Data[0] & LORA_CHALET_STATUS_CUR_SENSOR_MASK) != 0); mChaletMainStatus.mWiFiModuleStatus = Data[1]; VoltageArray[0] = Data[2]; VoltageArray[1] = Data[3]; VoltageArray[2] = Data[4]; VoltageArray[3] = Data[5]; memcpy(&temp,VoltageArray,4); mChaletMainStatus.mBatteryVoltage = temp; SolarPanelCurrentArray[0] = Data[6]; SolarPanelCurrentArray[1] = Data[7]; memcpy(&temp2,SolarPanelCurrentArray,2); mChaletMainStatus.mBatteryCurrent = temp2; BatterySOCArray[0] = Data[8]; BatterySOCArray[1] = Data[9]; memcpy(&temp2,BatterySOCArray,2); mChaletMainStatus.mBatterySOC = temp2; TemperatureArray[0] = Data[10]; TemperatureArray[1] = Data[11]; TemperatureArray[2] = Data[12]; TemperatureArray[3] = Data[13]; memcpy(&temp,TemperatureArray,4); mChaletMainStatus.mChaletTemperature = temp; 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("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); mThingsBoardInterface.UpdateChaletData(&mChaletMainStatus); mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_ONLINE_STATE_REG,(quint16)mChaletMainStatus.mIsOnline); mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_WIFI_STATE_REG,(quint16)mChaletMainStatus.mWiFiModuleStatus); mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_CURRENT_SENSOR_STATE_REG,(quint16)mChaletMainStatus.mCurrentSensorStatus); mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_HARAKIRI_DONE_REG,(quint16)mChaletMainStatus.mHarakiriDone); mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_STATUS_TOGGLE_REG,(quint16)mChaletMainStatus.mStatusToggleBit); //mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_WATCHDOG_STATUS_REG,(quint16),mChaletMainStatus.m) mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_INVERTER_REG,(quint16)mChaletMainStatus.mInverterRelayStatus); quint16 TempInt16; memcpy(&TempInt16,&VoltageArray[0],2); mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_BATTERY_VOLTAGE_REG_1,TempInt16); memcpy(&TempInt16,&VoltageArray[2],2); mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_BATTERY_VOLTAGE_REG_2,TempInt16); mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_BATTERY_CURRENT_REG_1,mChaletMainStatus.mBatteryCurrent); mChaletModbusRepo.WriteSingleReg(CHALET_MODBUS_BATTERY_SOC_REG,mChaletMainStatus.mBatterySOC); break; } case CHALET_AC_POWER_STATE_STATUS_RESPONSE: { mChaletMainStatus.mInverterRelayStatus = Data[0]; CmdResponseReceived(CHALET_AC_POWER_STATE_STATUS_REQUEST); break; } case CHALET_AC_POWER_SET_STATE_RESPONSE: { qDebug("Lora set Inverter Power response : 0x%d",(int)Data[0]); mChaletMainStatus.mInverterRelayStatus = Data[0]; CmdResponseReceived(CHALET_AC_POWER_SET_STATE_REQUEST); break; } case CHALET_BATTERY_VOLTAGE_RESPONSE: { CmdResponseReceived(CHALET_BATTERY_VOLTAGE_REQUEST); break; } case CHALET_BATTERY_CURRENT_RESPONSE: { CmdResponseReceived(CHALET_BATTERY_CURRENT_REQUEST); break; } case CHALET_WIFI_STATUS_RESPONSE: { mChaletMainStatus.mWiFiModuleStatus = Data[0]; CmdResponseReceived(CHALET_WIFI_STATUS_REQUEST); break; } case CHALET_WIFI_SET_STATE_RESPONSE: { qDebug("Lora set WiFi response : 0x%d",(int)Data[0]); mChaletMainStatus.mWiFiModuleStatus = Data[0]; CmdResponseReceived(CHALET_WIFI_SET_STATE_REQUEST); break; } case CHALET_DO_HARAKIRI_CONFIRMATION: { if(Data[0] == (char)1) { qDebug("ChaletDuino has commited suicide (HARAKIRI)"); mChaletMainStatus.mHarakiriDone = true; } else { qDebug("HARAKIRI magic word invalid."); } CmdResponseReceived(CHALET_DO_HARAKIRI_REQUEST); break; } case CHALET_REBOOT_CPU_RESPONSE: { if(Data[0] == (char)1) { qDebug("ChaletDuino is rebooting"); } else { qDebug("Reboot magic word invalid"); } CmdResponseReceived(CHALET_DO_HARAKIRI_REQUEST); break; } case CHALET_GET_STORED_WIFI_SETTINGS_RESPONSE: { CmdResponseReceived(CHALET_GET_STORED_WIFI_SETTINGS_REQUEST); mDeviceWiFiParameters = Data.left(8); emit DeviceWifiStoredParamsReceived(); break; } case CHALET_SET_STORED_WIFI_SETTINGS_RESPONSE: { CmdResponseReceived(CHALET_SET_STORED_WIFI_SETTINGS_REQUEST); emit DeviceWifiSetParamsResult(Data[0]); break; } default: { return RET_ERROR; break; } } return RET_OK; } void CChaletLoraDevice::CommTimerExpired() { if(mPendingNetworkMsgList.isEmpty()) { qDebug("Empty command list in comm timer "); ScheduleChaletStatusRequest(); mChaletStatusTimer->start(LORA_NORMAL_REQUEST_TIMEOUT); return; } if(mPendingNetworkMsgList.first().PendingResponse == true) { //The current command is still waiting for a response. Check how many times we tried to send it qDebug("Cmd 0x%x timetout... retrying",mPendingNetworkMsgList.first().mMessageID); if(mPendingNetworkMsgList.first().ResendCounter >= 2) { //After 2 retries, declare module offline, clear the send buffer and start sending status requests... if(mChaletMainStatus.mIsOnline == true) { mChaletMainStatus.mIsOnline = false; qDebug("Chalet LORA interface Offline. Switching to status requests..."); mPendingNetworkMsgList.clear(); ScheduleChaletStatusRequest(); mPendingNetworkMsgList.first().ResendCounter = 2; } } mPendingNetworkMsgList[0].ResendCounter++; mNbLostRequest++; ComputeCommStats(); } qDebug("Sending chalet request 0x%x",mPendingNetworkMsgList.at(0).mMessageID); mPendingNetworkMsgList[0].PendingResponse = true; SendChaletCommand(mPendingNetworkMsgList.at(0).mMessageID,mPendingNetworkMsgList.at(0).mData.size(),mPendingNetworkMsgList.at(0).mData); //Harakiri and Reboot commands should not stay in the queue... if(mPendingNetworkMsgList.at(0).mMessageID == CHALET_DO_HARAKIRI_REQUEST || mPendingNetworkMsgList.at(0).mMessageID == CHALET_REBOOT_CPU_REQUEST) { mPendingNetworkMsgList.clear(); ScheduleChaletStatusRequest(); } mChaletStatusTimer->start(LORA_RESPONSE_TIME_TIMEOUT); //we should get an answer within 5 seconds. // SendChaletCommand(CHALET_GENERAL_STATUS_REQUEST,0,QByteArray()); } int CChaletLoraDevice::SendWiFiModuleSetState(bool State) { QByteArray Data; Data.resize(1); if(State) { Data[0] = 1; } else { Data[0] = 0; } return ScheduleChaletCommand(CHALET_WIFI_SET_STATE_REQUEST,1,Data); } int CChaletLoraDevice::SendInverterPowerRelayState(bool State) { QByteArray Data; Data.resize(1); if(State) { Data[0] = 1; } else { Data[0] = 0; } return ScheduleChaletCommand(CHALET_AC_POWER_SET_STATE_REQUEST,Data); } int CChaletLoraDevice::SendDOHarakiri() { QByteArray Data; //Magic word... Data.clear(); Data.append(0xBA); Data.append(0xAD); Data.append(0xBE); Data.append(0xEF); return ScheduleChaletCommand(CHALET_DO_HARAKIRI_REQUEST,Data); } int CChaletLoraDevice::SendRebootCmd() { QByteArray Data; //Magic word... Data.clear(); Data.append(0xBA); Data.append(0xAD); Data.append(0xCA); Data.append(0xFE); return ScheduleChaletCommand(CHALET_REBOOT_CPU_REQUEST,Data); } int CChaletLoraDevice::SendGetWifiParams() { return ScheduleChaletCommand(CHALET_GET_STORED_WIFI_SETTINGS_REQUEST,QByteArray()); } int CChaletLoraDevice::SendSetWifiParams(QByteArray Data) { return ScheduleChaletCommand(CHALET_SET_STORED_WIFI_SETTINGS_REQUEST,Data); } int CChaletLoraDevice::SendChaletCommand(int CmdID, int DataSize, QByteArray Data) { mNetworkInterfacePtr->SendNetworkMessage(ID_CHALET_DEVICE,mDeviceAddress,CmdID,DataSize,&Data); mTotalNbRequest++; ComputeCommStats(); return RET_OK; } int CChaletLoraDevice::CmdResponseReceived(int CmdID) { Q_UNUSED(CmdID) if(mChaletMainStatus.mIsOnline == false) { mChaletMainStatus.mIsOnline = true; qDebug("Chalet is ONLINE!"); } qDebug("Chalet response received from cmd: 0x%x",CmdID); if(mPendingNetworkMsgList.size() == 0) { qDebug("Cmd ack received but list is empty!!!"); } else { CChaletNetworkMessage Msg = mPendingNetworkMsgList.takeFirst(); if(Msg.mMessageID != CmdID) { qDebug("Inconsistency between active cmd and ack received [received 0x%x] != [cur 0x%x]!!!", Msg.mMessageID,CmdID); } } if(mPendingNetworkMsgList.size() == 0) //If no message is left pending... schedule status request { ScheduleChaletStatusRequest(); mChaletStatusTimer->start(LORA_NORMAL_REQUEST_TIMEOUT);//Next status request will be sent within this timeout } else { mChaletStatusTimer->start(500); //wait a little and send the next cmd. } return RET_OK; } int CChaletLoraDevice::ScheduleChaletStatusRequest() { return ScheduleChaletCommand(CHALET_GENERAL_STATUS_REQUEST,0,QByteArray()); } int CChaletLoraDevice::ScheduleChaletCommand(int CmdID, int DataSize, QByteArray Data) { Q_UNUSED(DataSize) // if(mChaletMainStatus.mIsOnline == false) // { // return RET_ERROR; // } // CChaletNetworkMessage Command(CmdID,Data); // mPendingNetworkMsgList.append(Command); ScheduleChaletCommand(CmdID,Data); return RET_OK; } int CChaletLoraDevice::ScheduleChaletCommand(int CmdID, QByteArray Data) { // if(mChaletMainStatus.mIsOnline == false) // { // return RET_ERROR; // } if(mChaletMainStatus.mIsOnline == true && CmdID != CHALET_GENERAL_STATUS_REQUEST) //When chalet is online, send command right away if we are not waiting for an answer. { //if we are in nominal conditions (waiting to send a status request). Cancel the status request and send the command right away. if((mPendingNetworkMsgList.size() == 1) && (mPendingNetworkMsgList.at(0).mMessageID == CHALET_GENERAL_STATUS_REQUEST) && mPendingNetworkMsgList.at(0).PendingResponse == false) { mPendingNetworkMsgList.clear(); mChaletStatusTimer->start(10); //Allow some time to breathe } } CChaletNetworkMessage Command(CmdID,Data); mPendingNetworkMsgList.append(Command); 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; }