File indexing completed on 2024-04-21 05:31:41

0001 /*
0002     SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003     SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "Sensor.h"
0009 
0010 #include <chrono>
0011 #include <optional>
0012 
0013 #include <QEvent>
0014 
0015 #include "formatter/Formatter.h"
0016 #include "systemstats/SensorInfo.h"
0017 
0018 #include "Sensor.h"
0019 #include "SensorDaemonInterface_p.h"
0020 #include "SensorQuery.h"
0021 
0022 using namespace KSysGuard;
0023 namespace chrono = std::chrono;
0024 
0025 class Q_DECL_HIDDEN Sensor::Private
0026 {
0027 public:
0028     SensorInfo sensorInfo;
0029 
0030     Sensor::Status status = Sensor::Status::Unknown;
0031     QVariant value;
0032 
0033     bool usedByQml = false;
0034     bool componentComplete = false;
0035 
0036     QString pendingId;
0037     QString id;
0038 
0039     bool enabled = true;
0040 
0041     std::optional<int> updateRateLimit;
0042     chrono::steady_clock::time_point lastUpdate;
0043 };
0044 
0045 Sensor::Sensor(QObject *parent)
0046     : Sensor(QString{}, parent)
0047 {
0048 }
0049 
0050 Sensor::Sensor(const QString &id, QObject *parent)
0051     : QObject(parent)
0052     , d(new Private())
0053 {
0054     connect(this, &Sensor::statusChanged, this, &Sensor::valueChanged);
0055     connect(this, &Sensor::statusChanged, this, &Sensor::metaDataChanged);
0056     connect(this, &Sensor::enabledChanged, this, &Sensor::onEnabledChanged);
0057 
0058     setSensorId(id);
0059 }
0060 
0061 Sensor::Sensor(const SensorQuery &query, int index, QObject *parent)
0062     : Sensor(QString{}, parent)
0063 {
0064     if (index >= 0 && index < query.result().size()) {
0065         auto result = query.result().at(index);
0066         d->id = result.first;
0067         onMetaDataChanged(d->id, result.second);
0068         onEnabledChanged();
0069     }
0070 }
0071 
0072 bool Sensor::event(QEvent *event)
0073 {
0074     if (event->type() == QEvent::ParentAboutToChange && parent()) {
0075         parent()->disconnect(this);
0076     } else if (event->type() == QEvent::ParentChange && parent()) {
0077         if (parent()->metaObject()->indexOfSignal("enabledChanged()") != -1) {
0078             connect(parent(), SIGNAL(enabledChanged()), this, SIGNAL(enabledChanged()));
0079         }
0080     }
0081 
0082     return QObject::event(event);
0083 }
0084 
0085 Sensor::~Sensor()
0086 {
0087     SensorDaemonInterface::instance()->unsubscribe(d->id);
0088 }
0089 
0090 QString Sensor::sensorId() const
0091 {
0092     return d->id;
0093 }
0094 
0095 void Sensor::setSensorId(const QString &id)
0096 {
0097     if (id == d->id) {
0098         return;
0099     }
0100 
0101     if (d->usedByQml && !d->componentComplete) {
0102         d->pendingId = id;
0103         return;
0104     }
0105 
0106     d->id = id;
0107     d->status = Sensor::Status::Loading;
0108 
0109     if (!id.isEmpty()) {
0110         SensorDaemonInterface::instance()->requestMetaData(id);
0111         connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &Sensor::onMetaDataChanged, Qt::UniqueConnection);
0112     }
0113 
0114     if (enabled()) {
0115         SensorDaemonInterface::instance()->subscribe(id);
0116         SensorDaemonInterface::instance()->requestValue(id);
0117         connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::valueChanged, this, &Sensor::onValueChanged, Qt::UniqueConnection);
0118     }
0119 
0120     Q_EMIT sensorIdChanged();
0121     Q_EMIT statusChanged();
0122 }
0123 
0124 Sensor::Status Sensor::status() const
0125 {
0126     return d->status;
0127 }
0128 
0129 QString Sensor::name() const
0130 {
0131     return d->sensorInfo.name;
0132 }
0133 
0134 QString Sensor::shortName() const
0135 {
0136     if (d->sensorInfo.shortName.isEmpty()) {
0137         return d->sensorInfo.name;
0138     }
0139 
0140     return d->sensorInfo.shortName;
0141 }
0142 
0143 QString Sensor::description() const
0144 {
0145     return d->sensorInfo.description;
0146 }
0147 
0148 Unit Sensor::unit() const
0149 {
0150     return d->sensorInfo.unit;
0151 }
0152 
0153 qreal Sensor::minimum() const
0154 {
0155     return d->sensorInfo.min;
0156 }
0157 
0158 qreal Sensor::maximum() const
0159 {
0160     return d->sensorInfo.max;
0161 }
0162 
0163 QVariant::Type Sensor::type() const
0164 {
0165     return d->sensorInfo.variantType;
0166 }
0167 
0168 QVariant Sensor::value() const
0169 {
0170     if (!d->value.isValid()) {
0171         return QVariant{d->sensorInfo.variantType};
0172     }
0173     return d->value;
0174 }
0175 
0176 QString Sensor::formattedValue() const
0177 {
0178     return Formatter::formatValue(value(), unit(), MetricPrefixAutoAdjust, FormatOptionShowNull);
0179 }
0180 
0181 bool Sensor::enabled() const
0182 {
0183     if (d->enabled && parent()) {
0184         auto parentEnabled = parent()->property("enabled");
0185         if (parentEnabled.isValid()) {
0186             return parentEnabled.toBool();
0187         }
0188     }
0189 
0190     return d->enabled;
0191 }
0192 
0193 void Sensor::setEnabled(bool newEnabled)
0194 {
0195     if (newEnabled == d->enabled) {
0196         return;
0197     }
0198 
0199     d->enabled = newEnabled;
0200     Q_EMIT enabledChanged();
0201 }
0202 
0203 uint Sensor::updateInterval() const
0204 {
0205     // TODO: Make this dynamic once the backend supports that.
0206     return BackendUpdateInterval;
0207 }
0208 
0209 int Sensor::updateRateLimit() const
0210 {
0211     return d->updateRateLimit.value_or(-1);
0212 }
0213 
0214 void Sensor::setUpdateRateLimit(int newUpdateRateLimit)
0215 {
0216     // An update rate limit of 0 or less makes no sense, so treat it as clearing
0217     // the limit.
0218     if (newUpdateRateLimit <= 0) {
0219         if (!d->updateRateLimit) {
0220             return;
0221         }
0222 
0223         d->updateRateLimit.reset();
0224     } else {
0225         if (d->updateRateLimit && d->updateRateLimit.value() == newUpdateRateLimit) {
0226             return;
0227         }
0228 
0229         d->updateRateLimit = newUpdateRateLimit;
0230     }
0231 
0232     d->lastUpdate = chrono::steady_clock::now();
0233     Q_EMIT updateRateLimitChanged();
0234 }
0235 
0236 void KSysGuard::Sensor::resetUpdateRateLimit()
0237 {
0238     setUpdateRateLimit(-1);
0239 }
0240 
0241 void Sensor::classBegin()
0242 {
0243     d->usedByQml = true;
0244 }
0245 
0246 void Sensor::componentComplete()
0247 {
0248     d->componentComplete = true;
0249 
0250     setSensorId(d->pendingId);
0251 
0252     if (parent() && parent()->metaObject()->indexOfSignal("enabledChanged()") != -1) {
0253         connect(parent(), SIGNAL(enabledChanged()), this, SIGNAL(enabledChanged()));
0254     }
0255 }
0256 
0257 void Sensor::onMetaDataChanged(const QString &sensorId, const SensorInfo &metaData)
0258 {
0259     if (sensorId != d->id || !enabled()) {
0260         return;
0261     }
0262 
0263     d->sensorInfo = metaData;
0264 
0265     if (d->status == Sensor::Status::Loading) {
0266         d->status = Sensor::Status::Ready;
0267         Q_EMIT statusChanged();
0268     }
0269 
0270     Q_EMIT metaDataChanged();
0271 }
0272 
0273 void Sensor::onValueChanged(const QString &sensorId, const QVariant &value)
0274 {
0275     if (sensorId != d->id || !enabled()) {
0276         return;
0277     }
0278 
0279     if (d->updateRateLimit && d->value.isValid()) {
0280         auto updateRateLimit = chrono::steady_clock::duration(chrono::milliseconds(d->updateRateLimit.value()));
0281         auto now = chrono::steady_clock::now();
0282         if (now - d->lastUpdate < updateRateLimit) {
0283             return;
0284         } else {
0285             d->lastUpdate = now;
0286         }
0287     }
0288 
0289     d->value = value;
0290     Q_EMIT valueChanged();
0291 }
0292 
0293 void Sensor::onEnabledChanged()
0294 {
0295     if (enabled()) {
0296         connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &Sensor::onMetaDataChanged, Qt::UniqueConnection);
0297         connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::valueChanged, this, &Sensor::onValueChanged, Qt::UniqueConnection);
0298 
0299         SensorDaemonInterface::instance()->subscribe(d->id);
0300         // Force an update of metadata and data, since that may have changed
0301         // while we were disabled.
0302         SensorDaemonInterface::instance()->requestMetaData(d->id);
0303         SensorDaemonInterface::instance()->requestValue(d->id);
0304     } else {
0305         disconnect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &Sensor::onMetaDataChanged);
0306         disconnect(SensorDaemonInterface::instance(), &SensorDaemonInterface::valueChanged, this, &Sensor::onValueChanged);
0307         SensorDaemonInterface::instance()->unsubscribe(d->id);
0308     }
0309 }