diff --git a/app/include/Serializer.h b/app/include/Serializer.h index 6af9dcd..8ba4e1b 100644 --- a/app/include/Serializer.h +++ b/app/include/Serializer.h @@ -17,16 +17,16 @@ using json = nlohmann::json; -class Serializer -{ +class Serializer { public: Serializer(); void SerializeFans(std::vector> fans); - std::vector> DeserializeFans( - std::vector> availableSensors); - std::vector> DeserializeFanCurves( - std::vector> availableSensors, - std::vector> availableFans); + std::vector> + DeserializeFans(std::vector> availableSensors); + std::vector> + DeserializeFanCurves(std::vector> availableSensors, + std::vector> availableFans, + bool &everythingFound); std::shared_ptr DeserializeSettings(); private: diff --git a/app/include/fan/Fan.h b/app/include/fan/Fan.h index aada056..4c29fc7 100644 --- a/app/include/fan/Fan.h +++ b/app/include/fan/Fan.h @@ -22,6 +22,8 @@ public: virtual void FindPWMLimits() = 0; virtual void AdjustPWMLimits() = 0; + + virtual void EnforceSetValue() = 0; }; #endif // FAN_H_ diff --git a/app/include/fan/FanCurve.h b/app/include/fan/FanCurve.h index a4cd045..6d5ca6b 100644 --- a/app/include/fan/FanCurve.h +++ b/app/include/fan/FanCurve.h @@ -8,30 +8,32 @@ #include #include -struct FanStep -{ +struct FanStep { int Temp; int Percent; }; -class FanCurve -{ +class FanCurve { public: FanCurve(std::vector steps, std::vector> sensors, std::vector> fans, - std::unique_ptr aggregator); + std::unique_ptr aggregator, int hysteresis); void DoFanControl(); private: int AggregateTemperature(); void PrintInfo(); + bool ExceedsHysteresis(int temperature); + void ApplyFanPower(std::shared_ptr fan, int targetFanPower); std::vector mSteps; std::vector> mTempSensors; std::vector> mFans; std::unique_ptr mAggregator; + int mHystersis; + int mLastTemperature; }; #endif // FANCURVE_H_ diff --git a/app/include/fan/HwmonFan.h b/app/include/fan/HwmonFan.h index 94f125e..45fa509 100644 --- a/app/include/fan/HwmonFan.h +++ b/app/include/fan/HwmonFan.h @@ -30,11 +30,16 @@ public: void FindPWMLimits() override; void AdjustPWMLimits() override; + void EnforceSetValue() override; + json toJson() const override; const std::string toString() const override; private: + bool InhibitStopPeriodExpired(); + void SetPower(int percent); + std::shared_ptr mPWMControl; std::shared_ptr mRpmSensor; std::string mLabel; @@ -43,6 +48,9 @@ private: int mStartPWM = 0; bool mZeroFanModeSupported = false; std::chrono::time_point mLastAdjustmentTime; + std::chrono::time_point mLastStartTime; + int mSetValue = 0; + bool mWasStopped = false; }; #endif // HWMONFAN_H_ diff --git a/app/include/pwm/PWMControl.h b/app/include/pwm/PWMControl.h index 2f97254..47c88e6 100644 --- a/app/include/pwm/PWMControl.h +++ b/app/include/pwm/PWMControl.h @@ -26,8 +26,6 @@ public: json toJson() const override; private: - int mCurrentValue; - std::string mControlPath; std::string mEnablePath; std::string mModePath; diff --git a/app/include/sensor/LMSensorsFacade.h b/app/include/sensor/LMSensorsFacade.h index 3ba19a0..a264173 100644 --- a/app/include/sensor/LMSensorsFacade.h +++ b/app/include/sensor/LMSensorsFacade.h @@ -16,9 +16,13 @@ public: std::vector> TemperatureSensors(); std::vector> RPMSensors(); + void ReloadSensors(); + private: template std::vector> Sensors(); + void InitSensors(); + void CleanupSensors(); private: FILE *mConfigFile; diff --git a/app/include/sensor/SensorManager.h b/app/include/sensor/SensorManager.h index 3b76cad..6ad4c88 100644 --- a/app/include/sensor/SensorManager.h +++ b/app/include/sensor/SensorManager.h @@ -12,6 +12,8 @@ public: std::vector> TemperatureSensors(); std::vector> RPMSensors(); + void ReloadSensors(); + private: std::unique_ptr mLMSensorsFacade; std::unique_ptr mGPUSensorsFacade; diff --git a/app/src/App.cxx b/app/src/App.cxx index a49db69..4320522 100644 --- a/app/src/App.cxx +++ b/app/src/App.cxx @@ -1,14 +1,35 @@ +#include "fan/Fan.h" +#include "fan/FanCurve.h" +#include #include +#include +#include + #include +#include +#include using namespace std; void App::Init() { - mFans = mSerializer.DeserializeFans(mSensorManager.RPMSensors()); + BOOST_LOG_FUNCTION(); + bool everythingFound = false; + + mFans = mSerializer.DeserializeFans(mSensorManager.RPMSensors()); auto fanCurves = mSerializer.DeserializeFanCurves( - mSensorManager.TemperatureSensors(), mFans); + mSensorManager.TemperatureSensors(), mFans, everythingFound); + + while (!everythingFound) { + BOOST_LOG_TRIVIAL(info) << "Couldn't find every sensor. Retrying in 5s..."; + this_thread::sleep_for(chrono::seconds(5)); + + mSensorManager.ReloadSensors(); + mFans = mSerializer.DeserializeFans(mSensorManager.RPMSensors()); + fanCurves = mSerializer.DeserializeFanCurves( + mSensorManager.TemperatureSensors(), mFans, everythingFound); + } mSettings = mSerializer.DeserializeSettings(); diff --git a/app/src/FanGenerator.cxx b/app/src/FanGenerator.cxx index 1fbbe80..5bbd353 100644 --- a/app/src/FanGenerator.cxx +++ b/app/src/FanGenerator.cxx @@ -8,7 +8,7 @@ #include #include -#define SETTLE_TIMEOUT 5 +#define SETTLE_TIMEOUT 10 using namespace std; diff --git a/app/src/Serializer.cxx b/app/src/Serializer.cxx index 7904ad7..d666ac0 100644 --- a/app/src/Serializer.cxx +++ b/app/src/Serializer.cxx @@ -1,9 +1,11 @@ #include #include #include - #include +#include +#include + #include #include #include @@ -31,6 +33,8 @@ void Serializer::SerializeFans(vector> fans) { vector> Serializer::DeserializeFans(vector> availableSensors) { + BOOST_LOG_FUNCTION(); + vector> fans; // Create a for the sensors first, then searching becomes cheaper @@ -61,6 +65,12 @@ Serializer::DeserializeFans(vector> availableSensors) { } catch (const std::exception &e) { std::cout << "Deserialization error! Message: " << e.what() << std::endl; } + + BOOST_LOG_TRIVIAL(trace) << "### Available Fans"; + for (auto f : fans) { + BOOST_LOG_TRIVIAL(trace) << f->toString(); + } + return fans; } @@ -86,7 +96,10 @@ json Serializer::ReadJson() { vector> Serializer::DeserializeFanCurves( std::vector> availableSensors, - std::vector> availableFans) { + std::vector> availableFans, bool &everythingFound) { + BOOST_LOG_FUNCTION(); + + everythingFound = true; auto data = ReadJson(); map> sensorMap; @@ -113,6 +126,11 @@ vector> Serializer::DeserializeFanCurves( for (auto &sensor : el.value()["Sensors"].items()) { if (sensorMap.contains(sensor.value())) sensors.push_back(sensorMap[sensor.value()]); + else { + BOOST_LOG_TRIVIAL(warning) + << "Sensor " << sensor.value() << " not found!"; + everythingFound = false; + } } for (auto &fan : el.value()["Fans"].items()) { @@ -123,8 +141,10 @@ vector> Serializer::DeserializeFanCurves( std::unique_ptr aggregator = aggregatorFromString(el.value()["AggregateFunction"]); - curves.push_back( - make_shared(steps, sensors, fans, std::move(aggregator))); + int hysteresis = el.value()["Hysteresis"]; + + curves.push_back(make_shared(steps, sensors, fans, + std::move(aggregator), hysteresis)); } return curves; diff --git a/app/src/fan/FanCurve.cxx b/app/src/fan/FanCurve.cxx index 54a6a96..16e95b0 100644 --- a/app/src/fan/FanCurve.cxx +++ b/app/src/fan/FanCurve.cxx @@ -1,6 +1,6 @@ -#include #include +#include #include #include @@ -11,14 +11,23 @@ using namespace std; FanCurve::FanCurve(std::vector steps, std::vector> sensors, std::vector> fans, - std::unique_ptr aggregator) + std::unique_ptr aggregator, int hysteresis) : mSteps(steps), mTempSensors(sensors), mFans(fans), - mAggregator(std::move(aggregator)) { + mAggregator(std::move(aggregator)), mHystersis(hysteresis), + mLastTemperature(INT_MIN) { PrintInfo(); } void FanCurve::DoFanControl() { - BOOST_LOG_FUNCTION(); + BOOST_LOG_FUNCTION() + + BOOST_LOG_TRIVIAL(trace) << "## Fans in curve"; + for (auto f : mFans) + BOOST_LOG_TRIVIAL(trace) << f->toString(); + + BOOST_LOG_TRIVIAL(trace) << "## Sensors in curve"; + for (auto s : mTempSensors) + BOOST_LOG_TRIVIAL(trace) << s->toString(); int temp = AggregateTemperature(); @@ -43,14 +52,22 @@ void FanCurve::DoFanControl() { targetFanPower = p0 + ((p1 - p0) / (t1 - t0)) * (temp - t0); } - for (auto f : mFans) { - if (!f->ZeroFanModeSupported() && f->RPM() <= 0) { - BOOST_LOG_TRIVIAL(warning) << "Fan stopped completely!"; - f->PWM(f->StartPWM()); - f->AdjustPWMLimits(); - } else { - f->PWM(targetFanPower); + BOOST_LOG_TRIVIAL(trace) << "Current temp: " << temp; + BOOST_LOG_TRIVIAL(trace) << "Last temp: " << mLastTemperature; + + BOOST_LOG_TRIVIAL(trace) << "# Hysteresis check"; + if (ExceedsHysteresis(temp)) { + BOOST_LOG_TRIVIAL(trace) << "passed"; + for (auto f : mFans) { + ApplyFanPower(f, targetFanPower); + + mLastTemperature = temp; } + } else { + for (auto f : mFans) + f->EnforceSetValue(); + + BOOST_LOG_TRIVIAL(trace) << "not passed"; } } @@ -94,3 +111,22 @@ void FanCurve::PrintInfo() { BOOST_LOG_TRIVIAL(info) << sStream.str(); } + +bool FanCurve::ExceedsHysteresis(int temperature) { + int lowThreshold = mLastTemperature - mHystersis; + int highThreshold = mLastTemperature + mHystersis; + + return temperature <= lowThreshold || temperature >= highThreshold; +} + +void FanCurve::ApplyFanPower(std::shared_ptr fan, int targetFanPower) { + BOOST_LOG_FUNCTION(); + + if (!fan->ZeroFanModeSupported() && fan->RPM() <= 0) { + BOOST_LOG_TRIVIAL(warning) << "Fan stopped completely!"; + fan->PWM(fan->StartPWM()); + fan->AdjustPWMLimits(); + } else { + fan->PWM(targetFanPower); + } +} diff --git a/app/src/fan/FanLabeler.cxx b/app/src/fan/FanLabeler.cxx index a1678d5..7714ff7 100644 --- a/app/src/fan/FanLabeler.cxx +++ b/app/src/fan/FanLabeler.cxx @@ -16,7 +16,7 @@ void FanLabeler::RunFanLabelInteraction( cout << endl; for (auto f : fans) { - cout << "Setting fan to max power" << endl; + cout << "Setting fan " << f->toString() << " to max power" << endl; f->PWM(100); diff --git a/app/src/fan/HwmonFan.cxx b/app/src/fan/HwmonFan.cxx index 402e818..a91d6fa 100644 --- a/app/src/fan/HwmonFan.cxx +++ b/app/src/fan/HwmonFan.cxx @@ -1,3 +1,4 @@ +#include #include #include #include @@ -9,6 +10,7 @@ #include #define TIMEOUT 10 +#define INHIBIT_STOP_PERIOD 120 #define STEP 2 using namespace std; @@ -21,12 +23,40 @@ HwmonFan::HwmonFan(std::shared_ptr pwmControl, void HwmonFan::PWM(int percent) { if (percent < mMinPWM) { - mPWMControl->Power(mMinPWM); + if (mZeroFanModeSupported && InhibitStopPeriodExpired()) { + SetPower(percent); + mWasStopped = true; + } else { + SetPower(mMinPWM); + } } else { - mPWMControl->Power(percent); + if (mWasStopped) { + mWasStopped = false; + mLastStartTime = chrono::steady_clock::now(); + SetPower(mStartPWM); + } else { + SetPower(percent); + } } } +bool HwmonFan::InhibitStopPeriodExpired() { + BOOST_LOG_FUNCTION(); + + auto result = chrono::steady_clock::now() - mLastStartTime > + chrono::duration(chrono::seconds(INHIBIT_STOP_PERIOD)); + + BOOST_LOG_TRIVIAL(trace) << "Inhibit-Stop period expired:" + << (result ? "true" : "false"); + + return result; +} + +void HwmonFan::SetPower(int percent) { + mPWMControl->Power(percent); + mSetValue = percent; +} + int HwmonFan::RPM() { return mRpmSensor->value(); } void HwmonFan::Label(std::string label) { mLabel = label; } @@ -99,6 +129,8 @@ void HwmonFan::AdjustPWMLimits() { } } +void HwmonFan::EnforceSetValue() { mPWMControl->Power(mSetValue); } + json HwmonFan::toJson() const { json obj; obj = {mPWMControl->toJson(), mRpmSensor->toJson(), @@ -111,6 +143,6 @@ const string HwmonFan::toString() const { if (!mLabel.empty()) { return mLabel; } else { - return "fan:" + mPWMControl->toString(); + return mPWMControl->toString(); } } diff --git a/app/src/main.cxx b/app/src/main.cxx index acfb290..fb10613 100644 --- a/app/src/main.cxx +++ b/app/src/main.cxx @@ -27,8 +27,6 @@ namespace logging = boost::log; App app; -static int doInitialSetup = 0; - void signal_handler(int s) { app.Shutdown(); } void InitLogging(bool verbose) { diff --git a/app/src/pwm/PWMControl.cxx b/app/src/pwm/PWMControl.cxx index 395b66f..e53272f 100644 --- a/app/src/pwm/PWMControl.cxx +++ b/app/src/pwm/PWMControl.cxx @@ -15,9 +15,7 @@ using namespace std; namespace fs = filesystem; -PWMControl::PWMControl(string controlPath) - : mControlPath(controlPath) -{ +PWMControl::PWMControl(string controlPath) : mControlPath(controlPath) { fs::path pathEnable(mControlPath + PWM_POSTFIX_ENABLE); fs::path pathMode(mControlPath + PWM_POSTFIX_MODE); @@ -26,8 +24,6 @@ PWMControl::PWMControl(string controlPath) ifstream istrm; - mCurrentValue = Power(); - istrm.open(mEnablePath); istrm >> mInitialEnable; istrm.close(); @@ -37,35 +33,28 @@ PWMControl::PWMControl(string controlPath) istrm.close(); } -PWMControl::~PWMControl() -{ +PWMControl::~PWMControl() { BOOST_LOG_FUNCTION(); BOOST_LOG_TRIVIAL(trace) << "Cleanup"; Reset(); } -void -PWMControl::Power(int percent) -{ +void PWMControl::Power(int percent) { BOOST_LOG_FUNCTION(); int pwmValue = (PWM_MAX_VALUE * percent) / 100; - if (percent != mCurrentValue) { + if (percent != Power()) { BOOST_LOG_TRIVIAL(trace) << "Updating control value of " << toString() << " to " << percent << "% (" << pwmValue << ")"; ofstream ostrm(mControlPath, ios::trunc); ostrm << pwmValue; ostrm.close(); - - mCurrentValue = percent; } } -int -PWMControl::Power() -{ +int PWMControl::Power() { int value; ifstream istrm; @@ -75,17 +64,13 @@ PWMControl::Power() return (value * 100) / PWM_MAX_VALUE; } -void -PWMControl::EnableManualControl() -{ +void PWMControl::EnableManualControl() { ofstream ostrm(mEnablePath, ios::trunc); ostrm << static_cast(PWM_ENABLE::MANUAL_CONTROL); ostrm.close(); } -void -PWMControl::Reset() -{ +void PWMControl::Reset() { ofstream ostrm(mEnablePath, ios::trunc); ostrm << mInitialEnable; @@ -97,15 +82,9 @@ PWMControl::Reset() ostrm.close(); } -const string -PWMControl::toString() const -{ - return fs::path(mControlPath).filename(); -} +const string PWMControl::toString() const { return fs::path(mControlPath); } -json -PWMControl::toJson() const -{ - json obj = { "PWMControl", mControlPath }; +json PWMControl::toJson() const { + json obj = {"PWMControl", mControlPath}; return obj; } diff --git a/app/src/sensor/LMSensorsFacade.cxx b/app/src/sensor/LMSensorsFacade.cxx index ec92880..920b22c 100644 --- a/app/src/sensor/LMSensorsFacade.cxx +++ b/app/src/sensor/LMSensorsFacade.cxx @@ -12,12 +12,10 @@ using namespace std; #define CONFIG_FILE "/etc/conf.d/sensors" LMSensorsFacade::LMSensorsFacade() : mConfigFile(fopen(CONFIG_FILE, "r")) { - if (sensors_init(mConfigFile) != 0) { - throw runtime_error("Config file doesn't exist"); - } + InitSensors(); } -LMSensorsFacade::~LMSensorsFacade() { sensors_cleanup(); } +LMSensorsFacade::~LMSensorsFacade() { CleanupSensors(); } std::vector> LMSensorsFacade::TemperatureSensors() { return Sensors(); @@ -48,3 +46,16 @@ std::vector> LMSensorsFacade::Sensors() { return sensors; } + +void LMSensorsFacade::ReloadSensors() { + CleanupSensors(); + InitSensors(); +} + +void LMSensorsFacade::InitSensors() { + if (sensors_init(mConfigFile) != 0) { + throw runtime_error("Config file doesn't exist"); + } +} + +void LMSensorsFacade::CleanupSensors() { sensors_cleanup(); } diff --git a/app/src/sensor/SensorManager.cxx b/app/src/sensor/SensorManager.cxx index 9f29f27..b9bf07a 100644 --- a/app/src/sensor/SensorManager.cxx +++ b/app/src/sensor/SensorManager.cxx @@ -1,5 +1,8 @@ #include +#include +#include + using namespace std; SensorManager::SensorManager() @@ -7,10 +10,17 @@ SensorManager::SensorManager() mGPUSensorsFacade(make_unique()) {} vector> SensorManager::TemperatureSensors() { + BOOST_LOG_FUNCTION(); + vector> tempSensors; tempSensors = mLMSensorsFacade->TemperatureSensors(); + BOOST_LOG_TRIVIAL(trace) << "### Temperature sensors:"; + for (auto s : tempSensors) { + BOOST_LOG_TRIVIAL(trace) << s->toString(); + } + // auto gpuSensors = mGPUSensorsFacade->TemperatureSensors(); // tempSensors.insert(tempSensors.end(), gpuSensors.begin(), // gpuSensors.end()); @@ -21,3 +31,5 @@ vector> SensorManager::TemperatureSensors() { vector> SensorManager::RPMSensors() { return mLMSensorsFacade->RPMSensors(); } + +void SensorManager::ReloadSensors() { mLMSensorsFacade->ReloadSensors(); }