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.
 
 
 
 
 
 

295 lines
11 KiB

//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------
#ifndef __HISTORY_DATA_H__
#define __HISTORY_DATA_H__
#if defined(ENABLE_HISTORY)
#include <array>
#include "../appInterface.h"
#include "../hm/hmSystem.h"
#include "../utils/helper.h"
#define HISTORY_DATA_ARR_LENGTH 256
enum class HistoryStorageType : uint8_t {
POWER,
POWER_DAY,
YIELD
};
template<class HMSYSTEM>
class HistoryData {
private:
struct storage_t {
uint16_t refreshCycle = 0;
uint16_t loopCnt;
uint16_t listIdx; // index for next Element to write into WattArr
// ring buffer for watt history
std::array<uint16_t, (HISTORY_DATA_ARR_LENGTH + 1)> data;
void reset() {
loopCnt = 0;
listIdx = 0;
data.fill(0);
}
};
public:
void setup(IApp *app, HMSYSTEM *sys, settings_t *config, uint32_t *ts) {
mApp = app;
mSys = sys;
mConfig = config;
mTs = ts;
mCurPwr.reset();
mCurPwr.refreshCycle = mConfig->inst.sendInterval;
mCurPwrDay.reset();
mCurPwrDay.refreshCycle = mConfig->inst.sendInterval;
mYieldDay.reset();
mYieldDay.refreshCycle = 60;
mLastValueTs = 0;
mPgPeriod=0;
mMaximumDay = 0;
}
void tickerSecond() {
float curPwr = 0;
//float maxPwr = 0;
float yldDay = -0.1;
uint32_t ts = 0;
for (uint8_t i = 0; i < mSys->getNumInverters(); i++) {
Inverter<> *iv = mSys->getInverterByPos(i);
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
if (iv == NULL)
continue;
curPwr += iv->getChannelFieldValue(CH0, FLD_PAC, rec);
//maxPwr += iv->getChannelFieldValue(CH0, FLD_MP, rec);
yldDay += iv->getChannelFieldValue(CH0, FLD_YD, rec);
if (rec->ts > ts)
ts = rec->ts;
}
if ((++mCurPwr.loopCnt % mCurPwr.refreshCycle) == 0) {
mCurPwr.loopCnt = 0;
if (curPwr > 0) {
mLastValueTs = ts;
addValue(&mCurPwr, roundf(curPwr));
if (curPwr > mMaximumDay)
mMaximumDay = roundf(curPwr);
}
//if (maxPwr > 0)
// mMaximumDay = roundf(maxPwr);
}
if ((++mCurPwrDay.loopCnt % mCurPwrDay.refreshCycle) == 0) {
mCurPwrDay.loopCnt = 0;
if (curPwr > 0) {
mLastValueTs = ts;
addValueDay(&mCurPwrDay, roundf(curPwr));
}
}
if((++mYieldDay.loopCnt % mYieldDay.refreshCycle) == 0) {
mYieldDay.loopCnt = 0;
if (*mTs > mApp->getSunset())
{
if ((!mDayStored) && (yldDay > 0)) {
addValue(&mYieldDay, roundf(yldDay));
mDayStored = true;
}
}
else if (*mTs > mApp->getSunrise())
mDayStored = false;
}
}
uint16_t valueAt(HistoryStorageType type, uint16_t i) {
storage_t *s=NULL;
uint16_t idx=i;
DPRINTLN(DBG_VERBOSE, F("valueAt ") + String((uint8_t)type) + " i=" + String(i));
switch (type) {
case HistoryStorageType::POWER:
s = &mCurPwr;
idx = (s->listIdx + i) % HISTORY_DATA_ARR_LENGTH;
break;
case HistoryStorageType::POWER_DAY:
s = &mCurPwrDay;
idx = i;
break;
case HistoryStorageType::YIELD:
s = &mYieldDay;
idx = (s->listIdx + i) % HISTORY_DATA_ARR_LENGTH;
break;
}
if (s)
return s->data[idx];
return 0;
}
uint16_t getMaximumDay() {
return mMaximumDay;
}
uint32_t getLastValueTs(HistoryStorageType type) {
DPRINTLN(DBG_VERBOSE, F("getLastValueTs ") + String((uint8_t)type));
if (type == HistoryStorageType::POWER_DAY)
return mPgEndTime;
return mLastValueTs;
}
uint32_t getPeriode(HistoryStorageType type) {
DPRINTLN(DBG_VERBOSE, F("getPeriode ") + String((uint8_t)type));
switch (type) {
case HistoryStorageType::POWER:
return mCurPwr.refreshCycle;
break;
case HistoryStorageType::POWER_DAY:
return mPgPeriod / HISTORY_DATA_ARR_LENGTH;
break;
case HistoryStorageType::YIELD:
return (60 * 60 * 24); // 1 day
break;
}
return 0;
}
#if defined(ENABLE_HISTORY_LOAD_DATA)
/* For filling data from outside */
void addValue(HistoryStorageType historyType, uint8_t valueType, uint32_t value) {
if (valueType<2) {
storage_t *s=NULL;
switch (historyType) {
case HistoryStorageType::POWER:
s = &mCurPwr;
break;
case HistoryStorageType::POWER_DAY:
s = &mCurPwrDay;
break;
case HistoryStorageType::YIELD:
s = &mYieldDay;
break;
}
if (s)
{
if (valueType==0)
addValue(s, value);
if (valueType==1)
{
if (historyType == HistoryStorageType::POWER)
s->refreshCycle = value;
if (historyType == HistoryStorageType::POWER_DAY)
mPgPeriod = value * HISTORY_DATA_ARR_LENGTH;
}
}
return;
}
if (valueType == 2)
{
if (historyType == HistoryStorageType::POWER)
mLastValueTs = value;
if (historyType == HistoryStorageType::POWER_DAY)
mPgEndTime = value;
}
}
#endif
private:
void addValue(storage_t *s, uint16_t value) {
s->data[s->listIdx] = value;
s->listIdx = (s->listIdx + 1) % (HISTORY_DATA_ARR_LENGTH);
}
void addValueDay(storage_t *s, uint16_t value) {
DPRINTLN(DBG_VERBOSE, F("addValueDay ") + String(value));
bool storeStartEndTimes = false;
bool store_entry = false;
uint32_t pGraphStartTime = mApp->getSunrise();
uint32_t pGraphEndTime = mApp->getSunset();
uint32_t utcTs = mApp->getTimestamp();
switch (mPgState) {
case PowerGraphState::NO_TIME_SYNC:
if ((pGraphStartTime > 0)
&& (pGraphEndTime > 0) // wait until period data is available ...
&& (utcTs >= pGraphStartTime)
&& (utcTs < pGraphEndTime)) // and current time is in period
{
storeStartEndTimes = true; // period was received -> store
store_entry = true;
mPgState = PowerGraphState::IN_PERIOD;
}
break;
case PowerGraphState::IN_PERIOD:
if (utcTs > mPgEndTime) // check if end of day is reached ...
mPgState = PowerGraphState::WAIT_4_NEW_PERIOD; // then wait for new period setting
else
store_entry = true;
break;
case PowerGraphState::WAIT_4_NEW_PERIOD:
if ((mPgStartTime != pGraphStartTime) || (mPgEndTime != pGraphEndTime)) { // wait until new time period was received ...
storeStartEndTimes = true; // and store it for next period
mPgState = PowerGraphState::WAIT_4_RESTART;
}
break;
case PowerGraphState::WAIT_4_RESTART:
if ((utcTs >= mPgStartTime) && (utcTs < mPgEndTime)) { // wait until current time is in period again ...
mCurPwrDay.reset(); // then reset power graph data
store_entry = true;
mPgState = PowerGraphState::IN_PERIOD;
mCurPwr.reset(); // also reset "last values" graph
mMaximumDay = 0; // and the maximum of the (last) day
}
break;
}
// store start and end times of current time period and calculate period length
if (storeStartEndTimes) {
mPgStartTime = pGraphStartTime;
mPgEndTime = pGraphEndTime;
mPgPeriod = pGraphEndTime - pGraphStartTime; // time period of power graph in sec for scaling of x-axis
}
if (store_entry) {
DPRINTLN(DBG_VERBOSE, F("addValueDay store_entry") + String(value));
if (mPgPeriod) {
uint16_t pgPos = (utcTs - mPgStartTime) * (HISTORY_DATA_ARR_LENGTH - 1) / mPgPeriod;
s->listIdx = std::min(pgPos, (uint16_t)(HISTORY_DATA_ARR_LENGTH - 1));
} else
s->listIdx = 0;
DPRINTLN(DBG_VERBOSE, F("addValueDay store_entry idx=") + String(s->listIdx));
s->data[s->listIdx] = std::max(s->data[s->listIdx], value); // update current datapoint to maximum of all seen values
}
}
private:
IApp *mApp = nullptr;
HMSYSTEM *mSys = nullptr;
settings *mSettings = nullptr;
settings_t *mConfig = nullptr;
uint32_t *mTs = nullptr;
storage_t mCurPwr;
storage_t mCurPwrDay;
storage_t mYieldDay;
bool mDayStored = false;
uint16_t mMaximumDay = 0;
uint32_t mLastValueTs = 0;
enum class PowerGraphState {
NO_TIME_SYNC,
IN_PERIOD,
WAIT_4_NEW_PERIOD,
WAIT_4_RESTART
};
PowerGraphState mPgState = PowerGraphState::NO_TIME_SYNC;
uint32_t mPgStartTime = 0;
uint32_t mPgEndTime = 0;
uint32_t mPgPeriod = 0; // seconds
};
#endif /*ENABLE_HISTORY*/
#endif /*__HISTORY_DATA_H__*/