From 6f04164513c8b7c22e66b88a0c384efe7914b686 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Fri, 26 Nov 2021 11:16:33 +0100 Subject: [PATCH] Some more work on the power balance stats --- libnymea-app/energy/energylogs.cpp | 6 + libnymea-app/energy/powerbalancelogs.cpp | 8 +- nymea-app/ui/mainviews/EnergyView.qml | 78 ++-- .../ui/mainviews/energy/ConsumerStats.qml | 1 + .../ui/mainviews/energy/PowerBalanceStats.qml | 353 +++++++++++++----- 5 files changed, 303 insertions(+), 143 deletions(-) diff --git a/libnymea-app/energy/energylogs.cpp b/libnymea-app/energy/energylogs.cpp index ded15a56..0a26cb83 100644 --- a/libnymea-app/energy/energylogs.cpp +++ b/libnymea-app/energy/energylogs.cpp @@ -75,6 +75,12 @@ void EnergyLogs::setSampleRate(SampleRate sampleRate) if (m_sampleRate != sampleRate) { m_sampleRate = sampleRate; emit sampleRateChanged(); + + beginResetModel(); + qDeleteAll(m_list); + m_list.clear(); + endResetModel(); + fetchLogs(); } } diff --git a/libnymea-app/energy/powerbalancelogs.cpp b/libnymea-app/energy/powerbalancelogs.cpp index 0db11397..b0ca183a 100644 --- a/libnymea-app/energy/powerbalancelogs.cpp +++ b/libnymea-app/energy/powerbalancelogs.cpp @@ -121,7 +121,7 @@ void PowerBalanceLogs::addEntry(PowerBalanceLogEntry *entry) EnergyLogEntry *PowerBalanceLogs::find(const QDateTime ×tamp) const { - qWarning() << "Finding log entry for timestamp:" << timestamp; +// qWarning() << "Finding log entry for timestamp:" << timestamp; int oldest = 0; int newest = rowCount() - 1; EnergyLogEntry *entry = nullptr; @@ -131,7 +131,7 @@ EnergyLogEntry *PowerBalanceLogs::find(const QDateTime ×tamp) const EnergyLogEntry *newestEntry = get(newest); int middle = (newest - oldest) / 2 + oldest; EnergyLogEntry *middleEntry = get(middle); - qWarning() << "Oldest:" << oldestEntry->timestamp().toString() << "Middle:" << middleEntry->timestamp().toString() << "Newest:" << newestEntry->timestamp().toString() << ":" << (newest - oldest); +// qWarning() << "Oldest:" << oldestEntry->timestamp().toString() << "Middle:" << middleEntry->timestamp().toString() << "Newest:" << newestEntry->timestamp().toString() << ":" << (newest - oldest); if (timestamp <= oldestEntry->timestamp()) { return oldestEntry; } @@ -144,9 +144,9 @@ EnergyLogEntry *PowerBalanceLogs::find(const QDateTime ×tamp) const } if (timestamp < middleEntry->timestamp()) { - oldest = middle; - } else { newest = middle; + } else { + oldest = middle; } if ((newest - oldest) <= 1) { diff --git a/nymea-app/ui/mainviews/EnergyView.qml b/nymea-app/ui/mainviews/EnergyView.qml index 6fdfd931..7e69341a 100644 --- a/nymea-app/ui/mainviews/EnergyView.qml +++ b/nymea-app/ui/mainviews/EnergyView.qml @@ -97,54 +97,54 @@ MainViewBase { columnSpacing: 0 - CurrentConsumptionBalancePieChart { - Layout.fillWidth: true - Layout.preferredHeight: width - energyManager: energyManager - visible: producers.count > 0 - } - CurrentProductionBalancePieChart { - Layout.fillWidth: true - Layout.preferredHeight: width - energyManager: energyManager - visible: producers.count > 0 - } +// CurrentConsumptionBalancePieChart { +// Layout.fillWidth: true +// Layout.preferredHeight: width +// energyManager: energyManager +// visible: producers.count > 0 +// } +// CurrentProductionBalancePieChart { +// Layout.fillWidth: true +// Layout.preferredHeight: width +// energyManager: energyManager +// visible: producers.count > 0 +// } - PowerConsumptionBalanceHistory { - Layout.fillWidth: true - Layout.preferredHeight: width - visible: producers.count > 0 - } +// PowerConsumptionBalanceHistory { +// Layout.fillWidth: true +// Layout.preferredHeight: width +// visible: producers.count > 0 +// } - PowerProductionBalanceHistory { - Layout.fillWidth: true - Layout.preferredHeight: width - visible: producers.count > 0 - } +// PowerProductionBalanceHistory { +// Layout.fillWidth: true +// Layout.preferredHeight: width +// visible: producers.count > 0 +// } - ConsumersBarChart { - Layout.fillWidth: true - Layout.preferredHeight: width - energyManager: energyManager - visible: consumers.count > 0 - } - ConsumersHistory { - Layout.fillWidth: true - Layout.preferredHeight: width - visible: consumers.count > 0 - } +// ConsumersBarChart { +// Layout.fillWidth: true +// Layout.preferredHeight: width +// energyManager: energyManager +// visible: consumers.count > 0 +// } +// ConsumersHistory { +// Layout.fillWidth: true +// Layout.preferredHeight: width +// visible: consumers.count > 0 +// } PowerBalanceStats { Layout.fillWidth: true Layout.preferredHeight: width energyManager: energyManager } - ConsumerStats { - Layout.fillWidth: true - Layout.preferredHeight: width - energyManager: energyManager - visible: consumers.count > 0 - } +// ConsumerStats { +// Layout.fillWidth: true +// Layout.preferredHeight: width +// energyManager: energyManager +// visible: consumers.count > 0 +// } } } diff --git a/nymea-app/ui/mainviews/energy/ConsumerStats.qml b/nymea-app/ui/mainviews/energy/ConsumerStats.qml index 2db9c411..9a58bbdb 100644 --- a/nymea-app/ui/mainviews/energy/ConsumerStats.qml +++ b/nymea-app/ui/mainviews/energy/ConsumerStats.qml @@ -21,6 +21,7 @@ ChartView { property EnergyManager energyManager: null + readonly property date dayStart: { var d = new Date(); d.setHours(0,0,0,0); diff --git a/nymea-app/ui/mainviews/energy/PowerBalanceStats.qml b/nymea-app/ui/mainviews/energy/PowerBalanceStats.qml index 69e20456..863538f1 100644 --- a/nymea-app/ui/mainviews/energy/PowerBalanceStats.qml +++ b/nymea-app/ui/mainviews/energy/PowerBalanceStats.qml @@ -3,24 +3,55 @@ import QtQuick.Layouts 1.2 import QtQuick.Controls 2.3 import QtCharts 2.3 import Nymea 1.0 +import "qrc:/ui/components/" -ChartView { +ColumnLayout { id: root - backgroundColor: "transparent" - legend.alignment: Qt.AlignBottom - legend.font: Style.extraSmallFont - legend.labelColor: !powerBalanceLogs.fetchingData && powerBalanceLogs.count > 0 ? Style.foregroundColor : Style.gray - -// margins.left: 0 - margins.right: 0 - margins.bottom: 0 - margins.top: 0 - - title: qsTr("Energy consumption statistics") - titleColor: Style.foregroundColor property EnergyManager energyManager: null + readonly property date minuteStart: { + var d = new Date(); + d.setSeconds(0, 0) + return d; + } + readonly property var minutesList: { + var ret = [] + for (var i = 15; i >= 0; i--) { + var last = new Date(minuteStart) + ret.push(last.setTime(last.getTime() - i * 60 * 1000)) + } + return ret; + } + readonly property var minutesListNames: { + var ret = [] + for (var i = 0; i < minutesList.length; i++) { + ret.push(new Date(minutesList[i]).toLocaleString(Qt.locale(), "hh:mm")) + } + return ret; + } + + readonly property date hourStart: { + var d = new Date(); + d.setMinutes(0, 0, 0); + return d; + } + + readonly property var hoursList: { + var ret = [] + for (var i = 24; i >= 0; i--) { + var last = new Date(hourStart) + } + return ret; + } + readonly property var hoursListNames: { + var ret = []; + for (var i = 0; i < hoursList.length; i++) { + ret.push(new Date(hoursList[i]).toLocaleString(Qt.locale(), "dd")); + } + return ret; + } + readonly property date dayStart: { var d = new Date(); d.setHours(0,0,0,0); @@ -65,21 +96,101 @@ ChartView { return d; } + + + Label { + Layout.fillWidth: true + Layout.margins: Style.smallMargins + horizontalAlignment: Text.AlignHCenter + text: qsTr("Energy consumption statistics") + + } + + SelectionTabs { + id: selectionTabs + Layout.fillWidth: true + Layout.leftMargin: Style.smallMargins + Layout.rightMargin: Style.smallMargins + model: ListModel { +// ListElement { +// modelData: qsTr("Year") +// sampleRate: EnergyLogs.SampleRate1Year +// } +// ListElement { +// text: qsTr("Month") +// sampleRate: EnergyLogs.SampleRate1Month +// } +// ListElement { +// text: qsTr("Week") +// sampleRate: EnergyLogs.SampleRate1Week +// } + + Component.onCompleted: { + append({modelData: qsTr("Day"), config: "days", sampleRate: EnergyLogs.SampleRate1Day, startTime: yearStart, sampleList: daysList, sampleListNames: daysListNames }) + append({modelData: qsTr("Hour"), config: "hours", sampleRate: EnergyLogs.SampleRate1Hour, startTime: monthStart, sampleList: hoursList, sampleListNames: hoursListNames }) + } + +// ListElement { +// modelData: qsTr("Day") +// sampleRate: EnergyLogs.SampleRate1Day +// startTime: weekStart +// } +// ListElement { +// modelData: qsTr("Hour") +// sampleRate: EnergyLogs.SampleRate1Hour +// startTime: dayStart +// } + } +// currentIndex: 3 + onCurrentValueChanged: { + print("Selecging model:", currentValue) + powerBalanceLogs.loadingInhibited = true + powerBalanceLogs.sampleRate = currentValue.sampleRate + powerBalanceLogs.startTime = currentValue.startTime + powerBalanceLogs.loadingInhibited = false + + consumptionSeries.remove(0, consumptionSeries.count-1) + productionSeries.remove(0, productionSeries.count-1) + acquisitionSeries.remove(0, acquisitionSeries.count-1) + returnSeries.remove(0, returnSeries.count-1) + + print("sample list names:", currentValue.sampleListNames) + categoryAxis.categories = currentValue.sampleListNames + + } + } + + Connections { + target: energyManager + onPowerBalanceChanged: { + var start = powerBalanceLogs.get(powerBalanceLogs.count - 1) + print("updating", start.timestamp, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0)) + consumptionSeries.replace(consumptionSeries.count - 1, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0)) + productionSeries.replace(productionSeries.count - 1, root.energyManager.totalProduction - (start ? start.totalProduction : 0)) + acquisitionSeries.replace(acquisitionSeries.count - 1, root.energyManager.totalAcquisition - (start ? start.totalAcquisition : 0)) + returnSeries.replace(returnSeries.count - 1, root.energyManager.totalReturn - (start ? start.totalReturn : 0)) + } + } + PowerBalanceLogs { id: powerBalanceLogs engine: _engine - sampleRate: EnergyLogs.SampleRate1Day - startTime: root.yearStart; +// sampleRate: EnergyLogs.SampleRate1Day +// startTime: root.yearStart; + sampleRate: selectionTabs.currentValue.sampleRate// EnergyLogs.SampleRate1Min + startTime: selectionTabs.currentValue.startTime // root.weekStart; + + property var sampleList: minutesList onFetchingDataChanged: { if (!fetchingData) { - for (var i = 0; i < daysList.length; i++) { - var start = powerBalanceLogs.find(new Date(daysList[i])) + for (var i = 0; i < sampleList.length; i++) { + var start = powerBalanceLogs.find(new Date(sampleList[i])) var end = null; - if (i+1 < daysList.length) { - end = powerBalanceLogs.find(new Date(daysList[i+1])) + if (i+1 < sampleList.length) { + end = powerBalanceLogs.find(new Date(sampleList[i+1])) } - print("** stats for", daysList[i], new Date(daysList[i]), start.timestamp, start.totalConsumption) + print("** stats for:", new Date(sampleList[i]), start.timestamp, start.totalConsumption) var consumptionValue = (end != null ? end.totalConsumption : root.energyManager.totalConsumption) - (start ? start.totalConsumption : 0) var productionValue = (end != null ? end.totalProduction : root.energyManager.totalProduction) - (start ? start.totalProduction : 0) var acquisitionValue = (end != null ? end.totalAcquisition : root.energyManager.totalAcquisition) - (start ? start.totalAcquisition : 0) @@ -96,98 +207,140 @@ ChartView { } } } - } - Item { - id: labelsLayout - x: Style.smallMargins - y: root.plotArea.y - height: root.plotArea.height - width: plotArea.x - x - enabled: !powerBalanceLogs.fetchingData && powerBalanceLogs.count > 0 - - Repeater { - model: valueAxis.tickCount - delegate: Label { - y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2 - width: parent.width - Style.smallMargins - horizontalAlignment: Text.AlignRight - text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1)))).toFixed(0) + "kWh" - verticalAlignment: Text.AlignTop - font: Style.extraSmallFont + onEntryAdded: { + if (fetchingData) { + return } + + var start = entry + var consumptionValue = root.energyManager.totalConsumption - (start ? start.totalConsumption : 0) + var productionValue = root.energyManager.totalProduction - (start ? start.totalProduction : 0) + var acquisitionValue = root.energyManager.totalAcquisition - (start ? start.totalAcquisition : 0) + var returnValue = root.energyManager.totalReturn - (start ? start.totalReturn : 0) + consumptionSeries.append(consumptionValue) + productionSeries.append(productionValue) + acquisitionSeries.append(acquisitionValue) + returnSeries.append(returnValue) + consumptionSeries.remove(0, 1); + productionSeries.remove(0, 1); + acquisitionSeries.remove(0, 1); + returnSeries.remove(0, 1); } } - BarSeries { - axisX: BarCategoryAxis { - id: categoryAxis - categories: daysListNames - labelsColor: !powerBalanceLogs.fetchingData && powerBalanceLogs.count > 0 ? Style.foregroundColor : Style.gray - labelsFont: Style.extraSmallFont - gridVisible: false - gridLineColor: Style.tileOverlayColor - lineVisible: false - titleVisible: false - shadesVisible: false + ChartView { + id: chartView + Layout.fillWidth: true + Layout.fillHeight: true - } - axisY: ValueAxis { - id: valueAxis - min: 0 - gridLineColor: Style.tileOverlayColor - labelsVisible: false - labelsColor: Style.foregroundColor - labelsFont: Style.extraSmallFont - lineVisible: false - titleVisible: false - shadesVisible: false + backgroundColor: "transparent" + legend.alignment: Qt.AlignBottom + legend.font: Style.extraSmallFont + legend.labelColor: !powerBalanceLogs.fetchingData && powerBalanceLogs.count > 0 ? Style.foregroundColor : Style.gray - function adjustMax(newValue) { - if (max < newValue) { - max = Math.ceil(newValue / 100) * 100 + // margins.left: 0 + margins.right: 0 + margins.bottom: 0 + margins.top: 0 + +// title: qsTr("Energy consumption statistics") +// titleColor: "red"// Style.foregroundColor + + Item { + id: labelsLayout + x: Style.smallMargins + y: chartView.plotArea.y + height: chartView.plotArea.height + width: chartView.plotArea.x - x + enabled: !powerBalanceLogs.fetchingData && powerBalanceLogs.count > 0 + + Repeater { + model: valueAxis.tickCount + delegate: Label { + y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2 + width: parent.width - Style.smallMargins + horizontalAlignment: Text.AlignRight + text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1)))).toFixed(0) + "kWh" + verticalAlignment: Text.AlignTop + font: Style.extraSmallFont } } } - BarSet { - id: consumptionSeries - label: qsTr("Consumed") - borderWidth: 0 - } - BarSet { - id: productionSeries - label: qsTr("Produced") - color: Style.green - borderWidth: 0 - borderColor: color - } - BarSet { - id: acquisitionSeries - label: qsTr("From grid") - color: Style.red - borderWidth: 0 - borderColor: color - } - BarSet { - id: returnSeries - label: qsTr("To grid") - color: Style.orange - borderWidth: 0 - borderColor: color - } - } + BarSeries { + axisX: BarCategoryAxis { + id: categoryAxis + // categories: daysListNames +// categories: minutesListNames + labelsColor: !powerBalanceLogs.fetchingData && powerBalanceLogs.count > 0 ? Style.foregroundColor : Style.gray - Label { - x: root.plotArea.x - y: root.plotArea.y - width: root.plotArea.width - height: root.plotArea.height - wrapMode: Text.WordWrap - text: qsTr("No data available") - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - visible: !powerBalanceLogs.fetchingData && powerBalanceLogs.count == 0 + labelsFont: Style.extraSmallFont + gridVisible: false + gridLineColor: Style.tileOverlayColor + lineVisible: false + titleVisible: false + shadesVisible: false + + } + axisY: ValueAxis { + id: valueAxis + min: 0 + gridLineColor: Style.tileOverlayColor + labelsVisible: false + labelsColor: Style.foregroundColor + labelsFont: Style.extraSmallFont + lineVisible: false + titleVisible: false + shadesVisible: false + + function adjustMax(newValue) { + if (max < newValue) { + max = newValue // Math.ceil(newValue / 100) * 100 + } + } + } + + BarSet { + id: consumptionSeries + label: qsTr("Consumed") + borderWidth: 0 + } + BarSet { + id: productionSeries + label: qsTr("Produced") + color: Style.green + borderWidth: 0 + borderColor: color + } + BarSet { + id: acquisitionSeries + label: qsTr("From grid") + color: Style.red + borderWidth: 0 + borderColor: color + } + BarSet { + id: returnSeries + label: qsTr("To grid") + color: Style.orange + borderWidth: 0 + borderColor: color + } + } + + Label { + x: chartView.plotArea.x + y: chartView.plotArea.y + width: chartView.plotArea.width + height: chartView.plotArea.height + wrapMode: Text.WordWrap + text: qsTr("No data available") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + visible: !powerBalanceLogs.fetchingData && powerBalanceLogs.count == 0 + } } } +