Update energy logs to latest log api

This commit is contained in:
Michael Zanetti 2021-12-13 00:41:59 +01:00
parent f161e23e0b
commit d2f769bbf7
8 changed files with 128 additions and 32 deletions

View File

@ -227,12 +227,6 @@ void EnergyLogs::notificationReceivedInternal(const QVariantMap &data)
return;
}
QMetaEnum sampleRateEnum = QMetaEnum::fromType<SampleRate>();
SampleRate sampleRate = static_cast<SampleRate>(sampleRateEnum.keyToValue(data.value("params").toMap().value("sampleRate").toByteArray()));
if (sampleRate != m_sampleRate) {
return;
}
notificationReceived(data);
}

View File

@ -1,5 +1,7 @@
#include "powerbalancelogs.h"
#include <QMetaEnum>
PowerBalanceLogEntry::PowerBalanceLogEntry(QObject *parent): EnergyLogEntry(parent)
{
@ -180,6 +182,14 @@ void PowerBalanceLogs::notificationReceived(const QVariantMap &data)
{
QString notification = data.value("notification").toString();
QVariantMap params = data.value("params").toMap();
QMetaEnum sampleRateEnum = QMetaEnum::fromType<EnergyLogs::SampleRate>();
SampleRate sampleRate = static_cast<SampleRate>(sampleRateEnum.keyToValue(data.value("params").toMap().value("sampleRate").toByteArray()));
if (sampleRate != this->sampleRate()) {
return;
}
if (notification == "Energy.PowerBalanceLogEntryAdded") {
QVariantMap map = params.value("powerBalanceLogEntry").toMap();
QDateTime timestamp = QDateTime::fromSecsSinceEpoch(map.value("timestamp").toLongLong());

View File

@ -1,5 +1,7 @@
#include "thingpowerlogs.h"
#include <QMetaEnum>
ThingPowerLogEntry::ThingPowerLogEntry(QObject *parent):
EnergyLogEntry(parent)
{
@ -69,7 +71,7 @@ double ThingPowerLogs::maxValue() const
return m_maxValue;
}
EnergyLogEntry *ThingPowerLogs::find(const QUuid &thingId, const QDateTime &timestamp)
ThingPowerLogEntry *ThingPowerLogs::find(const QUuid &thingId, const QDateTime &timestamp)
{
// TODO: Can we do a binary search even if they key we're looking for is not unique (but still sorted)?
// For now, 365 * consumers items is the max we'll have here which seems on the edge for doing a stupid linear search...
@ -90,6 +92,11 @@ EnergyLogEntry *ThingPowerLogs::find(const QUuid &thingId, const QDateTime &time
return nullptr;
}
ThingPowerLogEntry *ThingPowerLogs::liveEntry(const QUuid &thingId)
{
return m_liveEntries.value(thingId);
}
void ThingPowerLogs::addEntry(ThingPowerLogEntry *entry)
{
appendEntry(entry);
@ -104,6 +111,16 @@ void ThingPowerLogs::addEntries(const QList<ThingPowerLogEntry *> &entries)
appendEntries(energyLogEntries);
}
ThingPowerLogEntry *ThingPowerLogs::unpack(const QVariantMap &map)
{
QDateTime timestamp = QDateTime::fromSecsSinceEpoch(map.value("timestamp").toLongLong());
QUuid thingId = map.value("thingId").toUuid();
double currentPower = map.value("currentPower").toDouble();
double totalConsumption = map.value("totalConsumption").toDouble();
double totalProduction = map.value("totalProduction").toDouble();
return new ThingPowerLogEntry(timestamp, thingId, currentPower, totalConsumption, totalProduction, this);
}
QString ThingPowerLogs::logsName() const
{
return "ThingPowerLogs";
@ -117,11 +134,22 @@ QVariantMap ThingPowerLogs::fetchParams() const
}
QVariantMap ret;
ret.insert("thingIds", thingIdsStrings);
ret.insert("includeCurrent", true);
return ret;
}
void ThingPowerLogs::logEntriesReceived(const QVariantMap &params)
{
foreach (const QVariant &variant, params.value("currentEntries").toList()) {
QVariantMap map = variant.toMap();
ThingPowerLogEntry *entry = unpack(map);
if (m_liveEntries.contains(entry->thingId())) {
m_liveEntries[entry->thingId()]->deleteLater();
}
m_liveEntries[entry->thingId()] = entry;
emit liveEntryChanged(entry);
}
// Grouping them so when the UI gets entriesAdded, the whole set for this timstamp will be available at once
QList<ThingPowerLogEntry*> groupForTimestamp;
foreach (const QVariant &variant, params.value("thingPowerLogEntries").toList()) {
@ -155,6 +183,32 @@ void ThingPowerLogs::notificationReceived(const QVariantMap &data)
{
QString notification = data.value("notification").toString();
QVariantMap params = data.value("params").toMap();
QMetaEnum sampleRateEnum = QMetaEnum::fromType<EnergyLogs::SampleRate>();
SampleRate sampleRate = static_cast<SampleRate>(sampleRateEnum.keyToValue(data.value("params").toMap().value("sampleRate").toByteArray()));
QVariantMap entryMap = params.value("thingPowerLogEntry").toMap();
QUuid thingId = entryMap.value("thingId").toUuid();
if (!m_thingIds.isEmpty() && !m_thingIds.contains(thingId)) {
// Not watching this thing...
return;
}
// We'll use 1 Min samples in any case for the live value
if (sampleRate == EnergyLogs::SampleRate1Min) {
ThingPowerLogEntry *liveEntry = unpack(params.value("thingPowerLogEntry").toMap());
if (m_liveEntries.contains(thingId)) {
m_liveEntries.value(thingId)->deleteLater();
}
m_liveEntries[thingId] = liveEntry;
emit liveEntryChanged(liveEntry);
}
// And append the sample rate we're interested in
if (sampleRate != this->sampleRate()) {
return;
}
if (notification == "Energy.ThingPowerLogEntryAdded") {
QVariantMap map = params.value("thingPowerLogEntry").toMap();
QDateTime timestamp = QDateTime::fromSecsSinceEpoch(map.value("timestamp").toLongLong());
@ -166,6 +220,11 @@ void ThingPowerLogs::notificationReceived(const QVariantMap &data)
double totalConsumption = map.value("totalConsumption").toDouble();
double totalProduction = map.value("totalProduction").toDouble();
ThingPowerLogEntry *entry = new ThingPowerLogEntry(timestamp, thingId, currentPower, totalConsumption, totalProduction, this);
// In order to be easier on resources, we'll batch notifications by grouping them by timestamp
// While the timestamp is the same, just cache the changes. Once the timestamp changes, we'll finalize the
// batch and actually append them.
// Also if we're not getting any more notification for a while and still have cached entries, we'll process the batch
if (m_cachedEntries.isEmpty()) {
m_cachedEntries.append(entry);
} else if (entry->timestamp() == m_cachedEntries.first()->timestamp()) {

View File

@ -44,7 +44,9 @@ public:
double minValue() const;
double maxValue() const;
Q_INVOKABLE EnergyLogEntry *find(const QUuid &thingId, const QDateTime &timestamp);
Q_INVOKABLE ThingPowerLogEntry *find(const QUuid &thingId, const QDateTime &timestamp);
Q_INVOKABLE ThingPowerLogEntry *liveEntry(const QUuid &thingId);
signals:
void thingIdsChanged();
@ -52,6 +54,8 @@ signals:
void minValueChanged();
void maxValueChanged();
void liveEntryChanged(ThingPowerLogEntry *entry);
protected:
QString logsName() const override;
QVariantMap fetchParams() const override;
@ -62,12 +66,16 @@ private:
void addEntry(ThingPowerLogEntry *entry);
void addEntries(const QList<ThingPowerLogEntry *> &entries);
ThingPowerLogEntry *unpack(const QVariantMap &map);
QList<QUuid> m_thingIds;
double m_minValue = 0;
double m_maxValue = 0;
QList<ThingPowerLogEntry*> m_cachedEntries;
QTimer m_cacheTimer;
QHash<QUuid, ThingPowerLogEntry*> m_liveEntries;
};
#endif // THINGPOWERLOGS_H

View File

@ -113,8 +113,9 @@ StatsBase {
var liveEntry = {}
for (var j = 0; j < consumers.count; j++) {
var consumer = consumers.get(j)
// print("Got consumer:", consumer.id, consumer.name)
var value = consumer.stateByName("totalEnergyConsumed").value;
var liveLogEntry = powerLogs.liveEntry(consumer.id)
// print("Got consumer:", consumer.id, consumer.name, liveLogEntry ? liveLogEntry.timestamp : "-")
var value = liveLogEntry ? liveLogEntry.totalConsumption : 0;
if (groupedEntry) {
value -= groupedEntry.hasOwnProperty(consumer.id) ? groupedEntry[consumer.id] : 0
}
@ -205,20 +206,41 @@ StatsBase {
var entry = entries[i]
var thing = engine.thingManager.things.getThing(entry.thingId)
// print("Adding new sample. thing:", thing.name);
// print("Timestamp:", entry.timestamp)
var totalEnergyConsumedState = thing.stateByName("totalEnergyConsumed")
// print("current total:", entry.totalConsumption)
// print("Timestamp:", entry.timestamp, entry.totalConsumption)
// update current last
var barSet = barSeries.thingBarSetMap[thing.id]
var lastTimestamp = categoryAxis.timestamps[categoryAxis.count - 1]
var previous = powerLogs.find(entry.thingId, lastTimestamp)
var previousValue = previous ? previous.totalConsumption : 0
// print("previousValue:", previousValue, "newValue:", entry.totalConsumption, "diff", entry.totalConsumption - previousValue)
barSet.replace(barSet.count - 1, entry.totalConsumption - previousValue)
var consumptionValue = totalEnergyConsumedState.value - entry.totalConsumption
// print("new slot total:", consumptionValue)
// remove the oldest
barSet.remove(0, 1)
categoryAxis.categories = configs[selectionTabs.currentValue.config].sampleListNames()
barSeries.thingBarSetMap[thing].append(consumptionValue)
barSeries.thingBarSetMap[thing].remove(0, 1);
// and add a new one (always 0 for a start)
barSet.append(0)
}
var labels = categoryAxis.timestamps
labels.splice(0, 1)
labels.push(entries[0].timestamp)
categoryAxis.timestamps = labels
chartView.animationOptions = ChartView.SeriesAnimations
}
onLiveEntryChanged: {
if (powerLogs.fetchingData) {
return
}
// print("live entry changed", entry.thingId, entry.timestamp)
var previous = powerLogs.find(entry.thingId, new Date(categoryAxis.timestamps[categoryAxis.timestamps.length - 1]))
var previousValue = previous ? previous.totalConsumption : 0
var barSet = barSeries.thingBarSetMap[entry.thingId]
barSet.replace(barSet.count - 1, entry.totalConsumption - previousValue)
}
}
ColumnLayout {
@ -385,7 +407,7 @@ StatsBase {
margins: Style.smallMargins
}
Label {
text: categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
text: toolTip.idx >= 0 && categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
font: Style.smallFont
}
@ -395,7 +417,7 @@ StatsBase {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: root.colors[index % root.colors.length]
color: index >= 0 ? root.colors[index % root.colors.length] : "white"
}
Label {
text: barSeries.thingBarSetMap.hasOwnProperty(model.id) ? "%1: %2 kWh".arg(model.name).arg(barSeries.thingBarSetMap[model.id].at(toolTip.idx).toFixed(2)) : ""

View File

@ -319,7 +319,7 @@ ChartView {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: root.colors[index % root.colors.length]
color: index >= 0 ? root.colors[index % root.colors.length] : "white"
}
Label {

View File

@ -135,7 +135,7 @@ ChartView {
}
delegate: ColumnLayout {
id: consumerDelegate
width: parent.width
width: parent ? parent.width : 0
spacing: 0
property Thing consumer: consumers.getThing(model.id)
property State currentPowerState: consumer ? consumer.stateByName("currentPower") : null

View File

@ -52,7 +52,7 @@ StatsBase {
}
var config = root.configs[currentValue.config]
print("config:", config.startTime(), config.sampleRate)
// print("config:", config.startTime(), config.sampleRate)
powerBalanceLogs.loadingInhibited = true
powerBalanceLogs.sampleRate = config.sampleRate
@ -89,7 +89,7 @@ StatsBase {
onPowerBalanceChanged: {
var start = powerBalanceLogs.get(powerBalanceLogs.count - 1 )
// print("balance changed:", d.consumptionSet, powerBalanceLogs, powerBalanceLogs.count)
// print("updating", start.timestamp, start.totalConsumption, root.energyManager.totalConsumption, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0))
// print("updating", start ? start.timestamp : "", start ? start.totalConsumption : 0, root.energyManager.totalConsumption, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0))
d.consumptionSet.replace(d.consumptionSet.count - 1, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0))
d.productionSet.replace(d.productionSet.count - 1, root.energyManager.totalProduction - (start ? start.totalProduction : 0))
d.acquisitionSet.replace(d.acquisitionSet.count - 1, root.energyManager.totalAcquisition - (start ? start.totalAcquisition : 0))
@ -106,7 +106,7 @@ StatsBase {
if (!fetchingData) {
chartView.animationOptions = ChartView.NoAnimation
print("Logs fetched")
// print("Logs fetched")
var config = root.configs[selectionTabs.currentValue.config]
var labels = []
@ -130,7 +130,7 @@ StatsBase {
liveEntry.acquisition -= entry.totalAcquisition
liveEntry.returned -= entry.totalReturn
}
print("Adding live entry:", liveEntry.consumption, root.energyManager.totalConsumption, entry ? entry.totalConsumption : 0)
// print("Adding live entry:", liveEntry.consumption, root.energyManager.totalConsumption, entry ? entry.totalConsumption : 0)
entries.unshift(liveEntry)
valueAxis.adjustMax(liveEntry.consumption)
valueAxis.adjustMax(liveEntry.production)
@ -206,6 +206,7 @@ StatsBase {
return
}
// print("Entry added")
var config = root.configs[selectionTabs.currentValue.config]
@ -219,18 +220,20 @@ StatsBase {
chartView.animationOptions = ChartView.NoAnimation
var timestamps = categoryAxis.timestamps;
timestamps.splice(0, 1)
timestamps.push(entry.timestamp)
timestamps.splice(0, 1)
categoryAxis.timestamps = timestamps
d.consumptionSet.remove(0, 1);
d.productionSet.remove(0, 1);
d.acquisitionSet.remove(0, 1);
d.returnSet.remove(0, 1);
d.consumptionSet.append(consumptionValue)
d.productionSet.append(productionValue)
d.acquisitionSet.append(acquisitionValue)
d.returnSet.append(returnValue)
d.consumptionSet.remove(0, 1);
d.productionSet.remove(0, 1);
d.acquisitionSet.remove(0, 1);
d.returnSet.remove(0, 1);
chartView.animationOptions = ChartView.SeriesAnimations
}
}
@ -359,7 +362,7 @@ StatsBase {
}
Label {
text: categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
text: toolTip.idx >= 0 && categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
font: Style.smallFont
}