File indexing completed on 2024-05-12 05:30:13

0001 /*
0002     SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include <QCoreApplication>
0008 #include <QDebug>
0009 
0010 #include <QDBusConnection>
0011 #include <QDBusMetaType>
0012 
0013 #include <QCommandLineParser>
0014 
0015 #include <iostream>
0016 
0017 #include <formatter/Formatter.h>
0018 #include <systemstats/DBusInterface.h>
0019 
0020 class SensorWatcher : public QCoreApplication
0021 {
0022     Q_OBJECT
0023 
0024 public:
0025     SensorWatcher(int &argc, char **argv);
0026     ~SensorWatcher() = default;
0027 
0028     void subscribe(const QStringList &sensorNames);
0029     void list();
0030     void showDetails(const QStringList &sensorNames);
0031 
0032     void setShowDetails(bool details);
0033     void setRemain(bool remain);
0034 
0035 private:
0036     void onNewSensorData(const KSysGuard::SensorDataList &changes);
0037     void onSensorMetaDataChanged(const KSysGuard::SensorInfoMap &sensors);
0038     void showSensorDetails(const KSysGuard::SensorInfoMap &sensors);
0039     KSysGuard::SystemStats::DBusInterface *m_iface;
0040     /// Contains the names of all sensors that have **not** reported any data yet
0041     QSet<QString> m_unreportedSensors;
0042     bool m_showDetails = false;
0043     /// When @c true, remain connected; otherwise, exit as soon as all subscribed sensors have reported data
0044     bool m_remain = false;
0045 };
0046 
0047 int main(int argc, char **argv)
0048 {
0049     qDBusRegisterMetaType<KSysGuard::SensorData>();
0050     qDBusRegisterMetaType<KSysGuard::SensorInfo>();
0051     qDBusRegisterMetaType<KSysGuard::SensorDataList>();
0052     qDBusRegisterMetaType<QHash<QString, KSysGuard::SensorInfo>>();
0053     qDBusRegisterMetaType<QStringList>();
0054 
0055     SensorWatcher app(argc, argv);
0056 
0057     QCommandLineParser parser;
0058     parser.addOption({ QStringLiteral("list"), QStringLiteral("List Available Sensors") });
0059     parser.addOption({ QStringLiteral("details"), QStringLiteral("Show detailed information about selected sensors") });
0060     parser.addOption({ QStringLiteral("remain"), QStringLiteral("Do not exit, but keep reporting sensor values") });
0061 
0062     parser.addPositionalArgument(QStringLiteral("sensorNames"), QStringLiteral("List of sensors to monitor"), QStringLiteral("sensorId1 sensorId2  ..."));
0063     parser.addHelpOption();
0064     parser.process(app);
0065 
0066     if (parser.isSet(QStringLiteral("list"))) {
0067         app.list();
0068     } else if (parser.positionalArguments().isEmpty()) {
0069         qDebug() << "No sensors specified.";
0070         parser.showHelp(-1);
0071     } else {
0072         app.setShowDetails(parser.isSet(QStringLiteral("details")));
0073         app.setRemain(parser.isSet(QStringLiteral("remain")));
0074         app.subscribe(parser.positionalArguments());
0075         app.exec();
0076     }
0077 }
0078 
0079 SensorWatcher::SensorWatcher(int &argc, char **argv)
0080     : QCoreApplication(argc, argv)
0081     , m_iface(new KSysGuard::SystemStats::DBusInterface(KSysGuard::SystemStats::ServiceName,
0082           KSysGuard::SystemStats::ObjectPath,
0083           QDBusConnection::sessionBus(),
0084           this))
0085 {
0086     connect(m_iface, &KSysGuard::SystemStats::DBusInterface::newSensorData, this, &SensorWatcher::onNewSensorData);
0087     connect(m_iface, &KSysGuard::SystemStats::DBusInterface::sensorMetaDataChanged, this, &SensorWatcher::onSensorMetaDataChanged);
0088 }
0089 
0090 void SensorWatcher::subscribe(const QStringList &sensorNames)
0091 {
0092     m_iface->subscribe(sensorNames);
0093 
0094     m_unreportedSensors = QSet<QString>(sensorNames.begin(), sensorNames.end());
0095     auto pendingInitialValues = m_iface->sensorData(sensorNames);
0096     pendingInitialValues.waitForFinished();
0097     onNewSensorData(pendingInitialValues.value());
0098 
0099     if (!m_remain && !m_unreportedSensors.isEmpty()) {
0100         // We want to do one pass only, and there are sensors that
0101         // have no initial data. That **might** be because they
0102         // are mis-spelled, so go through all the sensors and
0103         // check that the names are ok; sensors that do not exist
0104         // will be removed (and get a complaining message, too).
0105         QSet<QString> existingSensors;
0106         // Fill the set of existing sensor names
0107         {
0108             auto pendingSensors = m_iface->allSensors();
0109             pendingSensors.waitForFinished();
0110             const auto allSensors = pendingSensors.value();
0111             for (auto it = allSensors.constBegin(); it != allSensors.constEnd(); it++) {
0112                 existingSensors.insert(it.key());
0113             }
0114         }
0115 
0116         QSet<QString> nonexistentSensors;
0117         for (const auto& sensorName : std::as_const(m_unreportedSensors)) {
0118             if (!existingSensors.contains(sensorName)) {
0119                 // This sensor (named on the command-line, and with no data so far) does not exist
0120                 std::cout << "No sensor named '" << qPrintable(sensorName) << "'\n";
0121                 nonexistentSensors.insert(sensorName);
0122             }
0123         }
0124         m_unreportedSensors.subtract(nonexistentSensors);
0125     }
0126 
0127     if (m_showDetails) {
0128         auto pendingSensors = m_iface->sensors(sensorNames);
0129         pendingSensors.waitForFinished();
0130 
0131         auto sensors = pendingSensors.value();
0132         showSensorDetails(sensors);
0133     }
0134 
0135     if (!m_remain && m_unreportedSensors.isEmpty())
0136     {
0137         QTimer::singleShot(0, this, &QCoreApplication::quit);
0138     }
0139 }
0140 
0141 void SensorWatcher::onNewSensorData(const KSysGuard::SensorDataList &changes)
0142 {
0143     for (const auto &entry : changes) {
0144         std::cout << qPrintable(entry.sensorProperty) << ' ' << qPrintable(entry.payload.toString()) << std::endl;
0145         m_unreportedSensors.remove(entry.sensorProperty);
0146     }
0147     // Use a timer so that we get out of this slot first; since this is **also** called
0148     // from subscribe, we want to let that function complete too, just in case the user
0149     // has asked for details.
0150     if (!m_remain && m_unreportedSensors.isEmpty())
0151     {
0152         QTimer::singleShot(0, this, &QCoreApplication::quit);
0153     }
0154 }
0155 
0156 void SensorWatcher::onSensorMetaDataChanged(const KSysGuard::SensorInfoMap &sensors)
0157 {
0158     if (!m_showDetails) {
0159         return;
0160     }
0161 
0162     std::cout << "Sensor metadata changed\n";
0163     showSensorDetails(sensors);
0164 }
0165 
0166 void SensorWatcher::list()
0167 {
0168     auto pendingSensors = m_iface->allSensors();
0169     pendingSensors.waitForFinished();
0170     auto sensors = pendingSensors.value();
0171     if (sensors.count() < 1) {
0172         std::cout << "No sensors available.";
0173     }
0174     for (auto it = sensors.constBegin(); it != sensors.constEnd(); it++) {
0175         std::cout << qPrintable(it.key()) << ' ' << qPrintable(it.value().name) << std::endl;
0176     }
0177 }
0178 
0179 void SensorWatcher::setShowDetails(bool details)
0180 {
0181     m_showDetails = details;
0182 }
0183 
0184 void SensorWatcher::setRemain(bool remain)
0185 {
0186     m_remain = remain;
0187 }
0188 
0189 void SensorWatcher::showSensorDetails(const KSysGuard::SensorInfoMap &sensors)
0190 {
0191     for (auto it = sensors.constBegin(); it != sensors.constEnd(); ++it) {
0192         auto info = it.value();
0193         std::cout << qPrintable(it.key()) << "\n";
0194         std::cout << "    Name:        " << qPrintable(info.name) << "\n";
0195         std::cout << "    Short Name:  " << qPrintable(info.shortName) << "\n";
0196         std::cout << "    Description: " << qPrintable(info.description) << "\n";
0197         std::cout << "    Unit:        " << qPrintable(KSysGuard::Formatter::symbol(info.unit)) << "\n";
0198         std::cout << "    Minimum:     " << info.min << "\n";
0199         std::cout << "    Maximum:     " << info.max << "\n";
0200     }
0201 }
0202 
0203 #include "main.moc"