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"