Commit 2e0c9de4 authored by Arjen Hiemstra's avatar Arjen Hiemstra
Browse files

Add an updateRateLimit property to Sensor and SensorDataModel

This can be used to rate-limit value updates to reduce the number of
changes in the UI.
parent 38cd0763
......@@ -18,6 +18,11 @@
Boston, MA 02110-1301, USA.
*/
#include "Sensor.h"
#include <optional>
#include <chrono>
#include <QEvent>
#include "formatter/Formatter.h"
......@@ -29,6 +34,7 @@
using namespace KSysGuard;
namespace chrono = std::chrono;
class Q_DECL_HIDDEN Sensor::Private
{
......@@ -45,6 +51,9 @@ public:
QString id;
bool enabled = true;
std::optional<int> updateRateLimit;
chrono::steady_clock::time_point lastUpdate;
};
Sensor::Sensor(QObject *parent)
......@@ -211,6 +220,37 @@ uint Sensor::updateInterval() const
return BackendUpdateInterval;
}
int Sensor::updateRateLimit() const
{
return d->updateRateLimit.value_or(-1);
}
void Sensor::setUpdateRateLimit(int newUpdateRateLimit)
{
// An update rate limit of 0 or less makes no sense, so treat it as clearing
// the limit.
if (newUpdateRateLimit <= 0) {
if (!d->updateRateLimit) {
return;
}
d->updateRateLimit.reset();
} else {
if (d->updateRateLimit && d->updateRateLimit.value() == newUpdateRateLimit) {
return;
}
d->updateRateLimit = newUpdateRateLimit;
}
d->lastUpdate = chrono::steady_clock::now();
Q_EMIT updateRateLimitChanged();
}
void KSysGuard::Sensor::resetUpdateRateLimit()
{
setUpdateRateLimit(-1);
}
void Sensor::classBegin()
{
d->usedByQml = true;
......@@ -249,6 +289,16 @@ void Sensor::onValueChanged(const QString &sensorId, const QVariant &value)
return;
}
if (d->updateRateLimit) {
auto updateRateLimit = chrono::steady_clock::duration(chrono::milliseconds(d->updateRateLimit.value()));
auto now = chrono::steady_clock::now();
if (now - d->lastUpdate < updateRateLimit) {
return;
} else {
d->lastUpdate = now;
}
}
d->value = value;
Q_EMIT valueChanged();
}
......
......@@ -119,6 +119,15 @@ class SENSORS_EXPORT Sensor : public QObject, public QQmlParserStatus
* that we can support per-sensor update rates.
*/
Q_PROPERTY(uint updateInterval READ updateInterval NOTIFY updateIntervalChanged)
/**
* The minimum time between updates, in milliseconds.
*
* If this is set to a positive non-zero value, at least this many
* milliseconds need to elapse before another value update happens, otherwise
* it is ignored. This effectively rate-limits the updates and will prevent
* value updates.
*/
Q_PROPERTY(int updateRateLimit READ updateRateLimit WRITE setUpdateRateLimit NOTIFY updateRateLimitChanged RESET resetUpdateRateLimit)
public:
/**
......@@ -187,6 +196,11 @@ public:
uint updateInterval() const;
Q_SIGNAL void updateIntervalChanged();
int updateRateLimit() const;
void setUpdateRateLimit(int newUpdateRateLimit);
void resetUpdateRateLimit();
Q_SIGNAL void updateRateLimitChanged();
void classBegin() override;
void componentComplete() override;
......
......@@ -21,6 +21,7 @@
#include "SensorDataModel.h"
#include <optional>
#include <chrono>
#include <QMetaEnum>
......@@ -32,6 +33,8 @@
using namespace KSysGuard;
namespace chrono = std::chrono;
class Q_DECL_HIDDEN SensorDataModel::Private
{
public:
......@@ -61,6 +64,9 @@ public:
std::optional<qreal> minimum;
std::optional<qreal> maximum;
std::optional<int> updateRateLimit;
QHash<int, std::chrono::steady_clock::time_point> lastUpdateTimes;
private:
SensorDataModel *q;
};
......@@ -297,6 +303,37 @@ void SensorDataModel::setSensorColors(const QVariantMap &sensorColors)
Q_EMIT dataChanged(index(0,0), index(rowCount() - 1, columnCount() - 1), {Color});
}
int SensorDataModel::updateRateLimit() const
{
return d->updateRateLimit.value_or(-1);
}
void SensorDataModel::setUpdateRateLimit(int newUpdateRateLimit)
{
// An update rate limit of 0 or less makes no sense, so treat it as clearing
// the limit.
if (newUpdateRateLimit <= 0) {
if (!d->updateRateLimit) {
return;
}
d->updateRateLimit.reset();
} else {
if (d->updateRateLimit && d->updateRateLimit.value() == newUpdateRateLimit) {
return;
}
d->updateRateLimit = newUpdateRateLimit;
}
d->lastUpdateTimes.clear();
Q_EMIT updateRateLimitChanged();
}
void KSysGuard::SensorDataModel::resetUpdateRateLimit()
{
setUpdateRateLimit(-1);
}
bool KSysGuard::SensorDataModel::isReady() const
{
return d->sensors.size() == d->sensorInfos.size();
......@@ -429,6 +466,16 @@ void SensorDataModel::onValueChanged(const QString &sensorId, const QVariant &va
return;
}
if (d->updateRateLimit) {
auto updateRateLimit = chrono::steady_clock::duration(chrono::milliseconds(d->updateRateLimit.value()));
auto now = chrono::steady_clock::now();
if (d->lastUpdateTimes.contains(column) && now - d->lastUpdateTimes.value(column) < updateRateLimit) {
return;
} else {
d->lastUpdateTimes[column] = now;
}
}
d->sensorData[sensorId] = value;
Q_EMIT dataChanged(index(0, column), index(0, column), {Qt::DisplayRole, Value, FormattedValue});
}
......@@ -442,6 +489,7 @@ void SensorDataModel::Private::sensorsChanged()
sensors.clear();
sensorData.clear();
sensorInfos.clear();
lastUpdateTimes.clear();
sensors = requestedSensors;
......
......@@ -70,6 +70,15 @@ class SENSORS_EXPORT SensorDataModel : public QAbstractTableModel, public QQmlPa
* Used by the model to provide data for the Color role if set.
*/
Q_PROPERTY(QVariantMap sensorColors READ sensorColors WRITE setSensorColors NOTIFY sensorColorsChanged)
/**
* The minimum time between updates, in milliseconds.
*
* If this is set to a positive non-zero value, at least this many
* milliseconds need to elapse before another value update happens, otherwise
* it is ignored. This effectively rate-limits the updates and will prevent
* value updates.
*/
Q_PROPERTY(int updateRateLimit READ updateRateLimit WRITE setUpdateRateLimit NOTIFY updateRateLimitChanged RESET resetUpdateRateLimit)
public:
/**
......@@ -119,6 +128,11 @@ public:
void setSensorColors(const QVariantMap &sensorColors);
Q_SIGNAL void sensorColorsChanged();
int updateRateLimit() const;
void setUpdateRateLimit(int newUpdateRateLimit);
void resetUpdateRateLimit();
Q_SIGNAL void updateRateLimitChanged();
bool isReady() const;
Q_SIGNAL void readyChanged();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment