diff --git a/plugins/global/ksgrd/ksgrdiface.cpp b/plugins/global/ksgrd/ksgrdiface.cpp index f5691cca..ca21a988 100644 --- a/plugins/global/ksgrd/ksgrdiface.cpp +++ b/plugins/global/ksgrd/ksgrdiface.cpp @@ -1,402 +1,402 @@ /* Copyright (c) 2019 David Edmundson 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 "ksgrdiface.h" #include "AggregateSensor.h" #include #include #include #include #include // TODO instantiate multiple instances with args for which host to use KSGRDIface::KSGRDIface(QObject *parent, const QVariantList &args) : SensorPlugin(parent, args) { KSGRD::SensorMgr = new KSGRD::SensorManager(this); auto registerSubsystem = [this](const QString &id) { m_subsystems[id] = new SensorContainer(id, KSGRD::SensorMgr->translateSensorPath(id), this); }; registerSubsystem("acpi"); registerSubsystem("cpu"); registerSubsystem("disk"); registerSubsystem("lmsensors"); registerSubsystem("mem"); registerSubsystem("network"); registerSubsystem("partitions"); registerSubsystem("uptime"); registerSubsystem("system"); KSGRD::SensorMgr->engage(QStringLiteral("localhost"), QLatin1String(""), QStringLiteral("ksysguardd")); connect(KSGRD::SensorMgr, &KSGRD::SensorManager::update, this, &KSGRDIface::updateMonitorsList); updateMonitorsList(); // block for sensors to be loaded, but with a guard in case one fails to process // a non issue when we port things to be in-process QEventLoop e; auto t = new QTimer(&e); t->setInterval(2000); t->start(); connect(t, &QTimer::timeout, &e, [&e, this]() { e.quit(); }); connect(this, &KSGRDIface::sensorAdded, &e, [&e, this] { if (m_sensors.count() == m_sensorIds.count()) { e.quit(); } }); e.exec(); addAggregateSensors(); } KSGRDIface::~KSGRDIface() { } void KSGRDIface::subscribe(const QString &sensorPath) { if (!m_subscribedSensors.contains(sensorPath)) { m_subscribedSensors << sensorPath; const int index = m_sensorIds.indexOf(sensorPath); if (index != -1) { m_waitingFor++; KSGRD::SensorMgr->sendRequest(QStringLiteral("localhost"), sensorPath, (KSGRD::SensorClient *)this, index); KSGRD::SensorMgr->sendRequest(QStringLiteral("localhost"), QStringLiteral("%1?").arg(sensorPath), (KSGRD::SensorClient *)this, -(index + 2)); } } } void KSGRDIface::unsubscribe(const QString &sensorPath) { m_subscribedSensors.removeAll(sensorPath); } void KSGRDIface::updateMonitorsList() { KSGRD::SensorMgr->sendRequest(QStringLiteral("localhost"), QStringLiteral("monitors"), (KSGRD::SensorClient *)this, -1); } void KSGRDIface::onSensorMetaDataRetrieved(int id, const QList &answer) { if (answer.isEmpty() || id > m_sensorIds.count()) { qDebug() << "sensor info answer was empty, (" << answer.isEmpty() << ") or sensors does not exist to us "; return; } const QStringList newSensorInfo = QString::fromUtf8(answer[0]).split('\t'); if (newSensorInfo.count() < 4) { qDebug() << "bad sensor info, only" << newSensorInfo.count() << "entries, and we were expecting 4. Answer was " << answer; return; } const QString key = m_sensorIds.value(id); const QString &sensorName = newSensorInfo[0]; const QString &min = newSensorInfo[1]; const QString &max = newSensorInfo[2]; const QString &unit = newSensorInfo[3]; int subsystemIndex = key.indexOf('/'); int propertyIndex = key.lastIndexOf('/'); const QString subsystemId = key.left(subsystemIndex); const QString objectId = key.mid(subsystemIndex + 1, propertyIndex - (subsystemIndex + 1)); const QString propertyId = key.mid(propertyIndex + 1); auto subsystem = m_subsystems[subsystemId]; if (Q_UNLIKELY(!subsystem)) { qDebug() << "could not find subsystem" << subsystemId; return; } auto sensorObject = subsystem->object(objectId); if (!sensorObject) { auto name = KSGRD::SensorMgr->translateSensorPath(objectId); sensorObject = new SensorObject(objectId, name, subsystem); } auto sensor = m_sensors.value(key, nullptr); if (!sensor) { sensor = new SensorProperty(propertyId, sensorObject); } sensor->setName(sensorName); sensor->setShortName(shortNameFor(key)); sensor->setMin(min.toDouble()); sensor->setMax(max.toDouble()); sensor->setUnit(unitFromString(unit)); if (m_sensors.contains(key)) { return; } auto type = m_pendingTypes.take(key); if (type == QLatin1String("float")) { sensor->setVariantType(QVariant::Double); } else { sensor->setVariantType(QVariant::Int); } connect(sensor, &SensorProperty::subscribedChanged, this, [this, sensor](bool subscribed) { if (subscribed) { subscribe(sensor->path()); } else { unsubscribe(sensor->path()); } }); m_sensors[key] = sensor; emit sensorAdded(); } void KSGRDIface::onSensorListRetrieved(const QList &answer) { QSet sensors; int count = 0; for (const QByteArray &sens : answer) { const QString sensStr { QString::fromUtf8(sens) }; const QVector newSensorInfo = sensStr.splitRef('\t'); if (newSensorInfo.count() < 2) { continue; } auto type = newSensorInfo.at(1); if (type == QLatin1String("logfile")) { continue; // logfile data type not currently supported } const QString newSensor = newSensorInfo[0].toString(); sensors.insert(newSensor); m_sensorIds.append(newSensor); m_pendingTypes.insert(newSensor, type.toString()); //we don't emit sensorAdded yet, instead wait for the meta info to be fetched KSGRD::SensorMgr->sendRequest(QStringLiteral("localhost"), QStringLiteral("%1?").arg(newSensor), (KSGRD::SensorClient *)this, -(count + 2)); ++count; } // look for removed sensors // FIXME? foreach (const QString &sensor, m_sensorIds) { if (!sensors.contains(sensor)) { m_sensorIds.removeOne(sensor); m_sensors.remove(sensor); } } } void KSGRDIface::onSensorUpdated(int id, const QList &answer) { m_waitingFor--; const QString sensorName = m_sensorIds.at(id); if (sensorName.isEmpty()) { return; } QString reply; if (!answer.isEmpty()) { reply = QString::fromUtf8(answer[0]); } auto sensor = m_sensors[sensorName]; if (sensor) { if (sensor->info().variantType == QVariant::Double) { bool rc; double value = reply.toDouble(&rc); if (rc) { sensor->setValue(value); } } else if (sensor->info().variantType == QVariant::Int) { bool rc; int value = reply.toInt(&rc); if (rc) { sensor->setValue(value); } } else { sensor->setValue(reply); } } } KSysGuard::utils::Unit KSGRDIface::unitFromString(const QString &unitString) const { using namespace KSysGuard; static const QHash map({ { "%", utils::UnitPercent }, { "1/s", utils::UnitRate }, { "°C", utils::UnitCelsius }, { "dBm", utils::UnitDecibelMilliWatts }, { "KB", utils::UnitKiloByte }, { "KB/s", utils::UnitKiloByteRate }, { "MHz", utils::UnitMegaHertz }, { "port", utils::UnitNone }, { "rpm", utils::UnitRpm }, { "s", utils::UnitTime }, { "V", utils::UnitVolt } }); return map.value(unitString, utils::UnitNone); } void KSGRDIface::update() { for (int i = 0; i < m_subscribedSensors.count(); i++) { auto sensorName = m_subscribedSensors.at(i); int index = m_sensorIds.indexOf(sensorName); if (index < 0) { return; } m_waitingFor++; KSGRD::SensorMgr->sendRequest(QStringLiteral("localhost"), sensorName, (KSGRD::SensorClient *)this, index); } } void KSGRDIface::sensorLost(int) { m_waitingFor--; } void KSGRDIface::answerReceived(int id, const QList &answer) { //this is the response to "sensorName?" if (id < -1) { onSensorMetaDataRetrieved(-id - 2, answer); return; } //response to "monitors" if (id == -1) { onSensorListRetrieved(answer); return; } onSensorUpdated(id, answer); } void KSGRDIface::addAggregateSensors() { auto networkAll = new SensorObject("all", i18nc("@title All Network Interfaces", "All"), m_subsystems["network"]); auto sensor = new AggregateSensor(networkAll, "receivedDataRate", i18nc("@title", "Received Data Rate")); sensor->setShortName(i18nc("@title Received Data Rate", "Down")); sensor->setMatchSensors(QRegularExpression("[^/]*/receiver"), QStringLiteral("data")); sensor->setDescription(i18nc("@info", "The rate at which data is received on all interfaces.")); sensor->setUnit(KSysGuard::utils::UnitKiloByteRate); sensor = new AggregateSensor(networkAll, "totalReceivedData", i18nc("@title", "Total Received Data")); sensor->setShortName(i18nc("@title Total Receieved Data", "Total Down")); sensor->setMatchSensors(QRegularExpression("[^/]*/receiver"), QStringLiteral("dataTotal")); sensor->setDescription(i18nc("@info", "The total amount of data received on all interfaces.")); sensor->setUnit(KSysGuard::utils::UnitKiloByte); sensor = new AggregateSensor(networkAll, "sentDataRate", i18nc("@title", "Sent Data Rate")); sensor->setShortName(i18nc("@title Sent Data Rate", "Up")); sensor->setMatchSensors(QRegularExpression("[^/]*/transmitter"), QStringLiteral("data")); sensor->setDescription(i18nc("@info", "The rate at which data is sent on all interfaces.")); sensor->setUnit(KSysGuard::utils::UnitKiloByteRate); sensor = new AggregateSensor(networkAll, "totalSentData", i18nc("@title", "Total Sent Data")); sensor->setShortName(i18nc("@title Total Sent Data", "Total Up")); sensor->setMatchSensors(QRegularExpression("[^/]*/transmitter"), QStringLiteral("dataTotal")); sensor->setDescription(i18nc("@info", "The total amount of data sent on all interfaces.")); sensor->setUnit(KSysGuard::utils::UnitKiloByte); auto diskAll = new SensorObject("all", i18nc("@title All Disks", "All"), m_subsystems["disk"]); sensor = new AggregateSensor(diskAll, "read", i18nc("@title", "Disk Read Accesses")); sensor->setShortName(i18nc("@title Disk Read Accesses", "Read")); // TODO: This regex is not exhaustive as it doesn't consider things that aren't treated as sdX devices. // However, we do not simply want to match disk/* as that would include duplicate devices. sensor->setMatchSensors(QRegularExpression("^sd[a-z]+[0-9]+_[^/]*/Rate$"), QStringLiteral("rblk")); sensor->setDescription(i18nc("@info", "Read accesses across all disk devices")); sensor = new AggregateSensor(diskAll, "write", i18nc("@title", "Disk Write Accesses")); sensor->setShortName(i18nc("@title Disk Write Accesses", "Write")); // TODO: See above. sensor->setMatchSensors(QRegularExpression("^sd[a-z]+[0-9]+_[^/]*/Rate$"), QStringLiteral("wblk")); sensor->setDescription(i18nc("@info", "Write accesses across all disk devices")); auto memPhysical = m_subsystems["mem"]->object("physical"); Q_ASSERT(memPhysical); if (!memPhysical) { return; } PercentageSensor *appLevel = new PercentageSensor(memPhysical, "applicationlevel", i18nc("@title", "Application Memory Percentage")); appLevel->setShortName(i18nc("@title Application Memory Percentage", "Application")); appLevel->setBaseSensor(memPhysical->sensor("application")); appLevel->setDescription(i18nc("@info", "Percentage of memory taken by applications.")); PercentageSensor *bufLevel = new PercentageSensor(memPhysical, "buflevel", i18nc("@title", "Buffer Memory Percentage")); bufLevel->setShortName(i18nc("@title Buffer Memory Percentage", "Buffer")); bufLevel->setBaseSensor(memPhysical->sensor("buf")); bufLevel->setDescription(i18nc("@info", "Percentage of memory taken by the buffer.")); PercentageSensor *cacheLevel = new PercentageSensor(memPhysical, "cachelevel", i18nc("@title", "Cache Memory Percentage")); cacheLevel->setShortName(i18nc("@title Cache Memory Percentage", "Cache")); cacheLevel->setBaseSensor(memPhysical->sensor("cached")); cacheLevel->setDescription(i18nc("@info", "Percentage of memory taken by the cache.")); PercentageSensor *freeLevel = new PercentageSensor(memPhysical, "freelevel", i18nc("@title", "Free Memory Percentage")); freeLevel->setShortName(i18nc("@title Free Memory Percentage", "Free")); freeLevel->setBaseSensor(memPhysical->sensor("free")); freeLevel->setDescription(i18nc("@info", "Percentage of free memory.")); PercentageSensor *usedLevel = new PercentageSensor(memPhysical, "usedlevel", i18nc("@title", "Used Memory Percentage")); usedLevel->setShortName(i18nc("@title Used Memory Percentage", "Used")); usedLevel->setBaseSensor(memPhysical->sensor("used")); usedLevel->setDescription(i18nc("@info", "Percentage of used memory.")); PercentageSensor *availableLevel = new PercentageSensor(memPhysical, "availablelevel", i18nc("@title", "Available Memory Percentage")); availableLevel->setShortName(i18nc("@title Available Memory Percentage", "Available")); availableLevel->setBaseSensor(memPhysical->sensor("available")); availableLevel->setDescription(i18nc("@info", "Percentage of available memory.")); PercentageSensor *allocatedLevel = new PercentageSensor(memPhysical, "allocatedlevel", i18nc("@title", "Allocated Memory Percentage")); allocatedLevel->setShortName(i18nc("@title Allocated Memory Percentage", "Used")); allocatedLevel->setBaseSensor(memPhysical->sensor("allocated")); allocatedLevel->setDescription(i18nc("@info", "Percentage of allocated memory.")); } QString KSGRDIface::shortNameFor(const QString &key) { // TODO: This is pretty ugly, but it is really hard to add this information to ksysguardd. // So for now, we just map sensor ids to short names and return that. static QHash shortNames = { { QStringLiteral("cpu/system/TotalLoad"), i18nc("@title Total CPU Usage", "Usage") }, { QStringLiteral("mem/physical/used"), i18nc("@title Total Memory Usage", "Total Used") }, { QStringLiteral("mem/physical/cached"), i18nc("@title Cached Memory Usage", "Cached") }, { QStringLiteral("mem/physical/free"), i18nc("@title Free Memory Amount", "Free") }, - { QStringLiteral("mem/physical/available"), i18nc("@title Available Memory Amount", "Avalable") }, + { QStringLiteral("mem/physical/available"), i18nc("@title Available Memory Amount", "Available") }, { QStringLiteral("mem/physical/application"), i18nc("@title Application Memory Usage", "Application") }, { QStringLiteral("mem/physical/buf"), i18nc("@title Buffer Memory Usage", "Buffer") }, { QStringLiteral("cpu/system/processors"), i18nc("@title Number of Processors", "Processors") }, { QStringLiteral("cpu/system/cores"), i18nc("@title Number of Cores", "Cores") }, }; return shortNames.value(key, QString {}); } K_PLUGIN_FACTORY(KSGRDPluginFactory, registerPlugin();) #include "ksgrdiface.moc"