Commit 5cfc09fd authored by Arjen Hiemstra's avatar Arjen Hiemstra

Add KSysGuard::Sensors library

Summary:
This adds a new library for communicating with a new KSysGuard
DBus-based daemon that provides sensors. The daemon will be
posted by d_ed later on.

This new library pretty much maps the different objects from
the daemon to C++ objects.

Depends on D28333

Test Plan: None yet, needs the new daemon.

Reviewers: #plasma, davidedmundson

Reviewed By: #plasma, davidedmundson

Subscribers: ngraham, mart, zzag, plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D28141
parent d8e46779
......@@ -109,6 +109,7 @@ add_subdirectory( formatter )
add_subdirectory( lsofui )
add_subdirectory( processcore )
add_subdirectory( processui )
add_subdirectory( sensors )
if (KF5Plasma_FOUND)
add_subdirectory( signalplotter )
endif()
......
/*
Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
Copyright (C) 2019 Vlad Zahorodnii <vladzzag@gmail.com>
formatBootTimestamp is based on TimeUtil class:
Copyright (C) 2014 Gregor Mi <codestruct@posteo.org>
......
/*
Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
Copyright (C) 2019 Vlad Zahorodnii <vladzzag@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
......
/*
Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
Copyright (C) 2019 Vlad Zahorodnii <vladzzag@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
......
/*
Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
Copyright (C) 2019 Vlad Zahorodnii <vladzzag@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
......
/*
Copyright (C) 2019 Vlad Zagorodniy <vladzzag@gmail.com>
Copyright (C) 2019 Vlad Zahorodnii <vladzzag@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
......
set(KSYSGUARD_SENSORS_SOVERSION 1)
add_subdirectory(declarative)
add_definitions(-DTRANSLATION_DOMAIN=\"ksysguard_sensors\")
set(sensors_LIB_SRCS
Sensor.cpp
SensorDataModel.cpp
SensorTreeModel.cpp
SensorQuery.cpp
SensorDaemonInterface.cpp
)
set(sensors_LIB_HEADERS
Sensor.h
SensorDataModel.h
SensorTreeModel.h
SensorQuery.h
SensorInfo_p.h
)
ecm_qt_declare_logging_category(sensors_LIB_SRCS
HEADER sensors_logging.h
IDENTIFIER LIBKSYSGUARD_SENSORS
CATEGORY_NAME org.kde.libksysguard.sensors
)
set_source_files_properties(org.kde.KSysGuardDaemon.xml PROPERTIES INCLUDE SensorInfo_p.h)
qt5_add_dbus_interface(sensors_LIB_SRCS org.kde.KSysGuardDaemon.xml ksysguarddaemon)
add_library(Sensors ${sensors_LIB_SRCS})
add_library(KSysGuard::Sensors ALIAS Sensors)
target_include_directories(Sensors
PUBLIC
"$<BUILD_INTERFACE:${libksysguard_SOURCE_DIR}>"
"$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}/ksysguard/>"
)
generate_export_header(Sensors)
target_link_libraries(Sensors
PUBLIC
Qt5::Qml
KSysGuard::Formatter
PRIVATE
Qt5::Core
Qt5::DBus
KF5::I18n
)
set_target_properties(Sensors PROPERTIES
LIBRARY_OUTPUT_NAME KSysGuardSensors
VERSION ${KSYSGUARD_VERSION_STRING}
SOVERSION ${KSYSGUARD_SENSORS_SOVERSION}
)
install(TARGETS Sensors EXPORT libksysguardLibraryTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES
${sensors_LIB_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/sensors_export.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/ksysguard/sensors
COMPONENT Devel
)
/*
Copyright (C) 2019 Vlad Zahorodnii <vladzzag@gmail.com>
Copyright (C) 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <QEvent>
#include "Sensor.h"
#include "SensorDaemonInterface_p.h"
#include "SensorInfo_p.h"
#include "formatter/Formatter.h"
#include "SensorQuery.h"
using namespace KSysGuard;
class Q_DECL_HIDDEN Sensor::Private
{
public:
SensorInfo sensorInfo;
Sensor::Status status = Sensor::Status::Unknown;
QVariant value;
bool usedByQml = false;
bool componentComplete = false;
QString pendingId;
QString id;
bool enabled = true;
};
Sensor::Sensor(QObject *parent)
: Sensor(QString{}, parent)
{
}
Sensor::Sensor(const QString &id, QObject *parent)
: QObject(parent)
, d(new Private())
{
connect(this, &Sensor::statusChanged, this, &Sensor::valueChanged);
connect(this, &Sensor::statusChanged, this, &Sensor::metaDataChanged);
connect(this, &Sensor::enabledChanged, this, &Sensor::onEnabledChanged);
setSensorId(id);
}
Sensor::Sensor(const SensorQuery &query, int index, QObject *parent)
: Sensor(QString{}, parent)
{
if (index > 0 && index < query.result().size()) {
auto result = query.result().at(index);
d->id = result.first;
onMetaDataChanged(d->id, result.second);
onEnabledChanged();
}
}
bool Sensor::event(QEvent *event)
{
if (event->type() == QEvent::ParentAboutToChange && parent()) {
parent()->disconnect(this);
} else if (event->type() == QEvent::ParentChange && parent()) {
if (parent()->metaObject()->indexOfSignal("enabledChanged()") != -1) {
connect(parent(), SIGNAL(enabledChanged()), this, SIGNAL(enabledChanged()));
}
}
return QObject::event(event);
}
Sensor::~Sensor()
{
SensorDaemonInterface::instance()->unsubscribe(d->id);
}
QString Sensor::sensorId() const
{
return d->id;
}
void Sensor::setSensorId(const QString &id)
{
if (id == d->id) {
return;
}
if (d->usedByQml && !d->componentComplete) {
d->pendingId = id;
return;
}
d->id = id;
d->status = Sensor::Status::Loading;
if (!id.isEmpty()) {
SensorDaemonInterface::instance()->requestMetaData(id);
connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::metaDataChanged, this, &Sensor::onMetaDataChanged, Qt::UniqueConnection);
}
if (enabled()) {
SensorDaemonInterface::instance()->subscribe(id);
SensorDaemonInterface::instance()->requestValue(id);
connect(SensorDaemonInterface::instance(), &SensorDaemonInterface::valueChanged, this, &Sensor::onValueChanged, Qt::UniqueConnection);
}
Q_EMIT sensorIdChanged();
Q_EMIT statusChanged();
}
Sensor::Status Sensor::status() const
{
return d->status;
}
QString Sensor::name() const
{
return d->sensorInfo.name;
}
QString Sensor::shortName() const
{
if (d->sensorInfo.shortName.isEmpty()) {
return d->sensorInfo.name;
}
return d->sensorInfo.name;
}
QString Sensor::description() const
{
return d->sensorInfo.description;
}
Unit Sensor::unit() const
{
return d->sensorInfo.unit;
}
qreal Sensor::minimum() const
{
return d->sensorInfo.min;
}
qreal Sensor::maximum() const
{
return d->sensorInfo.max;
}
QVariant::Type Sensor::type() const
{
return d->sensorInfo.variantType;
}
QVariant Sensor::value() const
{
if (!d->value.isValid()) {
return QVariant{d->sensorInfo.variantType};
}
return d->value;
}
QString Sensor::formattedValue() const
{
return Formatter::formatValue(value(), unit(), MetricPrefixAutoAdjust, FormatOptionShowNull);
}
bool Sensor::enabled() const
{
if (d->enabled && parent()) {
auto parentEnabled = parent()->property("enabled");
if (parentEnabled.isValid()) {
return parentEnabled.toBool();
}
}
return d->enabled;
}
void Sensor::setEnabled(bool newEnabled)
{
if (newEnabled == d->enabled) {
return;
}
d->enabled = newEnabled;
Q_EMIT enabledChanged();
}
void Sensor::classBegin()
{
d->usedByQml = true;
}
void Sensor::componentComplete()
{
d->componentComplete = true;
setSensorId(d->pendingId);
if (parent() && parent()->metaObject()->indexOfSignal("enabledChanged()") != -1) {
connect(parent(), SIGNAL(enabledChanged()), this, SIGNAL(enabledChanged()));
}
}
void Sensor::onMetaDataChanged(const QString &sensorId, const SensorInfo &metaData)
{
if (sensorId != d->id || !enabled()) {
return;
}
d->sensorInfo = metaData;
if (d->status == Sensor::Status::Loading) {
d->status = Sensor::Status::Ready;
Q_EMIT statusChanged();
}
Q_EMIT metaDataChanged();
}
void Sensor::onValueChanged(const QString &sensorId, const QVariant &value)
{
if (sensorId != d->id || !enabled()) {
return;
}
d->value = value;
Q_EMIT valueChanged();
}
void Sensor::onEnabledChanged()
{
if (enabled()) {
SensorDaemonInterface::instance()->subscribe(d->id);
// Force an update of metadata and data, since that may have changed
// while we were disabled.
SensorDaemonInterface::instance()->requestMetaData(d->id);
SensorDaemonInterface::instance()->requestValue(d->id);
} else {
SensorDaemonInterface::instance()->unsubscribe(d->id);
}
}
/*
Copyright (C) 2019 Vlad Zahorodnii <vladzzag@gmail.com>
Copyright (C) 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#pragma once
#include <memory>
#include <QObject>
#include <QQmlParserStatus>
#include <QString>
#include <QVariant>
#include "formatter/Unit.h"
#include "sensors_export.h"
namespace KSysGuard
{
class SensorData;
class SensorInfo;
class SensorQuery;
/**
* An object encapsulating a backend sensor.
*
* This class represents a sensor as exposed by the backend. It allows querying
* various metadata properties of the sensor as well as the current value.
*/
class SENSORS_EXPORT Sensor : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
/**
* The path to the backend sensor this Sensor represents.
*/
Q_PROPERTY(QString sensorId READ sensorId WRITE setSensorId NOTIFY sensorIdChanged)
/**
* The user-visible name of this Sensor.
*/
Q_PROPERTY(QString name READ name NOTIFY metaDataChanged)
/**
* A shortened name that can be displayed when space is constrained.
*
* The value is the same as name if shortName was not provided by the backend.
*/
Q_PROPERTY(QString shortName READ shortName NOTIFY metaDataChanged)
/**
* A description of the Sensor.
*/
Q_PROPERTY(QString description READ description NOTIFY metaDataChanged)
/**
* The unit of this Sensor.
*/
Q_PROPERTY(KSysGuard::Unit unit READ unit NOTIFY metaDataChanged)
/**
* The minimum value this Sensor can have.
*/
Q_PROPERTY(qreal minimum READ minimum NOTIFY metaDataChanged)
/**
* The maximum value this Sensor can have.
*/
Q_PROPERTY(qreal maximum READ maximum NOTIFY metaDataChanged)
/**
* The QVariant type for this sensor.
*
* This is used to create proper default values.
*/
Q_PROPERTY(QVariant::Type type READ type NOTIFY metaDataChanged)
/**
* The status of the sensor.
*
* Due to the asynchronous nature of the underlying code, sensors are not
* immediately available on construction. Instead, they need to request data
* from the daemon and wait for it to arrive. This property reflects where
* in that process this sensor is.
*/
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
/**
* The current value of this sensor.
*/
Q_PROPERTY(QVariant value READ value NOTIFY valueChanged)
/**
* A formatted version of \property value.
*/
Q_PROPERTY(QString formattedValue READ formattedValue NOTIFY valueChanged)
/**
* Should this Sensor check for changes?
*
* Note that if set to true, the sensor will only be enabled when the parent
* is also enabled.
*/
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
public:
/**
* This enum type is used to specify status of the Sensor.
*/
enum class Status {
Unknown, ///< The sensor has no ID assigned.
Loading, ///< The sensor is currently being loaded.
Ready, ///< The sensor has been loaded.
Error, ///< An error occurred or the sensor has been removed.
Removed, ///< Removed from backend
};
Q_ENUM(Status)
explicit Sensor(QObject *parent = nullptr);
explicit Sensor(const QString &id, QObject *parent = nullptr);
/**
* Construct a Sensor from a SensorQuery result and index.
*
* This avoids an extra lookup for the sensor metadata.
*/
Sensor(const SensorQuery &query, int index, QObject *parent = nullptr);
~Sensor() override;
bool event(QEvent *event) override;
QString sensorId() const;
void setSensorId(const QString &id);
Q_SIGNAL void sensorIdChanged() const;
Status status() const;
Q_SIGNAL void statusChanged() const;
QString name() const;
QString shortName() const;
QString description() const;
KSysGuard::Unit unit() const;
qreal minimum() const;
qreal maximum() const;
QVariant::Type type() const;
/**
* This signal is emitted when any of the metadata properties change.
*/
Q_SIGNAL void metaDataChanged() const;
/**
* Returns the output of the sensor.
*
* The returned value is the most recent sensor data received from the ksysguard
* daemon, it's not necessarily the actual current output value.
*
* The client can't control how often the sensor data is sampled. The ksysguard
* daemon is in charge of picking the sample rate. When the Sensor receives new
* output value, dataChanged signal will be emitted.
*
* @see dataChanged
*/
QVariant value() const;
QString formattedValue() const;
Q_SIGNAL void valueChanged() const;
bool enabled() const;
void setEnabled(bool newEnabled);
Q_SIGNAL void enabledChanged();
void classBegin() override;
void componentComplete() override;
private:
void onMetaDataChanged(const QString &sensorId, const SensorInfo &metaData);
void onValueChanged(const QString &sensorId, const QVariant &value);
void onEnabledChanged();
class Private;
const std::unique_ptr<Private> d;
};
} // namespace KSysGuard
/*
Copyright (C) 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "SensorDaemonInterface_p.h"
#include <QDBusPendingCallWatcher>
#include "ksysguarddaemon.h"
using namespace KSysGuard;
class SensorDaemonInterface::Private
{
public:
std::unique_ptr<org::kde::KSysGuardDaemon> dbusInterface;
static const QString SensorServiceName;
static const QString SensorPath;
};
const QString SensorDaemonInterface::Private::SensorServiceName = QStringLiteral("org.kde.kstats");
const QString SensorDaemonInterface::Private::SensorPath = QStringLiteral("/");
SensorDaemonInterface::SensorDaemonInterface(QObject *parent)
: QObject(parent)
, d(new Private)
{
qDBusRegisterMetaType<SensorData>();
qDBusRegisterMetaType<SensorInfo>();
qDBusRegisterMetaType<SensorDataList>();
qDBusRegisterMetaType<SensorInfoMap>();
d->dbusInterface = std::make_unique<org::kde::KSysGuardDaemon>(Private::SensorServiceName, Private::SensorPath, QDBusConnection::sessionBus());
connect(d->dbusInterface.get(), &org::kde::KSysGuardDaemon::sensorMetaDataChanged, this, &SensorDaemonInterface::onMetaDataChanged);
connect(d->dbusInterface.get(), &org::kde::KSysGuardDaemon::newSensorData, this, &SensorDaemonInterface::onValueChanged);
connect(d->dbusInterface.get(), &org::kde::KSysGuardDaemon::sensorAdded, this, &SensorDaemonInterface::sensorAdded);
connect(d->dbusInterface.get(), &org::kde::KSysGuardDaemon::sensorRemoved, this, &SensorDaemonInterface::sensorRemoved);
}
SensorDaemonInterface::~SensorDaemonInterface()
{
}
void SensorDaemonInterface::requestMetaData(const QString &sensorId)
{
requestMetaData(QStringList{sensorId});
}
void SensorDaemonInterface::requestMetaData(const QStringList &sensorIds)
{
auto watcher = new QDBusPendingCallWatcher{d->dbusInterface->sensors(sensorIds), this};
connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [this](QDBusPendingCallWatcher *self) {
self->deleteLater();
const QDBusPendingReply<SensorInfoMap> reply = *self;
if (reply.isError()) {
return;
}
const auto infos = reply.value();
for (auto itr = infos.begin(); itr != infos.end(); ++itr) {
Q_EMIT metaDataChanged(itr.key(), itr.value());
}
});
}
void SensorDaemonInterface::requestValue(const QString &sensorId)
{
auto watcher = new QDBusPendingCallWatcher{d->dbusInterface->sensorData({sensorId}), this};
connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [this](QDBusPendingCallWatcher *self) {
self->deleteLater();
const QDBusPendingReply<SensorDataList> reply = *self;
if (reply.isError()) {
return;
}
const auto allData = reply.value();
for (auto data : allData) {
Q_EMIT valueChanged(data.attribute, data.payload);
}
});
}
QDBusPendingCallWatcher *SensorDaemonInterface::allSensors() const
{
return new QDBusPendingCallWatcher{d->dbusInterface->allSensors()};
}
void SensorDaemonInterface::subscribe(const QString &sensorId)
{
subscribe(QStringList{sensorId});
}
void KSysGuard::SensorDaemonInterface::subscribe(const QStringList &sensorIds)
{
d->dbusInterface->subscribe(sensorIds);
}
void SensorDaemonInterface::unsubscribe(const QString &sensorId)
{
unsubscribe(QStringList{sensorId});
}
void KSysGuard::SensorDaemonInterface::unsubscribe(const QStringList &sensorIds)
{
d->dbusInterface->unsubscribe(sensorIds);
}
SensorDaemonInterface *SensorDaemonInterface::instance()
{
static SensorDaemonInterface instance;
return &instance;