Warning, file /plasma/libksysguard/systemstats/AggregateSensor.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2019 Arjen Hiemstra <ahiemstra@heimr.nl>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "AggregateSensor.h"
0008 
0009 #include "SensorContainer.h"
0010 #include <QTimer>
0011 
0012 using namespace KSysGuard;
0013 
0014 // Add two QVariants together.
0015 //
0016 // This will try to add two QVariants together based on the type of first. This
0017 // will try to convert first and second to a common type, add them, then convert
0018 // back to the type of first.
0019 //
0020 // If any conversion fails or there is no way to add two types, first will be
0021 // returned.
0022 QVariant addVariants(const QVariant &first, const QVariant &second)
0023 {
0024     auto result = QVariant{};
0025 
0026     bool convertFirst = false;
0027     bool convertSecond = false;
0028 
0029     auto type = first.type();
0030     switch (static_cast<QMetaType::Type>(type)) {
0031     case QMetaType::Char:
0032     case QMetaType::Short:
0033     case QMetaType::Int:
0034     case QMetaType::Long:
0035     case QMetaType::LongLong:
0036         result = first.toLongLong(&convertFirst) + second.toLongLong(&convertSecond);
0037         break;
0038     case QMetaType::UChar:
0039     case QMetaType::UShort:
0040     case QMetaType::UInt:
0041     case QMetaType::ULong:
0042     case QMetaType::ULongLong:
0043         result = first.toULongLong(&convertFirst) + second.toULongLong(&convertSecond);
0044         break;
0045     case QMetaType::Float:
0046     case QMetaType::Double:
0047         result = first.toDouble(&convertFirst) + second.toDouble(&convertSecond);
0048         break;
0049     default:
0050         return first;
0051     }
0052 
0053     if (!convertFirst || !convertSecond) {
0054         return first;
0055     }
0056 
0057     if (!result.convert(type)) {
0058         return first;
0059     }
0060 
0061     return result;
0062 }
0063 
0064 class Q_DECL_HIDDEN AggregateSensor::Private
0065 {
0066 public:
0067     QRegularExpression matchObjects;
0068     QString matchProperty;
0069     SensorHash sensors;
0070     bool dataChangeQueued = false;
0071     int dataCompressionDuration = 100;
0072     SensorContainer *subsystem = nullptr;
0073 
0074     std::function<QVariant(AggregateSensor::SensorIterator, const AggregateSensor::SensorIterator)> aggregateFunction;
0075 };
0076 
0077 QVariant AggregateSensor::SensorIterator::operator*() const
0078 {
0079     return m_it.value()->value();
0080 }
0081 
0082 AggregateSensor::SensorIterator &AggregateSensor::SensorIterator::operator++()
0083 {
0084     do {
0085         ++m_it;
0086     } while (!(m_it == m_end || m_it.value()));
0087     return *this;
0088 }
0089 
0090 AggregateSensor::SensorIterator AggregateSensor::SensorIterator::operator++(int)
0091 {
0092     AggregateSensor::SensorIterator tmp = *this;
0093     operator++();
0094     return tmp;
0095 }
0096 
0097 bool AggregateSensor::SensorIterator::operator==(const SensorIterator &other) const
0098 {
0099     return m_it == other.m_it;
0100 }
0101 
0102 bool AggregateSensor::SensorIterator::operator!=(const SensorIterator &other) const
0103 {
0104     return m_it != other.m_it;
0105 }
0106 
0107 AggregateSensor::AggregateSensor(SensorObject *provider, const QString &id, const QString &name)
0108     : AggregateSensor(provider, id, name, QVariant{})
0109 {
0110 }
0111 
0112 AggregateSensor::AggregateSensor(SensorObject *provider, const QString &id, const QString &name, const QVariant &initialValue)
0113     : SensorProperty(id, name, initialValue, provider)
0114     , d(std::make_unique<Private>())
0115 {
0116     d->subsystem = qobject_cast<SensorContainer *>(provider->parent());
0117     setAggregateFunction(addVariants);
0118     connect(d->subsystem, &SensorContainer::objectAdded, this, &AggregateSensor::updateSensors);
0119     connect(d->subsystem, &SensorContainer::objectRemoved, this, &AggregateSensor::updateSensors);
0120 }
0121 
0122 AggregateSensor::~AggregateSensor() = default;
0123 
0124 QRegularExpression AggregateSensor::matchSensors() const
0125 {
0126     return d->matchObjects;
0127 }
0128 
0129 QVariant AggregateSensor::value() const
0130 {
0131     if (d->sensors.isEmpty()) {
0132         return QVariant();
0133     }
0134 
0135     auto it = d->sensors.constBegin();
0136     while (!it.value() && it != d->sensors.constEnd()) {
0137         it++;
0138     }
0139 
0140     if (it == d->sensors.constEnd()) {
0141         return QVariant{};
0142     }
0143 
0144     auto begin = SensorIterator(it, d->sensors.constEnd());
0145     const auto end = SensorIterator(d->sensors.constEnd(), d->sensors.constEnd());
0146     auto result = d->aggregateFunction(begin, end);
0147 
0148     return result;
0149 }
0150 
0151 void AggregateSensor::subscribe()
0152 {
0153     bool wasSubscribed = SensorProperty::isSubscribed();
0154     SensorProperty::subscribe();
0155     if (!wasSubscribed && isSubscribed()) {
0156         for (auto sensor : std::as_const(d->sensors)) {
0157             if (sensor) {
0158                 sensor->subscribe();
0159             }
0160         }
0161     }
0162 }
0163 
0164 void AggregateSensor::unsubscribe()
0165 {
0166     bool wasSubscribed = SensorProperty::isSubscribed();
0167     SensorProperty::unsubscribe();
0168     if (wasSubscribed && !isSubscribed()) {
0169         for (auto sensor : std::as_const(d->sensors)) {
0170             if (sensor) {
0171                 sensor->unsubscribe();
0172             }
0173         }
0174     }
0175 }
0176 
0177 void AggregateSensor::setMatchSensors(const QRegularExpression &objectIds, const QString &propertyName)
0178 {
0179     if (objectIds == d->matchObjects && propertyName == d->matchProperty) {
0180         return;
0181     }
0182 
0183     d->matchProperty = propertyName;
0184     d->matchObjects = objectIds;
0185     updateSensors();
0186 }
0187 
0188 std::function<QVariant(AggregateSensor::SensorIterator, AggregateSensor::SensorIterator)> AggregateSensor::aggregateFunction() const
0189 {
0190     return d->aggregateFunction;
0191 }
0192 
0193 void AggregateSensor::setAggregateFunction(const std::function<QVariant(QVariant, QVariant)> &newAggregateFunction)
0194 {
0195     auto aggregateFunction = [newAggregateFunction](AggregateSensor::SensorIterator begin, const AggregateSensor::SensorIterator end) -> QVariant {
0196         auto &it = begin;
0197         QVariant result = *begin;
0198         ++it;
0199         for (; it != end; ++it) {
0200             result = newAggregateFunction(result, *it);
0201         }
0202         return result;
0203     };
0204 
0205     d->aggregateFunction = aggregateFunction;
0206 }
0207 
0208 void AggregateSensor::setAggregateFunction(const std::function<QVariant(SensorIterator, const SensorIterator)> &newAggregateFunction)
0209 {
0210     d->aggregateFunction = newAggregateFunction;
0211 }
0212 
0213 void AggregateSensor::addSensor(SensorProperty *sensor)
0214 {
0215     if (!sensor || sensor->path() == path() || d->sensors.contains(sensor->path())) {
0216         return;
0217     }
0218 
0219     if (isSubscribed()) {
0220         sensor->subscribe();
0221     }
0222 
0223     connect(sensor, &SensorProperty::valueChanged, this, [this, sensor]() {
0224         sensorDataChanged(sensor);
0225     });
0226     d->sensors.insert(sensor->path(), sensor);
0227 }
0228 
0229 void AggregateSensor::removeSensor(const QString &sensorPath)
0230 {
0231     auto sensor = d->sensors.take(sensorPath);
0232     sensor->disconnect(this);
0233     if (isSubscribed()) {
0234         sensor->unsubscribe();
0235     }
0236 }
0237 
0238 int AggregateSensor::matchCount() const
0239 {
0240     return d->sensors.size();
0241 }
0242 
0243 void AggregateSensor::updateSensors()
0244 {
0245     if (!d->matchObjects.isValid()) {
0246         return;
0247     }
0248 
0249     auto itr = d->sensors.begin();
0250     while (itr != d->sensors.end()) {
0251         if (!itr.value()) {
0252             itr = d->sensors.erase(itr);
0253         } else if (itr.value()->parent() && itr.value()->parent()->parent() != d->subsystem) {
0254             itr.value()->disconnect(this);
0255             if (isSubscribed()) {
0256                 itr.value()->unsubscribe();
0257             }
0258             itr = d->sensors.erase(itr);
0259         } else {
0260             ++itr;
0261         }
0262     }
0263 
0264     for (auto obj : d->subsystem->objects()) {
0265         if (d->matchObjects.match(obj->id()).hasMatch()) {
0266             auto sensor = obj->sensor(d->matchProperty);
0267             if (sensor) {
0268                 addSensor(sensor);
0269             }
0270         }
0271     }
0272 
0273     delayedEmitDataChanged();
0274 }
0275 
0276 void AggregateSensor::sensorDataChanged(SensorProperty *sensor)
0277 {
0278     Q_UNUSED(sensor)
0279     delayedEmitDataChanged();
0280 }
0281 
0282 void AggregateSensor::delayedEmitDataChanged()
0283 {
0284     if (!d->dataChangeQueued) {
0285         d->dataChangeQueued = true;
0286         QTimer::singleShot(d->dataCompressionDuration, [this]() {
0287             Q_EMIT valueChanged();
0288             d->dataChangeQueued = false;
0289         });
0290     }
0291 }
0292 
0293 class Q_DECL_HIDDEN PercentageSensor::Private
0294 {
0295 public:
0296     SensorProperty *sensor = nullptr;
0297 };
0298 
0299 PercentageSensor::PercentageSensor(SensorObject *provider, const QString &id, const QString &name)
0300     : SensorProperty(id, name, provider)
0301     , d(std::make_unique<Private>())
0302 {
0303     setUnit(KSysGuard::UnitPercent);
0304     setMax(100);
0305 }
0306 
0307 PercentageSensor::~PercentageSensor() = default;
0308 
0309 void PercentageSensor::setBaseSensor(SensorProperty *property)
0310 {
0311     d->sensor = property;
0312     connect(property, &SensorProperty::valueChanged, this, &PercentageSensor::valueChanged);
0313     connect(property, &SensorProperty::sensorInfoChanged, this, &PercentageSensor::valueChanged);
0314 }
0315 
0316 QVariant PercentageSensor::value() const
0317 {
0318     if (!d->sensor) {
0319         return QVariant();
0320     }
0321     QVariant value = d->sensor->value();
0322     if (!value.isValid()) {
0323         return QVariant();
0324     }
0325     return (value.toReal() / d->sensor->info().max) * 100.0;
0326 }
0327 
0328 void PercentageSensor::subscribe()
0329 {
0330     d->sensor->subscribe();
0331 }
0332 
0333 void PercentageSensor::unsubscribe()
0334 {
0335     d->sensor->unsubscribe();
0336 }