diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index d49f58e..2f6b678 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -9,11 +9,14 @@ find_package(Boost 1.79.0 COMPONENTS json) add_executable(app src/main.cxx + src/sensor/SensorsWrapper.cxx + src/sensor/Sensor.cxx src/sensor/NvidiaSensor.cxx + src/sensor/HwmonSensor.cxx src/fan/Pwm.cxx src/fan/PwmControl.cxx - src/SensorsWrapper.cxx - src/sensor/HwmonSensor.cxx + src/fan/HwmonFan.cxx + src/Mapping.cxx ) set_property(TARGET app PROPERTY CXX_STANDARD 17) diff --git a/app/include/Mapping.h b/app/include/Mapping.h new file mode 100644 index 0000000..49cc848 --- /dev/null +++ b/app/include/Mapping.h @@ -0,0 +1,23 @@ +#ifndef MAPPING_H_ +#define MAPPING_H_ + +#include +#include + +#include +#include +#include + +class Mapping { +public: + std::vector> + createMapping(std::vector> rpmSensors, + std::vector> pwmControls); + +private: + template + void print(std::string listLabel, + std::vector> list); +}; + +#endif // MAPPING_H_ diff --git a/app/include/Printable.h b/app/include/Printable.h new file mode 100644 index 0000000..4607192 --- /dev/null +++ b/app/include/Printable.h @@ -0,0 +1,11 @@ +#ifndef PRINTABLE_H_ +#define PRINTABLE_H_ + +#include + +class Printable { +public: + virtual const std::string toString() const = 0; +}; + +#endif // PRINTABLE_H_ diff --git a/app/include/SensorsWrapper.h b/app/include/SensorsWrapper.h deleted file mode 100644 index 7a22ddf..0000000 --- a/app/include/SensorsWrapper.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef SENSORSWRAPPER_H_ -#define SENSORSWRAPPER_H_ - -#include -#include - -#include - -struct Sensor { - sensors_chip_name chipName; - sensors_feature feature; - sensors_subfeature subFeature; -}; - -class SensorsWrapper { -public: - SensorsWrapper(); - ~SensorsWrapper(); - - std::vector getTemperatureSensors(); - int getValue(Sensor sensor); - std::string getLabel(Sensor sensor); - -private: - std::vector mTemperatureSensors; - std::vector mFanSensors; -}; - -#endif // SENSORSWRAPPER_H_ diff --git a/app/include/fan/HwmonFan.h b/app/include/fan/HwmonFan.h index 4be401c..5931916 100644 --- a/app/include/fan/HwmonFan.h +++ b/app/include/fan/HwmonFan.h @@ -9,15 +9,15 @@ class HwmonFan : public Fan { public: - HwmonFan(std::unique_ptr pwmControl, - std::unique_ptr rpmSensor); + HwmonFan(std::shared_ptr pwmControl, + std::shared_ptr rpmSensor); void pwm(int percent) override; int rpm() override; private: - std::unique_ptr mPwmControl; - std::unique_ptr mRpmSensor; + std::shared_ptr mPwmControl; + std::shared_ptr mRpmSensor; }; #endif // HWMONFAN_H_ diff --git a/app/include/fan/PwmControl.h b/app/include/fan/PwmControl.h index 9faeae5..4a11895 100644 --- a/app/include/fan/PwmControl.h +++ b/app/include/fan/PwmControl.h @@ -3,11 +3,13 @@ #include +#include + enum class PWM_CONTROL_PROPERTY { CONTROL, ENABLE, MODE }; enum class PWM_ENABLE { FULL_SPEED = 0, MANUAL_CONTROL }; enum class PWM_MODE { DC = 0, PWM }; -class PwmControl { +class PwmControl : public Printable { public: PwmControl(std::string controlPath); @@ -17,6 +19,8 @@ public: void enableManualControl(); void reset(); + const std::string toString() const override; + private: int readValue(std::string path); diff --git a/app/include/sensor/HwmonSensor.h b/app/include/sensor/HwmonSensor.h index 94af1f6..94b5e48 100644 --- a/app/include/sensor/HwmonSensor.h +++ b/app/include/sensor/HwmonSensor.h @@ -11,7 +11,7 @@ public: const sensors_subfeature *subfeature); int value() override; - std::string name() override; + const std::string toString() const override; private: const sensors_chip_name *mChipName; diff --git a/app/include/sensor/NvidiaSensor.h b/app/include/sensor/NvidiaSensor.h index 2c5836b..a6dca0f 100644 --- a/app/include/sensor/NvidiaSensor.h +++ b/app/include/sensor/NvidiaSensor.h @@ -9,7 +9,8 @@ public: ~NvidiaSensor(); int value() override; - std::string name() override; + + const std::string toString() const override; }; #endif // NVIDIASENSOR_H_ diff --git a/app/include/sensor/Sensor.h b/app/include/sensor/Sensor.h index f4e969f..4f3c3ca 100644 --- a/app/include/sensor/Sensor.h +++ b/app/include/sensor/Sensor.h @@ -1,15 +1,18 @@ #ifndef SENSOR_H_ #define SENSOR_H_ -#include +#include -class Sensor { +class Sensor : public Printable { public: // Read the current value virtual int value() = 0; - // Name for displaying. Should be descriptive, e.g. "GPU" or the label from - // libsensors. - virtual std::string name() = 0; + + virtual int max() const; + virtual void max(int value); + +protected: + int mMax = 0; }; #endif // SENSOR_H_ diff --git a/app/include/sensor/SensorsWrapper.h b/app/include/sensor/SensorsWrapper.h new file mode 100644 index 0000000..7cfef9a --- /dev/null +++ b/app/include/sensor/SensorsWrapper.h @@ -0,0 +1,26 @@ +#ifndef SENSORSWRAPPER_H_ +#define SENSORSWRAPPER_H_ + +#include +#include + +#include + +#include +#include + +class SensorsWrapper { +public: + SensorsWrapper(); + ~SensorsWrapper(); + + std::vector> + Sensors(sensors_subfeature_type sensorType); + + std::vector> PwmControls(); + +private: + FILE *mConfigFile; +}; + +#endif // SENSORSWRAPPER_H_ diff --git a/app/src/Mapping.cxx b/app/src/Mapping.cxx new file mode 100644 index 0000000..002efea --- /dev/null +++ b/app/src/Mapping.cxx @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SETTLE_TIMEOUT 5 + +using namespace std; + +vector> +Mapping::createMapping(vector> rpmSensors, + vector> pwmControls) { + print("RPM Sensors", rpmSensors); + print("PWM controllers", pwmControls); + vector> mapping; + + cout << "Settings all fans to maximum speed" << endl; + for (auto c : pwmControls) { + c->enableManualControl(); + c->pwm(100); + } + + // Wait for fans to settle + cout << "Waiting for fans to settle" << endl; + this_thread::sleep_for(chrono::seconds(SETTLE_TIMEOUT)); + + // Record values of all RPM sensors + for (auto s : rpmSensors) { + int value = s->value(); + s->max(value); + + cout << s->toString() << " max value: " << s->max() << endl; + } + + // Set each fan to 50% and check if a sensor matches + for (auto c : pwmControls) { + cout << "Setting " << c->toString() + << " to 50% and wait for it to settle..." << endl; + c->pwm(50); + + this_thread::sleep_for(chrono::seconds(SETTLE_TIMEOUT)); + + for (auto s : rpmSensors) { + if (s->value() < s->max() * 0.7) { + cout << "Found matching sensor " << s->toString() << endl; + mapping.push_back(make_shared(c, s)); + } + } + + cout << "Setting fan back to 100% and wait for settling" << endl; + c->pwm(100); + this_thread::sleep_for(chrono::seconds(SETTLE_TIMEOUT)); + } + + cout << "Resetting all fans" << endl; + for (auto c : pwmControls) { + c->reset(); + } + + return mapping; +} + +template +void Mapping::print(string listLabel, vector> list) { + cout << listLabel << ": " << endl; + + for (auto i : list) { + cout << i->toString() << endl; + } + cout << "\n"; +} diff --git a/app/src/SensorsWrapper.cxx b/app/src/SensorsWrapper.cxx deleted file mode 100644 index 4cc409a..0000000 --- a/app/src/SensorsWrapper.cxx +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include - -#include - -using namespace std; - -#define CONFIG_FILE "/etc/conf.d/sensors" - -SensorsWrapper::SensorsWrapper() { - auto config = fopen(CONFIG_FILE, "r"); - if (sensors_init(config) != 0) { - throw runtime_error("Config file doesn't exist"); - } - - int c = 0; - for (const sensors_chip_name *chipName; - (chipName = sensors_get_detected_chips(0, &c)) != NULL;) { - - int d = 0; - for (const sensors_feature *feature; - (feature = sensors_get_features(chipName, &d)) != NULL;) { - auto tempFeature = sensors_get_subfeature(chipName, feature, - SENSORS_SUBFEATURE_TEMP_INPUT); - if (tempFeature) - mTemperatureSensors.push_back( - Sensor{*chipName, *feature, *tempFeature}); - - auto fanFeature = sensors_get_subfeature(chipName, feature, - SENSORS_SUBFEATURE_FAN_INPUT); - if (fanFeature) - mFanSensors.push_back(Sensor{*chipName, *feature, *fanFeature}); - } - } -} - -SensorsWrapper::~SensorsWrapper() { sensors_cleanup(); } - -std::vector SensorsWrapper::getTemperatureSensors() { - return mTemperatureSensors; -} - -int SensorsWrapper::getValue(Sensor sensor) { - double value; - const sensors_chip_name *chipName = &sensor.chipName; - - sensors_get_value(chipName, sensor.subFeature.number, &value); - - return (int)value; -} - -string SensorsWrapper::getLabel(Sensor sensor) { - const sensors_chip_name *chipName = &sensor.chipName; - const sensors_feature *feature = &sensor.feature; - return string(sensors_get_label(chipName, feature)); -} diff --git a/app/src/fan/HwmonFan.cxx b/app/src/fan/HwmonFan.cxx new file mode 100644 index 0000000..dd3c792 --- /dev/null +++ b/app/src/fan/HwmonFan.cxx @@ -0,0 +1,11 @@ +#include + +using namespace std; + +HwmonFan::HwmonFan(shared_ptr pwmControl, + shared_ptr rpmSensor) + : mPwmControl(pwmControl), mRpmSensor(rpmSensor) {} + +void HwmonFan::pwm(int percent) { mPwmControl->pwm(percent); } + +int HwmonFan::rpm() { return mRpmSensor->value(); } diff --git a/app/src/fan/PwmControl.cxx b/app/src/fan/PwmControl.cxx index 814bf2f..296335d 100644 --- a/app/src/fan/PwmControl.cxx +++ b/app/src/fan/PwmControl.cxx @@ -64,3 +64,7 @@ void PwmControl::reset() { ostrm.close(); } + +const string PwmControl::toString() const { + return fs::path(mControlPath).filename(); +} diff --git a/app/src/main.cxx b/app/src/main.cxx index 63538ea..5daa629 100644 --- a/app/src/main.cxx +++ b/app/src/main.cxx @@ -1,91 +1,20 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include +#include #include - -#define CONFIG_FILE "/etc/conf.d/sensors" -#define HWMON_BASE_PATH "/sys/class/hwmon" - -namespace fs = std::filesystem; - -template -std::vector> sensors() { - std::vector> sensors; - - int c = 0; - for (const sensors_chip_name *chipName; - (chipName = sensors_get_detected_chips(0, &c)) != NULL;) { - - int d = 0; - for (const sensors_feature *feature; - (feature = sensors_get_features(chipName, &d)) != NULL;) { - auto subFeature = sensors_get_subfeature(chipName, feature, T); - if (subFeature) { - sensors.push_back( - std::make_shared(chipName, feature, subFeature)); - } - } - } - - return sensors; -} - -std::vector> pwmControls() { - std::vector> controls; - - const std::regex re_ctrl_enable("pwm[0-9]_enable"); - const std::regex re_ctrl_mode("pwm[0-9]_mode"); - const std::regex re_ctrl("pwm[0-9]"); - - if (!fs::exists(HWMON_BASE_PATH)) { - std::cout << HWMON_BASE_PATH << " doesn't exist" << std::endl; - } else { - for (const fs::directory_entry &hwmon_device : - fs::directory_iterator{HWMON_BASE_PATH}) { - - for (const fs::directory_entry &control : - fs::directory_iterator{hwmon_device}) { - auto filename = control.path().filename().string(); - - if (regex_match(filename, re_ctrl)) { - auto controlPath = control.path().string(); - - controls.push_back(std::make_shared(controlPath)); - } - } - } - } - - return controls; -} +#include int main() { - auto config = fopen(CONFIG_FILE, "r"); - if (sensors_init(config) != 0) { - throw std::runtime_error("Config file doesn't exist"); - } + SensorsWrapper sensorsWrapper; - auto tempSensors = sensors(); + auto tempSensors = sensorsWrapper.Sensors(SENSORS_SUBFEATURE_TEMP_INPUT); tempSensors.push_back(std::make_shared()); - for (auto s : tempSensors) { - std::cout << s->name() << ": " << s->value() << std::endl; - } + auto pwmSensors = sensorsWrapper.Sensors(SENSORS_SUBFEATURE_FAN_INPUT); - std::cout << "pwm" << std::endl; + auto controls = sensorsWrapper.PwmControls(); - auto pwmSensors = sensors(); - for (auto s : pwmSensors) { - std::cout << s->name() << ": " << s->value() << std::endl; - } + Mapping m; + m.createMapping(pwmSensors, controls); return 0; } diff --git a/app/src/sensor/HwmonSensor.cxx b/app/src/sensor/HwmonSensor.cxx index 87796d1..0485881 100644 --- a/app/src/sensor/HwmonSensor.cxx +++ b/app/src/sensor/HwmonSensor.cxx @@ -1,6 +1,5 @@ -#include - #include +#include using namespace std; @@ -10,10 +9,12 @@ HwmonSensor::HwmonSensor(const sensors_chip_name *chipName, : mChipName(chipName), mFeature(feature), mSubFeature(subfeature) {} int HwmonSensor::value() { - double *value; - sensors_get_value(mChipName, mSubFeature->number, value); + double value; + sensors_get_value(mChipName, mSubFeature->number, &value); - return static_cast(*value); + return static_cast(value); } -string HwmonSensor::name() { return sensors_get_label(mChipName, mFeature); } +const string HwmonSensor::toString() const { + return sensors_get_label(mChipName, mFeature); +} diff --git a/app/src/sensor/NvidiaSensor.cxx b/app/src/sensor/NvidiaSensor.cxx index 99c913e..d306156 100644 --- a/app/src/sensor/NvidiaSensor.cxx +++ b/app/src/sensor/NvidiaSensor.cxx @@ -18,4 +18,4 @@ int NvidiaSensor::value() { return static_cast(temp); } -string NvidiaSensor::name() { return "GPU"; } +const std::string NvidiaSensor::toString() const { return "Nvidia GPU"; } diff --git a/app/src/sensor/Sensor.cxx b/app/src/sensor/Sensor.cxx new file mode 100644 index 0000000..bc1b582 --- /dev/null +++ b/app/src/sensor/Sensor.cxx @@ -0,0 +1,5 @@ +#include + +int Sensor::max() const { return mMax; } + +void Sensor::max(int value) { mMax = value; } diff --git a/app/src/sensor/SensorsWrapper.cxx b/app/src/sensor/SensorsWrapper.cxx new file mode 100644 index 0000000..b3f52fe --- /dev/null +++ b/app/src/sensor/SensorsWrapper.cxx @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +using namespace std; +namespace fs = std::filesystem; + +#define CONFIG_FILE "/etc/conf.d/sensors" +#define HWMON_BASE_PATH "/sys/class/hwmon" + +SensorsWrapper::SensorsWrapper() : mConfigFile(fopen(CONFIG_FILE, "r")) { + if (sensors_init(mConfigFile) != 0) { + throw runtime_error("Config file doesn't exist"); + } +} + +SensorsWrapper::~SensorsWrapper() { sensors_cleanup(); } + +std::vector> +SensorsWrapper::Sensors(sensors_subfeature_type sensorType) { + std::vector> sensors; + + int c = 0; + for (const sensors_chip_name *chipName; + (chipName = sensors_get_detected_chips(0, &c)) != NULL;) { + + int d = 0; + for (const sensors_feature *feature; + (feature = sensors_get_features(chipName, &d)) != NULL;) { + auto subFeature = sensors_get_subfeature(chipName, feature, sensorType); + if (subFeature) { + sensors.push_back( + std::make_shared(chipName, feature, subFeature)); + } + } + } + + return sensors; +} + +std::vector> SensorsWrapper::PwmControls() { + std::vector> controls; + + const std::regex re_ctrl_enable("pwm[0-9]_enable"); + const std::regex re_ctrl_mode("pwm[0-9]_mode"); + const std::regex re_ctrl("pwm[0-9]"); + + if (!fs::exists(HWMON_BASE_PATH)) { + std::cout << HWMON_BASE_PATH << " doesn't exist" << std::endl; + } else { + for (const fs::directory_entry &hwmon_device : + fs::directory_iterator{HWMON_BASE_PATH}) { + + for (const fs::directory_entry &control : + fs::directory_iterator{hwmon_device}) { + auto filename = control.path().filename().string(); + + if (regex_match(filename, re_ctrl)) { + auto controlPath = control.path().string(); + + controls.push_back(std::make_shared(controlPath)); + } + } + } + } + + return controls; +}