mirror of https://github.com/lumapu/ahoy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
10 KiB
276 lines
10 KiB
//-----------------------------------------------------------------------------
|
|
// 2024 Ahoy, https://ahoydtu.de
|
|
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef __PUB_MQTT_IV_DATA_H__
|
|
#define __PUB_MQTT_IV_DATA_H__
|
|
|
|
#include "../utils/dbg.h"
|
|
#include "../hm/hmSystem.h"
|
|
#include "pubMqttDefs.h"
|
|
|
|
typedef std::function<void(const char *subTopic, const char *payload, bool retained, uint8_t qos)> pubMqttPublisherType;
|
|
|
|
struct sendListCmdIv {
|
|
uint8_t cmd;
|
|
Inverter<> *iv;
|
|
sendListCmdIv(uint8_t a, Inverter<> *i) : cmd(a), iv(i) {}
|
|
};
|
|
|
|
template<class HMSYSTEM>
|
|
class PubMqttIvData {
|
|
public:
|
|
void setup(HMSYSTEM *sys, uint32_t *utcTs, std::queue<sendListCmdIv> *sendList) {
|
|
mSys = sys;
|
|
mUtcTimestamp = utcTs;
|
|
mSendList = sendList;
|
|
mState = IDLE;
|
|
mZeroValues = false;
|
|
|
|
memset(mIvLastRTRpub, 0, MAX_NUM_INVERTERS * sizeof(uint32_t));
|
|
mRTRDataHasBeenSent = false;
|
|
|
|
mTable[IDLE] = &PubMqttIvData::stateIdle;
|
|
mTable[START] = &PubMqttIvData::stateStart;
|
|
mTable[FIND_NXT_IV] = &PubMqttIvData::stateFindNxtIv;
|
|
mTable[SEND_DATA] = &PubMqttIvData::stateSend;
|
|
mTable[SEND_TOTALS] = &PubMqttIvData::stateSendTotals;
|
|
}
|
|
|
|
void loop() {
|
|
(this->*mTable[mState])();
|
|
yield();
|
|
}
|
|
|
|
bool start(bool zeroValues = false) {
|
|
if(IDLE != mState)
|
|
return false;
|
|
|
|
mZeroValues = zeroValues;
|
|
mRTRDataHasBeenSent = false;
|
|
mState = START;
|
|
return true;
|
|
}
|
|
|
|
void setPublishFunc(pubMqttPublisherType cb) {
|
|
mPublish = cb;
|
|
}
|
|
|
|
private:
|
|
enum State {IDLE, START, FIND_NXT_IV, SEND_DATA, SEND_TOTALS, NUM_STATES};
|
|
typedef void (PubMqttIvData::*StateFunction)();
|
|
|
|
void stateIdle() {
|
|
; // nothing to do
|
|
}
|
|
|
|
void stateStart() {
|
|
mLastIvId = 0;
|
|
mTotalFound = false;
|
|
mSendTotalYd = true;
|
|
mAllTotalFound = true;
|
|
if(!mSendList->empty()) {
|
|
mCmd = mSendList->front().cmd;
|
|
mIvSend = mSendList->front().iv;
|
|
|
|
if((RealTimeRunData_Debug != mCmd) || !mRTRDataHasBeenSent) { // send RealTimeRunData only once
|
|
mSendTotals = (RealTimeRunData_Debug == mCmd);
|
|
memset(mTotal, 0, sizeof(float) * 4);
|
|
mState = FIND_NXT_IV;
|
|
} else
|
|
mSendList->pop();
|
|
} else
|
|
mState = IDLE;
|
|
}
|
|
|
|
void stateFindNxtIv() {
|
|
bool found = false;
|
|
|
|
for (; mLastIvId < mSys->getNumInverters(); mLastIvId++) {
|
|
mIv = mSys->getInverterByPos(mLastIvId);
|
|
if (NULL != mIv) {
|
|
if (mIv->config->enabled) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
mLastIvId++;
|
|
|
|
mPos = 0;
|
|
if(found) {
|
|
record_t<> *rec = mIv->getRecordStruct(mCmd);
|
|
if((RealTimeRunData_Debug == mCmd) && mIv->getLastTs(rec) != 0 ) { //workaround for startup. Suspect, mCmd might cause to much messages....
|
|
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/last_success", mIv->config->name);
|
|
snprintf(mVal, 40, "%d", mIv->getLastTs(rec));
|
|
mPublish(mSubTopic, mVal, true, QOS_0);
|
|
|
|
if((mIv->ivGen == IV_HMS) || (mIv->ivGen == IV_HMT)) {
|
|
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/ch0/rssi", mIv->config->name);
|
|
snprintf(mVal, 40, "%d", mIv->rssi);
|
|
mPublish(mSubTopic, mVal, false, QOS_0);
|
|
}
|
|
}
|
|
|
|
mIv->isProducing(); // recalculate status
|
|
mState = SEND_DATA;
|
|
} else if(mSendTotals && mTotalFound)
|
|
mState = SEND_TOTALS;
|
|
else {
|
|
mSendList->pop();
|
|
mZeroValues = false;
|
|
mState = START;
|
|
}
|
|
}
|
|
|
|
void stateSend() {
|
|
record_t<> *rec = mIv->getRecordStruct(mCmd);
|
|
if(rec == NULL) {
|
|
if (mCmd != GetLossRate)
|
|
DPRINT(DBG_WARN, "unknown record to publish!");
|
|
return;
|
|
}
|
|
uint32_t lastTs = mIv->getLastTs(rec);
|
|
bool pubData = (lastTs > 0);
|
|
if (mCmd == RealTimeRunData_Debug)
|
|
pubData &= (lastTs != mIvLastRTRpub[mIv->id]);
|
|
|
|
if (pubData) {
|
|
if(mPos < rec->length) {
|
|
bool retained = false;
|
|
if (mCmd == RealTimeRunData_Debug) {
|
|
if((FLD_YT == rec->assign[mPos].fieldId) || (FLD_YD == rec->assign[mPos].fieldId))
|
|
retained = true;
|
|
|
|
// calculate total values for RealTimeRunData_Debug
|
|
if (CH0 == rec->assign[mPos].ch) {
|
|
if(mIv->getStatus() > InverterStatus::STARTING) {
|
|
if(mIv->config->add2Total) {
|
|
mTotalFound = true;
|
|
switch (rec->assign[mPos].fieldId) {
|
|
case FLD_PAC:
|
|
mTotal[0] += mIv->getValue(mPos, rec);
|
|
break;
|
|
case FLD_YT:
|
|
mTotal[1] += mIv->getValue(mPos, rec);
|
|
break;
|
|
case FLD_YD: {
|
|
float val = mIv->getValue(mPos, rec);
|
|
if(0 == val) // inverter restarted during day
|
|
mSendTotalYd = false;
|
|
else
|
|
mTotal[2] += val;
|
|
break;
|
|
}
|
|
case FLD_PDC:
|
|
mTotal[3] += mIv->getValue(mPos, rec);
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
mAllTotalFound = false;
|
|
}
|
|
} else
|
|
mIvLastRTRpub[mIv->id] = lastTs;
|
|
|
|
uint8_t qos = QOS_0;
|
|
if(FLD_ACT_ACTIVE_PWR_LIMIT == rec->assign[mPos].fieldId)
|
|
qos = QOS_2;
|
|
|
|
if((mIvSend == mIv) || (NULL == mIvSend)) { // send only updated values, or all if the inverter is NULL
|
|
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/ch%d/%s", mIv->config->name, rec->assign[mPos].ch, fields[rec->assign[mPos].fieldId]);
|
|
snprintf(mVal, 40, "%g", ah::round3(mIv->getValue(mPos, rec)));
|
|
mPublish(mSubTopic, mVal, retained, qos);
|
|
}
|
|
mPos++;
|
|
} else {
|
|
sendRadioStat(rec->length);
|
|
mState = FIND_NXT_IV;
|
|
}
|
|
} else
|
|
mState = FIND_NXT_IV;
|
|
}
|
|
|
|
inline void sendRadioStat(uint8_t start) {
|
|
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/radio_stat", mIv->config->name);
|
|
snprintf(mVal, 140, "{\"tx\":%d,\"success\":%d,\"fail\":%d,\"no_answer\":%d,\"retransmits\":%d,\"lossIvRx\":%d,\"lossIvTx\":%d,\"lossDtuRx\":%d,\"lossDtuTx\":%d}",
|
|
mIv->radioStatistics.txCnt,
|
|
mIv->radioStatistics.rxSuccess,
|
|
mIv->radioStatistics.rxFail,
|
|
mIv->radioStatistics.rxFailNoAnser,
|
|
mIv->radioStatistics.retransmits,
|
|
mIv->radioStatistics.ivLoss,
|
|
mIv->radioStatistics.ivSent,
|
|
mIv->radioStatistics.dtuLoss,
|
|
mIv->radioStatistics.dtuSent);
|
|
mPublish(mSubTopic, mVal, false, QOS_0);
|
|
}
|
|
|
|
void stateSendTotals() {
|
|
uint8_t fieldId;
|
|
mRTRDataHasBeenSent = true;
|
|
if(mPos < 4) {
|
|
bool retained = true;
|
|
switch (mPos) {
|
|
default:
|
|
case 0:
|
|
fieldId = FLD_PAC;
|
|
retained = false;
|
|
break;
|
|
case 1:
|
|
if(!mAllTotalFound) {
|
|
mPos++;
|
|
return;
|
|
}
|
|
fieldId = FLD_YT;
|
|
break;
|
|
case 2:
|
|
if((!mAllTotalFound) || (!mSendTotalYd)) {
|
|
mPos++;
|
|
return;
|
|
}
|
|
fieldId = FLD_YD;
|
|
break;
|
|
case 3:
|
|
fieldId = FLD_PDC;
|
|
retained = false;
|
|
break;
|
|
}
|
|
snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "total/%s", fields[fieldId]);
|
|
snprintf(mVal, 40, "%g", ah::round3(mTotal[mPos]));
|
|
mPublish(mSubTopic, mVal, retained, QOS_0);
|
|
mPos++;
|
|
} else {
|
|
mSendList->pop();
|
|
mZeroValues = false;
|
|
mPos = 0;
|
|
mState = IDLE;
|
|
}
|
|
}
|
|
|
|
HMSYSTEM *mSys;
|
|
uint32_t *mUtcTimestamp;
|
|
pubMqttPublisherType mPublish;
|
|
State mState;
|
|
StateFunction mTable[NUM_STATES];
|
|
|
|
uint8_t mCmd;
|
|
uint8_t mLastIvId;
|
|
bool mSendTotals, mTotalFound, mAllTotalFound, mSendTotalYd;
|
|
float mTotal[4];
|
|
|
|
Inverter<> *mIv, *mIvSend;
|
|
uint8_t mPos;
|
|
uint32_t mIvLastRTRpub[MAX_NUM_INVERTERS];
|
|
bool mRTRDataHasBeenSent;
|
|
|
|
char mSubTopic[32 + MAX_NAME_LENGTH + 1];
|
|
char mVal[140];
|
|
bool mZeroValues; // makes sure that yield day is sent even if no inverter is online
|
|
|
|
std::queue<sendListCmdIv> *mSendList;
|
|
};
|
|
|
|
#endif /*__PUB_MQTT_IV_DATA_H__*/
|
|
|