More work on the energy view
parent
4a91dbd334
commit
417edc4e58
|
|
@ -38,6 +38,10 @@
|
|||
#include "types/logentry.h"
|
||||
#include "logmanager.h"
|
||||
|
||||
#include "logging.h"
|
||||
NYMEA_LOGGING_CATEGORY(dcLogEngine, "LogEngine")
|
||||
|
||||
|
||||
LogsModel::LogsModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
|
||||
|
|
@ -151,6 +155,7 @@ void LogsModel::setTypeIds(const QStringList &typeIds)
|
|||
if (m_typeIds != fixedTypeIds) {
|
||||
m_typeIds = fixedTypeIds;
|
||||
emit typeIdsChanged();
|
||||
qCDebug(dcLogEngine()) << "Resetting model because type ids changed";
|
||||
beginResetModel();
|
||||
qDeleteAll(m_list);
|
||||
m_list.clear();
|
||||
|
|
@ -261,6 +266,9 @@ void LogsModel::logsReply(int /*commandId*/, const QVariantMap &data)
|
|||
foreach (const QVariant &logEntryVariant, logEntries) {
|
||||
QVariantMap entryMap = logEntryVariant.toMap();
|
||||
QDateTime timeStamp = QDateTime::fromMSecsSinceEpoch(entryMap.value("timestamp").toLongLong());
|
||||
if (!m_viewStartTime.isNull() && timeStamp < m_viewStartTime) {
|
||||
continue;
|
||||
}
|
||||
QString thingId = entryMap.value("thingId").toString();
|
||||
QString typeId = entryMap.value("typeId").toString();
|
||||
QMetaEnum sourceEnum = QMetaEnum::fromType<LogEntry::LoggingSource>();
|
||||
|
|
@ -273,7 +281,7 @@ void LogsModel::logsReply(int /*commandId*/, const QVariantMap &data)
|
|||
newBlock.append(entry);
|
||||
}
|
||||
|
||||
// qDebug() << "Received logs from" << offset << "to" << offset + count << "Actual count:" << newBlock.count();
|
||||
// qCDebug(dcLogEngine()) << objectName() << "Received logs from" << offset << "to" << offset + count << "Actual count:" << newBlock.count();
|
||||
|
||||
if (count < m_blockSize) {
|
||||
m_canFetchMore = false;
|
||||
|
|
@ -288,9 +296,11 @@ void LogsModel::logsReply(int /*commandId*/, const QVariantMap &data)
|
|||
|
||||
beginInsertRows(QModelIndex(), offset, offset + newBlock.count() - 1);
|
||||
for (int i = 0; i < newBlock.count(); i++) {
|
||||
// qCDebug(dcLogEngine()) << objectName() << "Inserting: list count" << m_list.count() << "blockSize" << newBlock.count() << "insterting at:" << offset + i;
|
||||
LogEntry *entry = newBlock.at(i);
|
||||
m_list.insert(offset + i, entry);
|
||||
emit logEntryAdded(entry);
|
||||
// qCDebug(dcLogEngine()) << objectName() << "done";
|
||||
}
|
||||
endInsertRows();
|
||||
emit countChanged();
|
||||
|
|
@ -298,7 +308,7 @@ void LogsModel::logsReply(int /*commandId*/, const QVariantMap &data)
|
|||
m_busyInternal = false;
|
||||
|
||||
if (m_viewStartTime.isValid() && m_list.count() > 0 && m_list.last()->timestamp() > m_viewStartTime && m_canFetchMore) {
|
||||
// qDebug() << "Fetching more because of viewStartTime" << m_viewStartTime.toString() << "last" << m_list.last()->timestamp().toString();
|
||||
qCDebug(dcLogEngine()) << objectName() << "Fetching more because of viewStartTime" << m_viewStartTime.toString() << "last" << m_list.last()->timestamp().toString();
|
||||
fetchMore();
|
||||
} else {
|
||||
m_busy = false;
|
||||
|
|
@ -311,7 +321,7 @@ void LogsModel::fetchMore(const QModelIndex &parent)
|
|||
Q_UNUSED(parent)
|
||||
|
||||
if (!m_engine) {
|
||||
qWarning() << "Cannot update. Engine not set";
|
||||
qCWarning(dcLogEngine()) << objectName() << "Cannot update. Engine not set";
|
||||
return;
|
||||
}
|
||||
if (m_busyInternal) {
|
||||
|
|
@ -319,7 +329,7 @@ void LogsModel::fetchMore(const QModelIndex &parent)
|
|||
}
|
||||
|
||||
if ((!m_startTime.isNull() && m_endTime.isNull()) || (m_startTime.isNull() && !m_endTime.isNull())) {
|
||||
qDebug() << "Need neither or both, startTime and endTime set";
|
||||
qCDebug(dcLogEngine()) << objectName() << "Need neither or both, startTime and endTime set";
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
#include "logmanager.h"
|
||||
|
||||
#include "logging.h"
|
||||
NYMEA_LOGGING_CATEGORY(dcLogEngine, "LogEngine")
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcLogEngine)
|
||||
|
||||
LogsModelNg::LogsModelNg(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QLoggingCategory>
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcLogEngine)
|
||||
|
||||
XYSeriesAdapter::XYSeriesAdapter(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
|
|
@ -156,12 +159,11 @@ void XYSeriesAdapter::logEntryAdded(LogEntry *entry)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
ensureSamples(entry->timestamp(), entry->timestamp());
|
||||
|
||||
int idx = entry->timestamp().secsTo(m_newestSample) / m_sampleRate;
|
||||
if (idx > m_samples.count()) {
|
||||
qWarning() << "Overflowing integer size for XYSeriesAdapter!";
|
||||
qCWarning(dcLogEngine) << "Overflowing integer size for XYSeriesAdapter!";
|
||||
return;
|
||||
}
|
||||
Sample *sample = m_samples.at(static_cast<int>(idx));
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ Thing *Things::getThing(const QUuid &thingId) const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
int Things::indexOf(Thing *thing) const
|
||||
{
|
||||
return m_things.indexOf(thing);
|
||||
}
|
||||
|
||||
int Things::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ public:
|
|||
|
||||
Q_INVOKABLE Thing *get(int index) const;
|
||||
Q_INVOKABLE Thing *getThing(const QUuid &thingId) const;
|
||||
Q_INVOKABLE int indexOf(Thing *thing) const;
|
||||
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex & index, int role = RoleName) const override;
|
||||
|
|
|
|||
|
|
@ -424,6 +424,22 @@ Thing *ThingsProxy::getThing(const QUuid &thingId) const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
int ThingsProxy::indexOf(Thing *thing) const
|
||||
{
|
||||
Things *t = qobject_cast<Things*>(sourceModel());
|
||||
ThingsProxy *tp = qobject_cast<ThingsProxy*>(sourceModel());
|
||||
int idx = -1;
|
||||
if (t) {
|
||||
idx = t->indexOf(thing);
|
||||
} else if (tp) {
|
||||
idx = tp->indexOf(thing);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
QModelIndex sourceIndex = sourceModel()->index(idx, 0);
|
||||
return mapFromSource(sourceIndex).row();
|
||||
}
|
||||
|
||||
Thing *ThingsProxy::getInternal(int source_index) const
|
||||
{
|
||||
Things* d = qobject_cast<Things*>(sourceModel());
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ public:
|
|||
|
||||
Q_INVOKABLE Thing *get(int index) const;
|
||||
Q_INVOKABLE Thing *getThing(const QUuid &thingId) const;
|
||||
Q_INVOKABLE int indexOf(Thing *thing) const;
|
||||
|
||||
signals:
|
||||
void engineChanged();
|
||||
|
|
|
|||
|
|
@ -74,6 +74,12 @@ ChartView {
|
|||
unknownConsumerEnergy = rootMeter.stateByName("totalEnergyConsumed").value
|
||||
}
|
||||
|
||||
if (rootMeter) {
|
||||
var slice = pieSeries.append(qsTr("Unknown"), unknownConsumerEnergy)
|
||||
slice.color = Style.accentColor
|
||||
d.sliceMap[slice] = rootMeter
|
||||
}
|
||||
|
||||
for (var i = 0; i < meters.count; i++) {
|
||||
var thing = meters.get(i);
|
||||
var value = 0;
|
||||
|
|
@ -100,11 +106,6 @@ ChartView {
|
|||
d.sliceMap[slice] = thing
|
||||
unknownConsumerEnergy -= value
|
||||
}
|
||||
if (rootMeter) {
|
||||
var slice = pieSeries.append(qsTr("Unknown"), unknownConsumerEnergy)
|
||||
slice.color = Style.accentColor
|
||||
d.sliceMap[slice] = rootMeter
|
||||
}
|
||||
}
|
||||
|
||||
PieSeries {
|
||||
|
|
@ -127,7 +128,7 @@ ChartView {
|
|||
font.pixelSize: app.largeFont
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: root.rootMeter
|
||||
text: root.rootMeterTotalConsumedEnergyState
|
||||
? root.rootMeterTotalConsumedEnergyState.value.toFixed(2)
|
||||
: Math.round(pieSeries.sum * 1000) / 1000
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ MainViewBase {
|
|||
engine: _engine
|
||||
shownInterfaces: ["energymeter"]
|
||||
}
|
||||
readonly property Thing rootMeter: energyMeters.count > 0 ? energyMeters.get(0) : null
|
||||
|
||||
ThingsProxy {
|
||||
id: consumers
|
||||
|
|
@ -58,12 +59,14 @@ MainViewBase {
|
|||
shownInterfaces: ["smartmeterproducer"]
|
||||
}
|
||||
|
||||
|
||||
Flickable {
|
||||
anchors.fill: parent
|
||||
anchors.margins: app.margins / 2
|
||||
contentHeight: energyGrid.childrenRect.height
|
||||
visible: energyMeters.count > 0
|
||||
|
||||
|
||||
GridLayout {
|
||||
id: energyGrid
|
||||
width: parent.width
|
||||
|
|
@ -74,15 +77,14 @@ MainViewBase {
|
|||
SmartMeterChart {
|
||||
Layout.fillWidth: true
|
||||
// Layout.preferredWidth: energyGrid.width / energyGrid.columns
|
||||
Layout.preferredHeight: width * .7
|
||||
Layout.preferredHeight: (energyGrid.width / energyGrid.columns) * .7
|
||||
// FIXME: multiple root meters... Not exactly a use case, still possible tho
|
||||
rootMeter: energyMeters.count > 0 ? energyMeters.get(0) : null
|
||||
rootMeter: root.rootMeter
|
||||
meters: consumers
|
||||
title: qsTr("Total consumed energy")
|
||||
visible: consumers.count > 0
|
||||
}
|
||||
|
||||
|
||||
ChartView {
|
||||
id: chartView
|
||||
Layout.fillWidth: true
|
||||
|
|
@ -90,9 +92,11 @@ MainViewBase {
|
|||
Layout.preferredHeight: width * .7
|
||||
legend.alignment: Qt.AlignBottom
|
||||
legend.font.pixelSize: app.smallFont
|
||||
legend.visible: false
|
||||
// legend.visible: false
|
||||
legend.labelColor: Style.foregroundColor
|
||||
backgroundColor: Style.tileBackgroundColor
|
||||
backgroundRoundness: Style.cornerRadius
|
||||
theme: ChartView.ChartThemeLight
|
||||
titleColor: Style.foregroundColor
|
||||
title: qsTr("Power usage history")
|
||||
|
||||
|
|
@ -109,9 +113,67 @@ MainViewBase {
|
|||
running: visible
|
||||
}
|
||||
|
||||
LogsModel {
|
||||
id: rootMeterLogsModel
|
||||
objectName: "Root meter model"
|
||||
engine: rootMeter ? _engine : null // Don't start fetching before we know what we want
|
||||
thingId: rootMeter ? rootMeter.id : ""
|
||||
typeIds: rootMeter ? [rootMeter.thingClass.stateTypes.findByName("currentPower").id] : []
|
||||
viewStartTime: xAxis.min
|
||||
live: true
|
||||
}
|
||||
XYSeriesAdapter {
|
||||
id: rootMeterSeriesAdapter
|
||||
logsModel: rootMeterLogsModel
|
||||
sampleRate: chartView.sampleRate
|
||||
xySeries: rootMeterSeries
|
||||
Component.onCompleted: ensureSamples(xAxis.min, xAxis.max)
|
||||
}
|
||||
Connections {
|
||||
target: xAxis
|
||||
onMinChanged: rootMeterSeriesAdapter.ensureSamples(xAxis.min, xAxis.max)
|
||||
onMaxChanged: rootMeterSeriesAdapter.ensureSamples(xAxis.min, xAxis.max)
|
||||
}
|
||||
|
||||
AreaSeries {
|
||||
id: rootMeterAreaSeries
|
||||
color: Style.accentColor
|
||||
borderWidth: 0
|
||||
axisX: xAxis
|
||||
axisY: yAxis
|
||||
name: qsTr("Unknown")
|
||||
useOpenGL: true
|
||||
lowerSeries: LineSeries {
|
||||
id: rootMeterLowerSeries
|
||||
XYPoint { x: xAxis.max.getTime(); y: 0 }
|
||||
XYPoint { x: xAxis.min.getTime(); y: 0 }
|
||||
}
|
||||
// HACK: We want this to be created (added to the chart) *before* the repeater Series below...
|
||||
// That might not be the case for a reason I don't understand. Most likely due to a mix of the declarative
|
||||
// approach here and the imperative approach using chartView.createSeries() below.
|
||||
// So hacking around by blocking the repeater from loading until this one is done
|
||||
property bool ready: false
|
||||
Component.onCompleted: ready = true
|
||||
|
||||
upperSeries: LineSeries {
|
||||
id: rootMeterSeries
|
||||
|
||||
onPointAdded: {
|
||||
var newPoint = rootMeterSeries.at(index)
|
||||
|
||||
if (newPoint.x > rootMeterLowerSeries.at(0).x) {
|
||||
rootMeterLowerSeries.replace(0, newPoint.x, 0)
|
||||
}
|
||||
if (newPoint.x < rootMeterLowerSeries.at(1).x) {
|
||||
rootMeterLowerSeries.replace(1, newPoint.x, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: consumersRepeater
|
||||
model: consumers
|
||||
model: rootMeterAreaSeries.ready && !engine.thingManager.fetchingData ? consumers : null
|
||||
|
||||
delegate: Item {
|
||||
id: consumer
|
||||
|
|
@ -119,6 +181,7 @@ MainViewBase {
|
|||
|
||||
property var model: LogsModel {
|
||||
id: logsModel
|
||||
objectName: consumer.thing.name
|
||||
engine: _engine
|
||||
thingId: consumer.thing.id
|
||||
typeIds: [consumer.thing.thingClass.stateTypes.findByName("currentPower").id]
|
||||
|
|
@ -163,9 +226,11 @@ MainViewBase {
|
|||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
print("creating series", consumer.thing.name, index)
|
||||
var indexInModel = consumers.indexOf(consumer.thing)
|
||||
print("creating series", consumer.thing.name, index, indexInModel)
|
||||
seriesAdapter.ensureSamples(xAxis.min, xAxis.max)
|
||||
var areaSeries = chartView.createSeries(ChartView.SeriesTypeArea, consumer.thing.name, xAxis, yAxis)
|
||||
areaSeries.useOpenGL = true
|
||||
areaSeries.upperSeries = upperSeries;
|
||||
if (index > 0) {
|
||||
areaSeries.lowerSeries = consumersRepeater.itemAt(index - 1).lineSeries
|
||||
|
|
@ -175,8 +240,8 @@ MainViewBase {
|
|||
}
|
||||
|
||||
var color = Style.accentColor
|
||||
for (var j = 0; j < index; j+=2) {
|
||||
if (index % 2 == 0) {
|
||||
for (var j = 0; j <= indexInModel; j+=2) {
|
||||
if (indexInModel % 2 == 0) {
|
||||
color = Qt.lighter(color, 1.2);
|
||||
} else {
|
||||
color = Qt.darker(color, 1.2)
|
||||
|
|
@ -191,9 +256,13 @@ MainViewBase {
|
|||
|
||||
ValueAxis {
|
||||
id: yAxis
|
||||
readonly property XYSeriesAdapter adapter: consumersRepeater.count > 0 ? consumersRepeater.itemAt(consumersRepeater.count - 1).adapter : null
|
||||
max: 7// adapter ? Math.ceil(Math.max(adapter.maxValue * 0.95, adapter.maxValue * 1.05)) : 1
|
||||
min: adapter ? Math.floor(Math.min(adapter.minValue * 0.95, adapter.minValue * 1.05)) : 0
|
||||
readonly property XYSeriesAdapter highestSeriesAdapter: consumersRepeater.count > 0 ? consumersRepeater.itemAt(consumersRepeater.count - 1).adapter : null
|
||||
property double rawMax: rootMeter ? rootMeterSeriesAdapter.maxValue
|
||||
: highestSeriesAdapter ? highestSeriesAdapter.maxValue : 1
|
||||
property double rawMin: rootMeter ? rootMeterSeriesAdapter.minValue
|
||||
: highestSeriesAdapter ? highestSeriesAdapter.minValue : 0
|
||||
max: Math.ceil(Math.max(rawMax * 0.9, rawMax * 1.1))
|
||||
min: Math.floor(Math.min(rawMin * 0.9, rawMin * 1.1))
|
||||
// This seems to crash occationally
|
||||
// onMinChanged: applyNiceNumbers();
|
||||
// onMaxChanged: applyNiceNumbers();
|
||||
|
|
@ -369,10 +438,10 @@ MainViewBase {
|
|||
Layout.preferredHeight: width * .7
|
||||
backgroundColor: Style.tileBackgroundColor
|
||||
backgroundRoundness: Style.cornerRadius
|
||||
rootMeter: energyMeters.count > 0 ? energyMeters.get(0) : null
|
||||
rootMeter: root.rootMeter
|
||||
meters: producers
|
||||
title: qsTr("Total produced energy")
|
||||
visible: producers.count > 0
|
||||
visible: root.rootMeter || producers.count > 0
|
||||
multiplier: -1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue