File indexing completed on 2024-05-05 14:06:50
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 }