Libksysguard

Sensor.cpp
1/*
2 SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3 SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "Sensor.h"
9
10#include <chrono>
11#include <optional>
12
13#include <QEvent>
14
15#include "formatter/Formatter.h"
16#include "systemstats/SensorInfo.h"
17
18#include "Sensor.h"
19#include "SensorDaemonInterface_p.h"
20#include "SensorQuery.h"
21
22using namespace KSysGuard;
23namespace chrono = std::chrono;
24
25class Q_DECL_HIDDEN Sensor::Private
26{
27public:
28 SensorInfo sensorInfo;
29
30 Sensor::Status status = Sensor::Status::Unknown;
31 QVariant value;
32
33 bool usedByQml = false;
34 bool componentComplete = false;
35
36 QString pendingId;
37 QString id;
38
39 bool enabled = true;
40
41 std::optional<int> updateRateLimit;
42 chrono::steady_clock::time_point lastUpdate;
43};
44
45Sensor::Sensor(QObject *parent)
46 : Sensor(QString{}, parent)
47{
48}
49
50Sensor::Sensor(const QString &id, QObject *parent)
51 : QObject(parent)
52 , d(new Private())
53{
54 connect(this, &Sensor::statusChanged, this, &Sensor::valueChanged);
55 connect(this, &Sensor::statusChanged, this, &Sensor::metaDataChanged);
56 connect(this, &Sensor::enabledChanged, this, &Sensor::onEnabledChanged);
57
58 setSensorId(id);
59}
60
61Sensor::Sensor(const SensorQuery &query, int index, QObject *parent)
62 : Sensor(QString{}, parent)
63{
64 if (index >= 0 && index < query.result().size()) {
65 auto result = query.result().at(index);
66 d->id = result.first;
67 onMetaDataChanged(d->id, result.second);
68 onEnabledChanged();
69 }
70}
71
72bool Sensor::event(QEvent *event)
73{
74 if (event->type() == QEvent::ParentAboutToChange && parent()) {
75 parent()->disconnect(this);
76 } else if (event->type() == QEvent::ParentChange && parent()) {
77 if (parent()->metaObject()->indexOfSignal("enabledChanged()") != -1) {
78 connect(parent(), SIGNAL(enabledChanged()), this, SIGNAL(enabledChanged()));
79 }
80 }
81
82 return QObject::event(event);
83}
84
85Sensor::~Sensor()
86{
87 SensorDaemonInterface::instance()->unsubscribe(d->id);
88}
89
91{
92 return d->id;
93}
94
95void Sensor::setSensorId(const QString &id)
96{
97 if (id == d->id) {
98 return;
99 }
100
101 if (d->usedByQml && !d->componentComplete) {
102 d->pendingId = id;
103 return;
104 }
105
106 d->id = id;
107 d->status = Sensor::Status::Loading;
108
109 if (!id.isEmpty()) {
110 SensorDaemonInterface::instance()->requestMetaData(id);
111 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &Sensor::onMetaDataChanged, Qt::UniqueConnection);
112 }
113
114 if (enabled()) {
115 SensorDaemonInterface::instance()->subscribe(id);
116 SensorDaemonInterface::instance()->requestValue(id);
117 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::valueChanged, this, &Sensor::onValueChanged, Qt::UniqueConnection);
118 }
119
120 Q_EMIT sensorIdChanged();
121 Q_EMIT statusChanged();
122}
123
125{
126 return d->status;
127}
128
129QString Sensor::name() const
130{
131 return d->sensorInfo.name;
132}
133
135{
136 if (d->sensorInfo.shortName.isEmpty()) {
137 return d->sensorInfo.name;
138 }
139
140 return d->sensorInfo.shortName;
141}
142
144{
145 return d->sensorInfo.description;
146}
147
148Unit Sensor::unit() const
149{
150 return d->sensorInfo.unit;
151}
152
153qreal Sensor::minimum() const
154{
155 return d->sensorInfo.min;
156}
157
158qreal Sensor::maximum() const
159{
160 return d->sensorInfo.max;
161}
162
164{
165 return d->sensorInfo.variantType;
166}
167
169{
170 if (!d->value.isValid()) {
171 return QVariant{d->sensorInfo.variantType};
172 }
173 return d->value;
174}
175
176QString Sensor::formattedValue() const
177{
178 return Formatter::formatValue(value(), unit(), MetricPrefixAutoAdjust, FormatOptionShowNull);
179}
180
181bool Sensor::enabled() const
182{
183 if (d->enabled && parent()) {
184 auto parentEnabled = parent()->property("enabled");
185 if (parentEnabled.isValid()) {
186 return parentEnabled.toBool();
187 }
188 }
189
190 return d->enabled;
191}
192
193void Sensor::setEnabled(bool newEnabled)
194{
195 if (newEnabled == d->enabled) {
196 return;
197 }
198
199 d->enabled = newEnabled;
200 Q_EMIT enabledChanged();
201}
202
203uint Sensor::updateInterval() const
204{
205 // TODO: Make this dynamic once the backend supports that.
206 return BackendUpdateInterval;
207}
208
209int Sensor::updateRateLimit() const
210{
211 return d->updateRateLimit.value_or(-1);
212}
213
214void Sensor::setUpdateRateLimit(int newUpdateRateLimit)
215{
216 // An update rate limit of 0 or less makes no sense, so treat it as clearing
217 // the limit.
218 if (newUpdateRateLimit <= 0) {
219 if (!d->updateRateLimit) {
220 return;
221 }
222
223 d->updateRateLimit.reset();
224 } else {
225 if (d->updateRateLimit && d->updateRateLimit.value() == newUpdateRateLimit) {
226 return;
227 }
228
229 d->updateRateLimit = newUpdateRateLimit;
230 }
231
232 d->lastUpdate = chrono::steady_clock::now();
233 Q_EMIT updateRateLimitChanged();
234}
235
236void KSysGuard::Sensor::resetUpdateRateLimit()
237{
238 setUpdateRateLimit(-1);
239}
240
241void Sensor::classBegin()
242{
243 d->usedByQml = true;
244}
245
246void Sensor::componentComplete()
247{
248 d->componentComplete = true;
249
250 setSensorId(d->pendingId);
251
252 if (parent() && parent()->metaObject()->indexOfSignal("enabledChanged()") != -1) {
253 connect(parent(), SIGNAL(enabledChanged()), this, SIGNAL(enabledChanged()));
254 }
255}
256
257void Sensor::onMetaDataChanged(const QString &sensorId, const SensorInfo &metaData)
258{
259 if (sensorId != d->id || !enabled()) {
260 return;
261 }
262
263 d->sensorInfo = metaData;
264
265 if (d->status == Sensor::Status::Loading) {
266 d->status = Sensor::Status::Ready;
267 Q_EMIT statusChanged();
268 }
269
271}
272
273void Sensor::onValueChanged(const QString &sensorId, const QVariant &value)
274{
275 if (sensorId != d->id || !enabled()) {
276 return;
277 }
278
279 if (d->updateRateLimit && d->value.isValid()) {
280 auto updateRateLimit = chrono::steady_clock::duration(chrono::milliseconds(d->updateRateLimit.value()));
281 auto now = chrono::steady_clock::now();
282 if (now - d->lastUpdate < updateRateLimit) {
283 return;
284 } else {
285 d->lastUpdate = now;
286 }
287 }
288
289 d->value = value;
290 Q_EMIT valueChanged();
291}
292
293void Sensor::onEnabledChanged()
294{
295 if (enabled()) {
296 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &Sensor::onMetaDataChanged, Qt::UniqueConnection);
297 connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::valueChanged, this, &Sensor::onValueChanged, Qt::UniqueConnection);
298
299 SensorDaemonInterface::instance()->subscribe(d->id);
300 // Force an update of metadata and data, since that may have changed
301 // while we were disabled.
302 SensorDaemonInterface::instance()->requestMetaData(d->id);
303 SensorDaemonInterface::instance()->requestValue(d->id);
304 } else {
305 disconnect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &Sensor::onMetaDataChanged);
306 disconnect(SensorDaemonInterface::instance(), &SensorDaemonInterface::valueChanged, this, &Sensor::onValueChanged);
307 SensorDaemonInterface::instance()->unsubscribe(d->id);
308 }
309}
static QString formatValue(const QVariant &value, Unit unit, MetricPrefix targetPrefix=MetricPrefixAutoAdjust, FormatOptions options=FormatOptionNone)
Converts value to the appropriate displayable string.
An object to query the daemon for a list of sensors and their metadata.
Definition SensorQuery.h:26
An object encapsulating a backend sensor.
Definition Sensor.h:34
int updateRateLimit
The minimum time between updates, in milliseconds.
Definition Sensor.h:117
QVariant::Type type
The QVariant type for this sensor.
Definition Sensor.h:73
Status
This enum type is used to specify status of the Sensor.
Definition Sensor.h:123
@ Loading
The sensor is currently being loaded.
@ Ready
The sensor has been loaded.
qreal maximum
The maximum value this Sensor can have.
Definition Sensor.h:67
KSysGuard::Unit unit
The unit of this Sensor.
Definition Sensor.h:59
QString shortName
A shortened name that can be displayed when space is constrained.
Definition Sensor.h:51
QVariant value
The current value of this sensor.
Definition Sensor.h:86
QString description
A description of the Sensor.
Definition Sensor.h:55
QString name
The user-visible name of this Sensor.
Definition Sensor.h:45
bool enabled
Should this Sensor check for changes?
Definition Sensor.h:97
QString sensorId
The path to the backend sensor this Sensor represents.
Definition Sensor.h:41
qreal minimum
The minimum value this Sensor can have.
Definition Sensor.h:63
Q_SIGNAL void metaDataChanged() const
This signal is emitted when any of the metadata properties change.
Status status
The status of the sensor.
Definition Sensor.h:82
uint updateInterval
The time in milliseconds between each update of the sensor.
Definition Sensor.h:108
Q_SCRIPTABLE CaptureState status()
ParentAboutToChange
const_reference at(qsizetype i) const const
qsizetype size() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool event(QEvent *e)
virtual const QMetaObject * metaObject() const const
QObject * parent() const const
QVariant property(const char *name) const const
UniqueConnection
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool toBool() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:21:23 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.