Add totals to balance

This commit is contained in:
Michael Zanetti 2021-11-22 00:43:36 +01:00
parent d684016498
commit 034f90d7a9
8 changed files with 239 additions and 39 deletions

View File

@ -12,12 +12,16 @@ PowerBalanceLogEntry::PowerBalanceLogEntry()
} }
PowerBalanceLogEntry::PowerBalanceLogEntry(const QDateTime &timestamp, double consumption, double production, double acquisition, double storage): PowerBalanceLogEntry::PowerBalanceLogEntry(const QDateTime &timestamp, double consumption, double production, double acquisition, double storage, double totalConsumption, double totalProduction, double totalAcquisition, double totalReturn):
m_timestamp(timestamp), m_timestamp(timestamp),
m_consumption(consumption), m_consumption(consumption),
m_production(production), m_production(production),
m_acquisition(acquisition), m_acquisition(acquisition),
m_storage(storage) m_storage(storage),
m_totalConsumption(totalConsumption),
m_totalProduction(totalProduction),
m_totalAcquisition(totalAcquisition),
m_totalReturn(totalReturn)
{ {
} }
@ -47,6 +51,26 @@ double PowerBalanceLogEntry::storage() const
return m_storage; return m_storage;
} }
double PowerBalanceLogEntry::totalConsumption() const
{
return m_totalConsumption;
}
double PowerBalanceLogEntry::totalProduction() const
{
return m_totalProduction;
}
double PowerBalanceLogEntry::totalAcquisition() const
{
return m_totalAcquisition;
}
double PowerBalanceLogEntry::totalReturn() const
{
return m_totalReturn;
}
QVariant PowerBalanceLogEntries::get(int index) const QVariant PowerBalanceLogEntries::get(int index) const
{ {
return QVariant::fromValue(at(index)); return QVariant::fromValue(at(index));

View File

@ -20,6 +20,7 @@ public:
virtual ~EnergyLogs() = default; virtual ~EnergyLogs() = default;
enum SampleRate { enum SampleRate {
SampleRateAny = 0,
SampleRate1Min = 1, SampleRate1Min = 1,
SampleRate15Mins = 15, SampleRate15Mins = 15,
SampleRate1Hour = 60, SampleRate1Hour = 60,
@ -57,20 +58,33 @@ class PowerBalanceLogEntry
Q_PROPERTY(double production READ production) Q_PROPERTY(double production READ production)
Q_PROPERTY(double acquisition READ acquisition) Q_PROPERTY(double acquisition READ acquisition)
Q_PROPERTY(double storage READ storage) Q_PROPERTY(double storage READ storage)
Q_PROPERTY(double totalConsumption READ totalConsumption)
Q_PROPERTY(double totalProduction READ totalProduction)
Q_PROPERTY(double totalAcquisition READ totalAcquisition)
Q_PROPERTY(double totalReturn READ totalReturn)
public: public:
PowerBalanceLogEntry(); PowerBalanceLogEntry();
PowerBalanceLogEntry(const QDateTime &timestamp, double consumption, double production, double acquisition, double storage); PowerBalanceLogEntry(const QDateTime &timestamp, double consumption, double production, double acquisition, double storage, double totalConsumption, double totalProduction, double totalAcquisition, double totalReturn);
QDateTime timestamp() const; QDateTime timestamp() const;
double consumption() const; double consumption() const;
double production() const; double production() const;
double acquisition() const; double acquisition() const;
double storage() const; double storage() const;
double totalConsumption() const;
double totalProduction() const;
double totalAcquisition() const;
double totalReturn() const;
private: private:
QDateTime m_timestamp; QDateTime m_timestamp;
double m_consumption = 0; double m_consumption = 0;
double m_production = 0; double m_production = 0;
double m_acquisition = 0; double m_acquisition = 0;
double m_storage = 0; double m_storage = 0;
double m_totalConsumption = 0;
double m_totalProduction = 0;
double m_totalAcquisition = 0;
double m_totalReturn = 0;
}; };
Q_DECLARE_METATYPE(PowerBalanceLogEntry) Q_DECLARE_METATYPE(PowerBalanceLogEntry)

View File

@ -61,6 +61,10 @@ public:
virtual double currentPowerProduction() const = 0; virtual double currentPowerProduction() const = 0;
virtual double currentPowerAcquisition() const = 0; virtual double currentPowerAcquisition() const = 0;
virtual double currentPowerStorage() const = 0; virtual double currentPowerStorage() const = 0;
virtual double totalConsumption() const = 0;
virtual double totalProduction() const = 0;
virtual double totalAcquisition() const = 0;
virtual double totalReturn() const = 0;
virtual EnergyLogs* logs() const = 0; virtual EnergyLogs* logs() const = 0;

View File

@ -33,6 +33,10 @@ EnergyJsonHandler::EnergyJsonHandler(EnergyManager *energyManager, QObject *pare
returns.insert("currentPowerProduction", enumValueName(Double)); returns.insert("currentPowerProduction", enumValueName(Double));
returns.insert("currentPowerAcquisition", enumValueName(Double)); returns.insert("currentPowerAcquisition", enumValueName(Double));
returns.insert("currentPowerStorage", enumValueName(Double)); returns.insert("currentPowerStorage", enumValueName(Double));
returns.insert("totalConsumption", enumValueName(Double));
returns.insert("totalProduction", enumValueName(Double));
returns.insert("totalAcquisition", enumValueName(Double));
returns.insert("totalReturn", enumValueName(Double));
registerMethod("GetPowerBalance", description, params, returns); registerMethod("GetPowerBalance", description, params, returns);
params.clear(); returns.clear(); params.clear(); returns.clear();
@ -67,6 +71,10 @@ EnergyJsonHandler::EnergyJsonHandler(EnergyManager *energyManager, QObject *pare
params.insert("currentPowerProduction", enumValueName(Double)); params.insert("currentPowerProduction", enumValueName(Double));
params.insert("currentPowerAcquisition", enumValueName(Double)); params.insert("currentPowerAcquisition", enumValueName(Double));
params.insert("currentPowerStorage", enumValueName(Double)); params.insert("currentPowerStorage", enumValueName(Double));
params.insert("totalConsumption", enumValueName(Double));
params.insert("totalProduction", enumValueName(Double));
params.insert("totalAcquisition", enumValueName(Double));
params.insert("totalReturn", enumValueName(Double));
registerNotification("PowerBalanceChanged", description, params); registerNotification("PowerBalanceChanged", description, params);
params.clear(); params.clear();
@ -95,6 +103,10 @@ EnergyJsonHandler::EnergyJsonHandler(EnergyManager *energyManager, QObject *pare
params.insert("currentPowerProduction", m_energyManager->currentPowerProduction()); params.insert("currentPowerProduction", m_energyManager->currentPowerProduction());
params.insert("currentPowerAcquisition", m_energyManager->currentPowerAcquisition()); params.insert("currentPowerAcquisition", m_energyManager->currentPowerAcquisition());
params.insert("currentPowerStorage", m_energyManager->currentPowerStorage()); params.insert("currentPowerStorage", m_energyManager->currentPowerStorage());
params.insert("totalConsumption", m_energyManager->totalConsumption());
params.insert("totalProduction", m_energyManager->totalProduction());
params.insert("totalAcquisition", m_energyManager->totalAcquisition());
params.insert("totalReturn", m_energyManager->totalReturn());
emit PowerBalanceChanged(params); emit PowerBalanceChanged(params);
}); });
@ -149,16 +161,17 @@ JsonReply *EnergyJsonHandler::GetPowerBalance(const QVariantMap &params)
ret.insert("currentPowerProduction", m_energyManager->currentPowerProduction()); ret.insert("currentPowerProduction", m_energyManager->currentPowerProduction());
ret.insert("currentPowerAcquisition", m_energyManager->currentPowerAcquisition()); ret.insert("currentPowerAcquisition", m_energyManager->currentPowerAcquisition());
ret.insert("currentPowerStorage", m_energyManager->currentPowerStorage()); ret.insert("currentPowerStorage", m_energyManager->currentPowerStorage());
ret.insert("totalConsumption", m_energyManager->totalConsumption());
ret.insert("totalProduction", m_energyManager->totalProduction());
ret.insert("totalAcquisition", m_energyManager->totalAcquisition());
ret.insert("totalReturn", m_energyManager->totalReturn());
return createReply(ret); return createReply(ret);
} }
JsonReply *EnergyJsonHandler::GetPowerBalanceLogs(const QVariantMap &params) JsonReply *EnergyJsonHandler::GetPowerBalanceLogs(const QVariantMap &params)
{ {
qCDebug(dcEnergyExperience()) << "params" << params;
qCDebug(dcEnergyExperience()) << "from" << params.value("from");
EnergyLogs::SampleRate sampleRate = enumNameToValue<EnergyLogs::SampleRate>(params.value("sampleRate").toString()); EnergyLogs::SampleRate sampleRate = enumNameToValue<EnergyLogs::SampleRate>(params.value("sampleRate").toString());
QDateTime from = params.contains("from") ? QDateTime::fromMSecsSinceEpoch(params.value("from").toLongLong() * 1000) : QDateTime(); QDateTime from = params.contains("from") ? QDateTime::fromMSecsSinceEpoch(params.value("from").toLongLong() * 1000) : QDateTime();
qCDebug(dcEnergyExperience()) << "from2" << from;
QDateTime to = params.contains("to") ? QDateTime::fromMSecsSinceEpoch(params.value("to").toLongLong() * 1000) : QDateTime(); QDateTime to = params.contains("to") ? QDateTime::fromMSecsSinceEpoch(params.value("to").toLongLong() * 1000) : QDateTime();
QVariantMap returns; QVariantMap returns;
returns.insert("powerBalanceLogEntries", pack(m_energyManager->logs()->powerBalanceLogs(sampleRate, from, to))); returns.insert("powerBalanceLogEntries", pack(m_energyManager->logs()->powerBalanceLogs(sampleRate, from, to)));

View File

@ -6,6 +6,7 @@
#include <QDir> #include <QDir>
#include <QSqlQuery> #include <QSqlQuery>
#include <QSqlError> #include <QSqlError>
#include <QSqlRecord>
#include <QSettings> #include <QSettings>
#include <QLoggingCategory> #include <QLoggingCategory>
@ -53,7 +54,6 @@ EnergyLogger::EnergyLogger(QObject *parent) : EnergyLogs(parent)
} }
// Start the scheduling // Start the scheduling
scheduleNextSample(SampleRate1Min); scheduleNextSample(SampleRate1Min);
foreach (SampleRate sampleRate, m_configs.keys()) { foreach (SampleRate sampleRate, m_configs.keys()) {
scheduleNextSample(sampleRate); scheduleNextSample(sampleRate);
@ -71,9 +71,9 @@ EnergyLogger::EnergyLogger(QObject *parent) : EnergyLogs(parent)
m_sampleTimer.start(1000); m_sampleTimer.start(1000);
} }
void EnergyLogger::logPowerBalance(double consumption, double production, double acquisition, double storage) void EnergyLogger::logPowerBalance(double consumption, double production, double acquisition, double storage, double totalConsumption, double totalProduction, double totalAcquisition, double totalReturn)
{ {
PowerBalanceLogEntry entry(QDateTime::currentDateTime(), consumption, production, acquisition, storage); PowerBalanceLogEntry entry(QDateTime::currentDateTime(), consumption, production, acquisition, storage, totalConsumption, totalProduction, totalAcquisition, totalReturn);
// Add everything to livelog, keep that for one day, in memory only // Add everything to livelog, keep that for one day, in memory only
m_balanceLiveLog.prepend(entry); m_balanceLiveLog.prepend(entry);
@ -124,8 +124,8 @@ PowerBalanceLogEntries EnergyLogger::powerBalanceLogs(SampleRate sampleRate, con
} }
while (query.next()) { while (query.next()) {
qCDebug(dcEnergyExperience()) << "Adding result"; // qCDebug(dcEnergyExperience()) << "Adding result";
result.append(PowerBalanceLogEntry(QDateTime::fromMSecsSinceEpoch(query.value("timestamp").toLongLong()), query.value("consumption").toDouble(), query.value("production").toDouble(), query.value("acquisition").toDouble(), query.value("storage").toDouble())); result.append(queryResultToBalanceLogEntry(query.record()));
} }
return result; return result;
} }
@ -178,6 +178,33 @@ ThingPowerLogEntries EnergyLogger::thingPowerLogs(SampleRate sampleRate, const Q
} }
PowerBalanceLogEntry EnergyLogger::latestLogEntry(SampleRate sampleRate)
{
QSqlQuery query(m_db);
QString queryString = "SELECT MAX(timestamp), consumption, production, acquisition, storage, totalConsumption, totalProduction, totalAcquisition, totalReturn FROM powerBalance";
QVariantList bindValues;
if (sampleRate != SampleRateAny) {
queryString += " WHERE sampleRate = ?";
bindValues.append(sampleRate);
}
queryString += ";";
query.prepare(queryString);
foreach (const QVariant &value, bindValues) {
query.addBindValue(value);
}
query.exec();
if (query.lastError().isValid()) {
qCWarning(dcEnergyExperience()) << "Error obtaining latest log entry from DB:" << query.lastError() << query.executedQuery();
return PowerBalanceLogEntry();
}
if (!query.next()) {
qCDebug(dcEnergyExperience()) << "No power balance log entry in DB for sample rate:" << sampleRate;
return PowerBalanceLogEntry();
}
qCDebug(dcEnergyExperience()) << "Loaded latest log entry:" << query.record();
return queryResultToBalanceLogEntry(query.record());
}
void EnergyLogger::sample() void EnergyLogger::sample()
{ {
QDateTime now = QDateTime::currentDateTime(); QDateTime now = QDateTime::currentDateTime();
@ -210,8 +237,19 @@ void EnergyLogger::sample()
medianProduction /= sampleStart.msecsTo(sampleEnd); medianProduction /= sampleStart.msecsTo(sampleEnd);
medianAcquisition /= sampleStart.msecsTo(sampleEnd); medianAcquisition /= sampleStart.msecsTo(sampleEnd);
medianStorage /= sampleStart.msecsTo(sampleEnd); medianStorage /= sampleStart.msecsTo(sampleEnd);
PowerBalanceLogEntry newest = m_balanceLiveLog.count() > 0 ? m_balanceLiveLog.at(0) : PowerBalanceLogEntry();
double totalConsumption = newest.totalConsumption();
double totalProduction = newest.totalProduction();
double totalAcquisition = newest.totalAcquisition();
double totalReturn = newest.totalReturn();
qCDebug(dcEnergyExperience()) << "Power balance for sample:" << medianConsumption << medianProduction << medianAcquisition << medianStorage << "duration:" << sampleStart.msecsTo(sampleEnd); qCDebug(dcEnergyExperience()) << "Power balance for sample:" << medianConsumption << medianProduction << medianAcquisition << medianStorage << "duration:" << sampleStart.msecsTo(sampleEnd);
insertPowerBalance(sampleEnd, SampleRate1Min, medianConsumption, medianProduction, medianAcquisition, medianStorage); insertPowerBalance(sampleEnd, SampleRate1Min, medianConsumption, medianProduction, medianAcquisition, medianStorage, totalConsumption, totalProduction, totalAcquisition, totalReturn);
m_lastSampleTotalConsumption = totalConsumption;
m_lastSampleTotalProducation = totalProduction;
m_lastSampleTotalAcquisition = totalAcquisition;
m_lastSampleTotalReturn = totalReturn;
foreach (const ThingId &thingId, m_thingsPowerLiveLogs.keys()) { foreach (const ThingId &thingId, m_thingsPowerLiveLogs.keys()) {
medianConsumption = 0; medianConsumption = 0;
@ -307,7 +345,11 @@ bool EnergyLogger::initDB()
"consumption FLOAT," "consumption FLOAT,"
"production FLOAT," "production FLOAT,"
"acquisition FLOAT," "acquisition FLOAT,"
"storage FLOAT" "storage FLOAT,"
"totalConsumption FLOAT,"
"totalProduction FLOAT,"
"totalAcquisition FLOAT,"
"totalReturn FLOAT"
");"); ");");
if (m_db.lastError().isValid()) { if (m_db.lastError().isValid()) {
@ -333,7 +375,7 @@ bool EnergyLogger::initDB()
} }
} }
qCDebug(dcEnergyExperience()) << "Initialized logging DB successfully."; qCDebug(dcEnergyExperience()) << "Initialized logging DB successfully." << m_db.databaseName();
return true; return true;
} }
@ -412,16 +454,16 @@ void EnergyLogger::rectifySamples(SampleRate sampleRate, SampleRate baseSampleRa
QDateTime newestSample = getNewestPowerBalanceSampleTimestamp(sampleRate); QDateTime newestSample = getNewestPowerBalanceSampleTimestamp(sampleRate);
qCDebug(dcEnergyExperience()) << "Checking for missing samples for" << sampleRate; qCDebug(dcEnergyExperience()) << "Checking for missing samples for" << sampleRate;
qCDebug(dcEnergyExperience()) << "Newest sample:" << newestSample.toString() << "Oldest base sample:" << oldestBaseSample.toString(); // qCDebug(dcEnergyExperience()) << "Newest sample:" << newestSample.toString() << "Oldest base sample:" << oldestBaseSample.toString();
if (newestSample.isNull()) { if (newestSample.isNull()) {
qCDebug(dcEnergyExperience()) << "No sample at all so far. Using base as starting point."; // qCDebug(dcEnergyExperience()) << "No sample at all so far. Using base as starting point.";
newestSample = oldestBaseSample; newestSample = oldestBaseSample;
} }
qCDebug(dcEnergyExperience()) << "next sample after last in series:" << nextSampleTimestamp(sampleRate, newestSample).toString(); // qCDebug(dcEnergyExperience()) << "next sample after last in series:" << nextSampleTimestamp(sampleRate, newestSample).toString();
qCDebug(dcEnergyExperience()) << "next scheduled sample:" << m_nextSamples.value(sampleRate).toString(); // qCDebug(dcEnergyExperience()) << "next scheduled sample:" << m_nextSamples.value(sampleRate).toString();
while (!newestSample.isNull() && nextSampleTimestamp(sampleRate, newestSample) < m_nextSamples[sampleRate]) { while (!newestSample.isNull() && nextSampleTimestamp(sampleRate, newestSample) < m_nextSamples[sampleRate]) {
QDateTime nextSample = nextSampleTimestamp(sampleRate, newestSample.addMSecs(1000)); QDateTime nextSample = nextSampleTimestamp(sampleRate, newestSample.addMSecs(1000));
qCDebug(dcEnergyExperience()) << "Rectifying missed sample for" << sampleRate << "from" << nextSample.toString(); // qCDebug(dcEnergyExperience()) << "Rectifying missed sample for" << sampleRate << "from" << nextSample.toString();
samplePowerBalance(sampleRate, baseSampleRate, nextSample); samplePowerBalance(sampleRate, baseSampleRate, nextSample);
newestSample = nextSample; newestSample = nextSample;
} }
@ -430,17 +472,17 @@ void EnergyLogger::rectifySamples(SampleRate sampleRate, SampleRate baseSampleRa
QDateTime oldestBaseSample = getOldestThingPowerSampleTimestamp(thingId, baseSampleRate); QDateTime oldestBaseSample = getOldestThingPowerSampleTimestamp(thingId, baseSampleRate);
QDateTime newestSample = getNewestThingPowerSampleTimestamp(thingId, sampleRate); QDateTime newestSample = getNewestThingPowerSampleTimestamp(thingId, sampleRate);
qCDebug(dcEnergyExperience()) << "T Checking for missing samples for" << sampleRate; // qCDebug(dcEnergyExperience()) << "T Checking for missing samples for" << sampleRate;
qCDebug(dcEnergyExperience()) << "T Newest sample:" << newestSample.toString() << "Oldest base sample:" << oldestBaseSample.toString(); // qCDebug(dcEnergyExperience()) << "T Newest sample:" << newestSample.toString() << "Oldest base sample:" << oldestBaseSample.toString();
if (newestSample.isNull()) { if (newestSample.isNull()) {
qCDebug(dcEnergyExperience()) << "T No sample at all so far. Using base as starting point."; // qCDebug(dcEnergyExperience()) << "T No sample at all so far. Using base as starting point.";
newestSample = oldestBaseSample; newestSample = oldestBaseSample;
} }
qCDebug(dcEnergyExperience()) << "T next sample after last in series:" << nextSampleTimestamp(sampleRate, newestSample).toString(); // qCDebug(dcEnergyExperience()) << "T next sample after last in series:" << nextSampleTimestamp(sampleRate, newestSample).toString();
qCDebug(dcEnergyExperience()) << "T next scheduled sample:" << m_nextSamples.value(sampleRate).toString(); // qCDebug(dcEnergyExperience()) << "T next scheduled sample:" << m_nextSamples.value(sampleRate).toString();
while (!newestSample.isNull() && nextSampleTimestamp(sampleRate, newestSample) < m_nextSamples[sampleRate]) { while (!newestSample.isNull() && nextSampleTimestamp(sampleRate, newestSample) < m_nextSamples[sampleRate]) {
QDateTime nextSample = nextSampleTimestamp(sampleRate, newestSample.addMSecs(1000)); QDateTime nextSample = nextSampleTimestamp(sampleRate, newestSample.addMSecs(1000));
qCDebug(dcEnergyExperience()) << "T Rectifying missed sample for" << sampleRate << "from" << nextSample.toString(); // qCDebug(dcEnergyExperience()) << "T Rectifying missed sample for" << sampleRate << "from" << nextSample.toString();
sampleThingPower(thingId, sampleRate, baseSampleRate, nextSample); sampleThingPower(thingId, sampleRate, baseSampleRate, nextSample);
newestSample = nextSample; newestSample = nextSample;
} }
@ -453,6 +495,9 @@ QDateTime EnergyLogger::nextSampleTimestamp(SampleRate sampleRate, const QDateTi
QDate date = dateTime.date(); QDate date = dateTime.date();
QDateTime next; QDateTime next;
switch (sampleRate) { switch (sampleRate) {
case SampleRateAny:
qCWarning(dcEnergyExperience()) << "Cannot calculate next sample timestamp without a sample rate";
return QDateTime();
case SampleRate1Min: case SampleRate1Min:
time.setHMS(time.hour(), time.minute(), 0); time.setHMS(time.hour(), time.minute(), 0);
next = QDateTime(date, time).addMSecs(60 * 1000); next = QDateTime(date, time).addMSecs(60 * 1000);
@ -512,12 +557,20 @@ bool EnergyLogger::samplePowerBalance(SampleRate sampleRate, SampleRate baseSamp
double medianProduction = 0; double medianProduction = 0;
double medianAcquisition = 0; double medianAcquisition = 0;
double medianStorage = 0; double medianStorage = 0;
double totalConsumption = 0;
double totalProduction = 0;
double totalAcquisition = 0;
double totalReturn = 0;
while (query.next()) { while (query.next()) {
qCDebug(dcEnergyExperience()) << "Frame:" << query.value("consumption").toDouble() << query.value("production").toDouble() << query.value("acquisition").toDouble() << QDateTime::fromMSecsSinceEpoch(query.value("timestamp").toLongLong()).toString(); qCDebug(dcEnergyExperience()) << "Frame:" << query.value("consumption").toDouble() << query.value("production").toDouble() << query.value("acquisition").toDouble() << QDateTime::fromMSecsSinceEpoch(query.value("timestamp").toLongLong()).toString();
medianConsumption += query.value("consumption").toDouble(); medianConsumption += query.value("consumption").toDouble();
medianProduction += query.value("production").toDouble(); medianProduction += query.value("production").toDouble();
medianAcquisition += query.value("acquisition").toDouble(); medianAcquisition += query.value("acquisition").toDouble();
medianStorage += query.value("storage").toDouble(); medianStorage += query.value("storage").toDouble();
totalConsumption = query.value("totalConsumption").toDouble();
totalProduction = query.value("totalProduction").toDouble();
totalAcquisition = query.value("totalAcquisition").toDouble();
totalReturn = query.value("totalReturn").toDouble();
} }
qCDebug(dcEnergyExperience()) << "Totals:" << medianConsumption << medianProduction << medianAcquisition << medianStorage << "base samplerate" << baseSampleRate << "samplerate:" << sampleRate; qCDebug(dcEnergyExperience()) << "Totals:" << medianConsumption << medianProduction << medianAcquisition << medianStorage << "base samplerate" << baseSampleRate << "samplerate:" << sampleRate;
medianConsumption = medianConsumption * baseSampleRate / sampleRate; medianConsumption = medianConsumption * baseSampleRate / sampleRate;
@ -526,25 +579,29 @@ bool EnergyLogger::samplePowerBalance(SampleRate sampleRate, SampleRate baseSamp
medianStorage = medianStorage * baseSampleRate / sampleRate; medianStorage = medianStorage * baseSampleRate / sampleRate;
qCDebug(dcEnergyExperience()) << "Sampled:" << medianConsumption << medianProduction << medianAcquisition << medianStorage; qCDebug(dcEnergyExperience()) << "Sampled:" << medianConsumption << medianProduction << medianAcquisition << medianStorage;
return insertPowerBalance(sampleEnd, sampleRate, medianConsumption, medianProduction, medianAcquisition, medianStorage); return insertPowerBalance(sampleEnd, sampleRate, medianConsumption, medianProduction, medianAcquisition, medianStorage, totalConsumption, totalProduction, totalAcquisition, totalReturn);
} }
bool EnergyLogger::insertPowerBalance(const QDateTime &timestamp, SampleRate sampleRate, double consumption, double production, double acquisition, double storage) bool EnergyLogger::insertPowerBalance(const QDateTime &timestamp, SampleRate sampleRate, double consumption, double production, double acquisition, double storage, double totalConsumption, double totalProduction, double totalAcquisition, double totalReturn)
{ {
QSqlQuery query = QSqlQuery(m_db); QSqlQuery query = QSqlQuery(m_db);
query.prepare("INSERT INTO powerBalance (timestamp, sampleRate, consumption, production, acquisition, storage) values (?, ?, ?, ?, ?, ?);"); query.prepare("INSERT INTO powerBalance (timestamp, sampleRate, consumption, production, acquisition, storage, totalConsumption, totalProduction, totalAcquisition, totalReturn) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
query.addBindValue(timestamp.toMSecsSinceEpoch()); query.addBindValue(timestamp.toMSecsSinceEpoch());
query.addBindValue(sampleRate); query.addBindValue(sampleRate);
query.addBindValue(consumption); query.addBindValue(consumption);
query.addBindValue(production); query.addBindValue(production);
query.addBindValue(acquisition); query.addBindValue(acquisition);
query.addBindValue(storage); query.addBindValue(storage);
query.addBindValue(totalConsumption);
query.addBindValue(totalProduction);
query.addBindValue(totalAcquisition);
query.addBindValue(totalReturn);
query.exec(); query.exec();
if (query.lastError().isValid()) { if (query.lastError().isValid()) {
qCWarning(dcEnergyExperience()) << "Error logging consumption sample:" << query.lastError(); qCWarning(dcEnergyExperience()) << "Error logging consumption sample:" << query.lastError();
return false; return false;
} }
emit powerBalanceEntryAdded(sampleRate, PowerBalanceLogEntry(timestamp, consumption, production, acquisition, storage)); emit powerBalanceEntryAdded(sampleRate, PowerBalanceLogEntry(timestamp, consumption, production, acquisition, storage, totalConsumption, totalProduction, totalAcquisition, totalReturn));
return true; return true;
} }
@ -635,3 +692,17 @@ void EnergyLogger::trimThingPower(const ThingId &thingId, SampleRate sampleRate,
qCDebug(dcEnergyExperience()).nospace() << "Trimmed " << query.numRowsAffected() << " from thing power series for: " << thingId << sampleRate << " (Older than: " << beforeTime.toString() << ")"; qCDebug(dcEnergyExperience()).nospace() << "Trimmed " << query.numRowsAffected() << " from thing power series for: " << thingId << sampleRate << " (Older than: " << beforeTime.toString() << ")";
} }
} }
PowerBalanceLogEntry EnergyLogger::queryResultToBalanceLogEntry(const QSqlRecord &record) const
{
return PowerBalanceLogEntry(QDateTime::fromMSecsSinceEpoch(record.value("timestamp").toLongLong()),
record.value("consumption").toDouble(),
record.value("production").toDouble(),
record.value("acquisition").toDouble(),
record.value("storage").toDouble(),
record.value("totalConsumption").toDouble(),
record.value("totalProduction").toDouble(),
record.value("totalAcquisition").toDouble(),
record.value("totalReturn").toDouble());
}

View File

@ -8,6 +8,7 @@
#include <QObject> #include <QObject>
#include <QDateTime> #include <QDateTime>
#include <QSqlDatabase> #include <QSqlDatabase>
#include <QSqlResult>
#include <QTimer> #include <QTimer>
#include <QMap> #include <QMap>
@ -17,12 +18,14 @@ class EnergyLogger : public EnergyLogs
public: public:
explicit EnergyLogger(QObject *parent = nullptr); explicit EnergyLogger(QObject *parent = nullptr);
void logPowerBalance(double consumption, double production, double acquisition, double storage); void logPowerBalance(double consumption, double production, double acquisition, double storage, double totalConsumption, double totalProduction, double totalAcquisition, double totalReturn);
void logThingPower(const ThingId &thingId, double currentPower, double totalConsumption, double totalProduction); void logThingPower(const ThingId &thingId, double currentPower, double totalConsumption, double totalProduction);
PowerBalanceLogEntries powerBalanceLogs(SampleRate sampleRate, const QDateTime &from = QDateTime(), const QDateTime &to = QDateTime()) const override; PowerBalanceLogEntries powerBalanceLogs(SampleRate sampleRate, const QDateTime &from = QDateTime(), const QDateTime &to = QDateTime()) const override;
ThingPowerLogEntries thingPowerLogs(SampleRate sampleRate, const QList<ThingId> &thingIds, const QDateTime &from = QDateTime(), const QDateTime &to = QDateTime()) const override; ThingPowerLogEntries thingPowerLogs(SampleRate sampleRate, const QList<ThingId> &thingIds, const QDateTime &from = QDateTime(), const QDateTime &to = QDateTime()) const override;
PowerBalanceLogEntry latestLogEntry(SampleRate sampleRate);
private slots: private slots:
void sample(); void sample();
@ -40,13 +43,15 @@ private:
void rectifySamples(SampleRate sampleRate, EnergyLogger::SampleRate baseSampleRate); void rectifySamples(SampleRate sampleRate, EnergyLogger::SampleRate baseSampleRate);
bool samplePowerBalance(SampleRate sampleRate, SampleRate baseSampleRate, const QDateTime &sampleEnd); bool samplePowerBalance(SampleRate sampleRate, SampleRate baseSampleRate, const QDateTime &sampleEnd);
bool insertPowerBalance(const QDateTime &timestamp, SampleRate sampleRate, double consumption, double production, double acquisition, double storage); bool insertPowerBalance(const QDateTime &timestamp, SampleRate sampleRate, double consumption, double production, double acquisition, double storage, double totalConsumption, double totalProduction, double totalAcquisition, double totalReturn);
bool sampleThingsPower(SampleRate sampleRate, SampleRate baseSampleRate, const QDateTime &sampleEnd); bool sampleThingsPower(SampleRate sampleRate, SampleRate baseSampleRate, const QDateTime &sampleEnd);
bool sampleThingPower(const ThingId &thingId, SampleRate sampleRate, SampleRate baseSampleRate, const QDateTime &sampleEnd); bool sampleThingPower(const ThingId &thingId, SampleRate sampleRate, SampleRate baseSampleRate, const QDateTime &sampleEnd);
bool insertThingPower(const QDateTime &timestamp, SampleRate sampleRate, const ThingId &thingId, double currentPower, double totalConsumption, double totalProduction); bool insertThingPower(const QDateTime &timestamp, SampleRate sampleRate, const ThingId &thingId, double currentPower, double totalConsumption, double totalProduction);
void trimPowerBalance(SampleRate sampleRate, const QDateTime &beforeTime); void trimPowerBalance(SampleRate sampleRate, const QDateTime &beforeTime);
void trimThingPower(const ThingId &thingId, SampleRate sampleRate, const QDateTime &beforeTime); void trimThingPower(const ThingId &thingId, SampleRate sampleRate, const QDateTime &beforeTime);
PowerBalanceLogEntry queryResultToBalanceLogEntry(const QSqlRecord &record) const;
private: private:
struct SampleConfig { struct SampleConfig {
SampleRate baseSampleRate; SampleRate baseSampleRate;
@ -59,6 +64,11 @@ private:
QTimer m_sampleTimer; QTimer m_sampleTimer;
QHash<SampleRate, QDateTime> m_nextSamples; QHash<SampleRate, QDateTime> m_nextSamples;
double m_lastSampleTotalConsumption = 0;
double m_lastSampleTotalProducation = 0;
double m_lastSampleTotalAcquisition = 0;
double m_lastSampleTotalReturn = 0;
QSqlDatabase m_db; QSqlDatabase m_db;
int m_maxMinuteSamples = 0; int m_maxMinuteSamples = 0;

View File

@ -24,6 +24,13 @@ EnergyManagerImpl::EnergyManagerImpl(ThingManager *thingManager, QObject *parent
EnergyManagerImpl::setRootMeter(rootMeterThingId); EnergyManagerImpl::setRootMeter(rootMeterThingId);
qCDebug(dcEnergyExperience()) << "Loaded root meter" << rootMeterThingId; qCDebug(dcEnergyExperience()) << "Loaded root meter" << rootMeterThingId;
PowerBalanceLogEntry latestEntry = m_logger->latestLogEntry(EnergyLogs::SampleRateAny);
m_totalConsumption = latestEntry.totalConsumption();
m_totalProduction = latestEntry.totalProduction();
m_totalAcquisition = latestEntry.totalAcquisition();
m_totalReturn = latestEntry.totalReturn();
qCDebug(dcEnergyExperience()) << "Loader power balance totals. Consumption:" << m_totalConsumption << "Production:" << m_totalProduction << "Acquisition:" << m_totalAcquisition << "Return:" << m_totalReturn;
foreach (Thing *thing, m_thingManager->configuredThings()) { foreach (Thing *thing, m_thingManager->configuredThings()) {
watchThing(thing); watchThing(thing);
} }
@ -75,6 +82,26 @@ double EnergyManagerImpl::currentPowerStorage() const
return m_currentPowerStorage; return m_currentPowerStorage;
} }
double EnergyManagerImpl::totalConsumption() const
{
return m_totalConsumption;
}
double EnergyManagerImpl::totalProduction() const
{
return m_totalProduction;
}
double EnergyManagerImpl::totalAcquisition() const
{
return m_totalAcquisition;
}
double EnergyManagerImpl::totalReturn() const
{
return m_totalReturn;
}
EnergyLogs *EnergyManagerImpl::logs() const EnergyLogs *EnergyManagerImpl::logs() const
{ {
return m_logger; return m_logger;
@ -106,6 +133,10 @@ void EnergyManagerImpl::watchThing(Thing *thing)
|| thing->thingClass().interfaces().contains("smartmeterconsumer") || thing->thingClass().interfaces().contains("smartmeterconsumer")
|| thing->thingClass().interfaces().contains("smartmeterproducer") || thing->thingClass().interfaces().contains("smartmeterproducer")
|| thing->thingClass().interfaces().contains("energystorage")) { || thing->thingClass().interfaces().contains("energystorage")) {
m_totalEnergyConsumedCache[thing] = thing->stateValue("totalEnergyConsumed").toDouble();
m_totalEnergyProducedCache[thing] = thing->stateValue("totalEnergyProduced").toDouble();
connect(thing, &Thing::stateValueChanged, this, [=](const StateTypeId &stateTypeId, const QVariant &value){ connect(thing, &Thing::stateValueChanged, this, [=](const StateTypeId &stateTypeId, const QVariant &value){
if (thing->thingClass().getStateType(stateTypeId).name() == "currentPower") { if (thing->thingClass().getStateType(stateTypeId).name() == "currentPower") {
m_logger->logThingPower(thing->id(), value.toDouble(), thing->state("totalEnergyConsumed").value().toDouble(), thing->state("totalEnergyProduced").value().toDouble()); m_logger->logThingPower(thing->id(), value.toDouble(), thing->state("totalEnergyConsumed").value().toDouble(), thing->state("totalEnergyProduced").value().toDouble());
@ -127,22 +158,44 @@ void EnergyManagerImpl::updatePowerBalance()
double currentPowerAcquisition = 0; double currentPowerAcquisition = 0;
if (m_rootMeter) { if (m_rootMeter) {
currentPowerAcquisition = m_rootMeter->stateValue("currentPower").toDouble(); currentPowerAcquisition = m_rootMeter->stateValue("currentPower").toDouble();
double oldAcquisition = m_totalEnergyConsumedCache.value(m_rootMeter);
double newAcquisition = m_rootMeter->stateValue("totalEnergyConsumed").toDouble();
qCDebug(dcEnergyExperience()) << "Root meteter total consumption diff" << "old" << oldAcquisition << " new" << newAcquisition << (newAcquisition -oldAcquisition);
m_totalAcquisition += newAcquisition - oldAcquisition;
m_totalEnergyConsumedCache[m_rootMeter] = newAcquisition;
double oldReturn = m_totalEnergyProducedCache.value(m_rootMeter);
double newReturn = m_rootMeter->stateValue("totalEnergyProduced").toDouble();
qCDebug(dcEnergyExperience()) << "Root meteter total return diff" << "old" << oldReturn << " new" << newReturn << (newReturn - oldReturn);
m_totalReturn += newReturn - oldReturn;
m_totalEnergyProducedCache[m_rootMeter] = newReturn;
} }
double currentPowerProduction = 0; double currentPowerProduction = 0;
foreach (Thing* thing, m_thingManager->configuredThings().filterByInterface("smartmeterproducer")) { foreach (Thing* thing, m_thingManager->configuredThings().filterByInterface("smartmeterproducer")) {
currentPowerProduction += thing->stateValue("currentPower").toDouble(); currentPowerProduction += thing->stateValue("currentPower").toDouble();
double oldProduction = m_totalEnergyProducedCache.value(thing);
double newProduction = thing->stateValue("totalEnergyProduced").toDouble();
qCDebug(dcEnergyExperience()) << "inverter total production diff" << "old" << oldProduction << " new" << newProduction << (newProduction - oldProduction);
m_totalProduction += newProduction - oldProduction;
m_totalEnergyProducedCache[thing] = newProduction;
} }
double currentPowerStorage = 0; double currentPowerStorage = 0;
double totalFromStorage = 0;
foreach (Thing *thing, m_thingManager->configuredThings().filterByInterface("energystorage")) { foreach (Thing *thing, m_thingManager->configuredThings().filterByInterface("energystorage")) {
currentPowerStorage += thing->stateValue("currentPower").toDouble(); currentPowerStorage += thing->stateValue("currentPower").toDouble();
double oldProduction = m_totalEnergyProducedCache.value(thing);
double newProduction = thing->stateValue("totalEnergyProduced").toDouble();
totalFromStorage += newProduction - oldProduction;
m_totalEnergyProducedCache[thing] = newProduction;
} }
double currentPowerConsumption = -currentPowerProduction + currentPowerAcquisition - currentPowerStorage; double currentPowerConsumption = currentPowerAcquisition + qAbs(qMin(0.0, currentPowerProduction)) - currentPowerStorage;
m_totalConsumption = m_totalAcquisition + m_totalProduction + totalFromStorage;
qCDebug(dcEnergyExperience()).noquote().nospace() << "Power balance: " << "🔥: " << currentPowerConsumption << " W, 🌞: " << currentPowerProduction << " W, 💵: " << currentPowerAcquisition << " W, 🔋: " << currentPowerStorage << " W. Totals: 🔥: " << m_totalConsumption << " kWh, 🌞: " << m_totalProduction << " kWh, 💵↓: " << m_totalAcquisition << " kWh, 💵↑: " << m_totalReturn << " kWh";
qCDebug(dcEnergyExperience()) << "Consumption:" << currentPowerConsumption << "Production:" << currentPowerProduction << "Acquisition:" << currentPowerAcquisition << "Storage:" << currentPowerStorage;
if (currentPowerAcquisition != m_currentPowerAcquisition if (currentPowerAcquisition != m_currentPowerAcquisition
|| currentPowerConsumption != m_currentPowerConsumption || currentPowerConsumption != m_currentPowerConsumption
|| currentPowerProduction != m_currentPowerProduction || currentPowerProduction != m_currentPowerProduction
@ -152,7 +205,7 @@ void EnergyManagerImpl::updatePowerBalance()
m_currentPowerConsumption = currentPowerConsumption; m_currentPowerConsumption = currentPowerConsumption;
m_currentPowerStorage = currentPowerStorage; m_currentPowerStorage = currentPowerStorage;
emit powerBalanceChanged(); emit powerBalanceChanged();
m_logger->logPowerBalance(m_currentPowerConsumption, m_currentPowerProduction, m_currentPowerAcquisition, m_currentPowerStorage); m_logger->logPowerBalance(m_currentPowerConsumption, m_currentPowerProduction, m_currentPowerAcquisition, m_currentPowerStorage, m_totalConsumption, m_totalProduction, m_totalAcquisition, m_totalReturn);
} }
} }

View File

@ -24,6 +24,10 @@ public:
double currentPowerProduction() const override; double currentPowerProduction() const override;
double currentPowerAcquisition() const override; double currentPowerAcquisition() const override;
double currentPowerStorage() const override; double currentPowerStorage() const override;
double totalConsumption() const override;
double totalProduction() const override;
double totalAcquisition() const override;
double totalReturn() const override;
EnergyLogs* logs() const override; EnergyLogs* logs() const override;
@ -42,12 +46,19 @@ private:
Thing *m_rootMeter = nullptr; Thing *m_rootMeter = nullptr;
QTimer m_balanceUpdateTimer; QTimer m_balanceUpdateTimer;
double m_currentPowerConsumption; double m_currentPowerConsumption = 0;
double m_currentPowerProduction; double m_currentPowerProduction = 0;
double m_currentPowerAcquisition; double m_currentPowerAcquisition = 0;
double m_currentPowerStorage; double m_currentPowerStorage = 0;
double m_totalConsumption = 0;
double m_totalProduction = 0;
double m_totalAcquisition = 0;
double m_totalReturn = 0;
EnergyLogger *m_logger = nullptr; EnergyLogger *m_logger = nullptr;
QHash<Thing*, double> m_totalEnergyConsumedCache;
QHash<Thing*, double> m_totalEnergyProducedCache;
}; };
#endif // ENERGYMANAGERIMPL_H #endif // ENERGYMANAGERIMPL_H