12 Commits

Author SHA1 Message Date
0f91960ef9 Adapt version 2022-10-06 10:47:55 +02:00
d9192f10be Add SIGTERM handling, adjust limit finding step 2022-10-06 10:39:43 +02:00
f606a1e450 Update version 2022-10-04 09:55:23 +02:00
2d744245fc Fix segmentation fault on shutdown 2022-10-04 09:53:57 +02:00
bd1695b4a7 Update version 2022-10-04 09:42:40 +02:00
987e65fd90 Add stuff to .gitignore 2022-10-04 09:42:14 +02:00
e567ede3ef Add logic to adjust minPWM on fan stop 2022-10-04 09:41:04 +02:00
d65934a278 Update version 2022-10-04 00:29:21 +02:00
46a06214a2 Fix severe bug, improve logging, handle fan stops
Conversion from power percentage to PWM value didn't take into floating
point arithmetic into account
Make log messages more helpful, add handling for fan stopping completely
2022-10-04 00:26:57 +02:00
1c2067286e Cleanup logging output 2022-10-03 14:39:09 +02:00
16c7a930c2 Update version, remove integrity check 2022-10-03 14:29:43 +02:00
95b6f248c8 Reduce file writes, add logging capability 2022-10-03 14:02:44 +02:00
14 changed files with 184 additions and 64 deletions

5
.gitignore vendored
View File

@@ -1,3 +1,8 @@
oot/ oot/
compile_commands.json compile_commands.json
.cache/ .cache/
pkg/
rel_oot/
src/
*.pkg.tar.zst
*.tar.gz

View File

@@ -1,5 +1,5 @@
pkgname=fantasize pkgname=fantasize
pkgver=0.1.0 pkgver=0.1.5
pkgrel=1 pkgrel=1
pkgdesc='C++ fan control for Linux' pkgdesc='C++ fan control for Linux'
url='https://github.com/Tabascl/fantasize.git' url='https://github.com/Tabascl/fantasize.git'
@@ -7,7 +7,7 @@ source=("$pkgname-$pkgver.tar.gz::https://github.com/Tabascl/fantasize/archive/r
arch=('x86_64') arch=('x86_64')
license=('GPL3') license=('GPL3')
makedepends=('git' 'cmake' 'nlohmann-json' 'boost' 'cuda') makedepends=('git' 'cmake' 'nlohmann-json' 'boost' 'cuda')
sha256sums=('69d61255edb49b66396f00b7bfadb39ce1220769838929ad74dc9f3301742ce7') sha256sums=('SKIP')
build() { build() {
cmake -S "$pkgname-$pkgver/app" -B build -DCMAKE_BUILD_TYPE=Release cmake -S "$pkgname-$pkgver/app" -B build -DCMAKE_BUILD_TYPE=Release

View File

@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
project(fantasize VERSION 0.1.0) project(fantasize VERSION 0.1.5)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(nlohmann_json 3.11.2 REQUIRED) find_package(nlohmann_json 3.11.2 REQUIRED)
find_package(Boost 1.80 COMPONENTS program_options REQUIRED) find_package(Boost 1.80 COMPONENTS program_options log log_setup date_time REQUIRED)
add_executable(${PROJECT_NAME} add_executable(${PROJECT_NAME}
src/main.cxx src/main.cxx

View File

@@ -27,6 +27,8 @@ private:
FanGenerator mFanGenerator; FanGenerator mFanGenerator;
std::unique_ptr<Controller> mController; std::unique_ptr<Controller> mController;
std::vector<std::shared_ptr<Fan>> mFans;
}; };
#endif // APP_H_ #endif // APP_H_

View File

@@ -10,11 +10,14 @@ public:
virtual int RPM() = 0; virtual int RPM() = 0;
virtual void Label(std::string label) = 0; virtual void Label(std::string label) = 0;
virtual void MinPWM(int value) = 0;
virtual void StartPWM(int value) = 0;
virtual void FindMinPWM() = 0; virtual void MinPWM(int value) = 0;
virtual void FindStartPWM() = 0; virtual int MinPWM() = 0;
virtual void StartPWM(int value) = 0;
virtual int StartPWM() = 0;
virtual void FindPWMLimits() = 0;
}; };
#endif // FAN_H_ #endif // FAN_H_

View File

@@ -17,11 +17,14 @@ public:
int RPM() override; int RPM() override;
void Label(std::string label) override; void Label(std::string label) override;
void MinPWM(int value) override;
void StartPWM(int value) override;
void FindMinPWM() override; void MinPWM(int value) override;
void FindStartPWM() override; int MinPWM() override;
void StartPWM(int value) override;
int StartPWM() override;
void FindPWMLimits() override;
json toJson() const override; json toJson() const override;

View File

@@ -16,8 +16,8 @@ public:
PWMControl(std::string controlPath); PWMControl(std::string controlPath);
~PWMControl(); ~PWMControl();
void pwm(int percent); void Power(int percent);
int pwm(); int Power();
void EnableManualControl(); void EnableManualControl();
void Reset(); void Reset();
@@ -27,7 +27,7 @@ public:
json toJson() const override; json toJson() const override;
private: private:
int readValue(std::string path); int mCurrentValue;
std::string mControlPath; std::string mControlPath;
std::string mEnablePath; std::string mEnablePath;

View File

@@ -5,10 +5,10 @@
using namespace std; using namespace std;
void App::Init() { void App::Init() {
auto fans = mSerializer.DeserializeFans(mSensorManager.RPMSensors()); mFans = mSerializer.DeserializeFans(mSensorManager.RPMSensors());
auto fanCurves = mSerializer.DeserializeFanCurves( auto fanCurves = mSerializer.DeserializeFanCurves(
mSensorManager.TemperatureSensors(), fans); mSensorManager.TemperatureSensors(), mFans);
mController = make_unique<Controller>(fanCurves); mController = make_unique<Controller>(fanCurves);
} }
@@ -18,11 +18,13 @@ void App::InitialSetup() {
mPWMControlFacade.PWMControls()); mPWMControlFacade.PWMControls());
std::for_each(std::execution::par, std::begin(fans), std::end(fans), std::for_each(std::execution::par, std::begin(fans), std::end(fans),
[](auto &&fan) { fan->FindMinPWM(); }); [](auto &&fan) { fan->FindPWMLimits(); });
mFanLabeler.RunFanLabelInteraction(fans); mFanLabeler.RunFanLabelInteraction(fans);
mSerializer.SerializeFans(fans); mSerializer.SerializeFans(fans);
mFans = fans;
} }
void App::NormalOperation() { void App::NormalOperation() {
@@ -30,4 +32,8 @@ void App::NormalOperation() {
mController->StartFanControlLoop(); mController->StartFanControlLoop();
} }
void App::Shutdown() { mController.reset(); } void App::Shutdown() {
mSerializer.SerializeFans(mFans);
mFans.clear();
mController.reset();
}

View File

@@ -22,7 +22,7 @@ FanGenerator::FindFans(vector<shared_ptr<Sensor>> rpmSensors,
cout << "Setting all fans to maximum speed" << endl; cout << "Setting all fans to maximum speed" << endl;
for (auto c : pwmControls) { for (auto c : pwmControls) {
c->EnableManualControl(); c->EnableManualControl();
c->pwm(100); c->Power(100);
} }
// Wait for fans to settle // Wait for fans to settle
@@ -41,7 +41,7 @@ FanGenerator::FindFans(vector<shared_ptr<Sensor>> rpmSensors,
for (auto c : pwmControls) { for (auto c : pwmControls) {
cout << "Setting " << c->toString() cout << "Setting " << c->toString()
<< " to 50% and wait for it to settle..." << endl; << " to 50% and wait for it to settle..." << endl;
c->pwm(50); c->Power(50);
this_thread::sleep_for(chrono::seconds(SETTLE_TIMEOUT)); this_thread::sleep_for(chrono::seconds(SETTLE_TIMEOUT));
@@ -53,7 +53,7 @@ FanGenerator::FindFans(vector<shared_ptr<Sensor>> rpmSensors,
} }
cout << "Setting fan back to 100%" << endl; cout << "Setting fan back to 100%" << endl;
c->pwm(100); c->Power(100);
} }
return mapping; return mapping;

View File

@@ -47,10 +47,12 @@ Serializer::DeserializeFans(vector<shared_ptr<Sensor>> availableSensors) {
auto rpmSensor = sensorMap[el.value()["LMSensor"]]; auto rpmSensor = sensorMap[el.value()["LMSensor"]];
int minPWM = el.value()["MinPWM"]; int minPWM = el.value()["MinPWM"];
int startPWM = el.value()["StartPWM"];
string label = el.value()["Label"]; string label = el.value()["Label"];
auto fan = make_shared<HwmonFan>(pwmControl, rpmSensor); auto fan = make_shared<HwmonFan>(pwmControl, rpmSensor);
fan->MinPWM(minPWM); fan->MinPWM(minPWM);
fan->StartPWM(startPWM);
fan->Label(label); fan->Label(label);
fans.push_back(fan); fans.push_back(fan);

View File

@@ -1,5 +1,8 @@
#include <boost/log/attributes/named_scope.hpp>
#include <iostream> #include <iostream>
#include <boost/log/trivial.hpp>
#include <fan/FanCurve.h> #include <fan/FanCurve.h>
using namespace std; using namespace std;
@@ -8,20 +11,21 @@ FanCurve::FanCurve(std::vector<FanStep> steps,
std::vector<std::shared_ptr<Sensor>> sensors, std::vector<std::shared_ptr<Sensor>> sensors,
std::vector<std::shared_ptr<Fan>> fans) std::vector<std::shared_ptr<Fan>> fans)
: mSteps(steps), mTempSensors(sensors), mFans(fans) { : mSteps(steps), mTempSensors(sensors), mFans(fans) {
cout << "Initialized Fan Curve:" << endl;
PrintInfo(); PrintInfo();
} }
void FanCurve::DoFanControl() { void FanCurve::DoFanControl() {
BOOST_LOG_FUNCTION();
int temp = AggregateTemperature(); int temp = AggregateTemperature();
int t0, t1, p0, p1; int t0, t1, p0, p1;
int targetFanSpeed; int targetFanPower;
if (temp <= mSteps[0].Temp) { if (temp <= mSteps[0].Temp) {
targetFanSpeed = mSteps[0].Percent; targetFanPower = mSteps[0].Percent;
} else if (temp > mSteps[mSteps.size() - 1].Temp) { } else if (temp > mSteps[mSteps.size() - 1].Temp) {
targetFanSpeed = mSteps[mSteps.size() - 1].Percent; targetFanPower = mSteps[mSteps.size() - 1].Percent;
} else { } else {
for (int i = 0; i < mSteps.size(); i++) { for (int i = 0; i < mSteps.size(); i++) {
if (temp > mSteps[i].Temp) { if (temp > mSteps[i].Temp) {
@@ -33,11 +37,19 @@ void FanCurve::DoFanControl() {
} }
} }
targetFanSpeed = p0 + ((p1 - p0) / (t1 - t0)) * (temp - t0); targetFanPower = p0 + ((p1 - p0) / (t1 - t0)) * (temp - t0);
} }
for (auto f : mFans) { for (auto f : mFans) {
f->PWM(targetFanSpeed); if (f->RPM() <= 0) {
BOOST_LOG_TRIVIAL(warning) << "Fan stopped completely!";
f->PWM(f->StartPWM());
BOOST_LOG_TRIVIAL(info) << "Adjusting minPWM of fan " << f->toString();
f->MinPWM(f->MinPWM() + 2);
} else {
f->PWM(targetFanPower);
}
} }
} }
@@ -52,28 +64,32 @@ int FanCurve::AggregateTemperature() {
} }
void FanCurve::PrintInfo() { void FanCurve::PrintInfo() {
BOOST_LOG_FUNCTION()
BOOST_LOG_TRIVIAL(info) << "### Fan curve:";
stringstream sStream; stringstream sStream;
cout << "Steps: "; sStream << "Steps: ";
for (auto s : mSteps) { for (auto s : mSteps) {
sStream << "[ " << s.Temp << "C, " << s.Percent << "% ] "; sStream << "[ " << s.Temp << "C, " << s.Percent << "% ] ";
} }
cout << sStream.str() << endl; BOOST_LOG_TRIVIAL(info) << sStream.str();
sStream.str(string()); sStream.str(string());
cout << "Sensors: "; sStream << "Sensors: ";
for (auto s : mTempSensors) { for (auto s : mTempSensors) {
sStream << s->toString() << ", "; sStream << s->toString() << ", ";
} }
cout << sStream.str() << endl; BOOST_LOG_TRIVIAL(info) << sStream.str();
sStream.str(string()); sStream.str(string());
cout << "Fans: "; sStream << "Fans: ";
for (auto s : mFans) { for (auto s : mFans) {
sStream << s->toString() << ", "; sStream << s->toString() << ", ";
} }
cout << sStream.str() << endl; BOOST_LOG_TRIVIAL(info) << sStream.str();
} }

View File

@@ -7,7 +7,8 @@
#include <boost/json/object.hpp> #include <boost/json/object.hpp>
#include <fan/HwmonFan.h> #include <fan/HwmonFan.h>
#define TIMEOUT 5 #define TIMEOUT 10
#define STEP 2
using namespace std; using namespace std;
@@ -19,9 +20,9 @@ HwmonFan::HwmonFan(std::shared_ptr<PWMControl> pwmControl,
void HwmonFan::PWM(int percent) { void HwmonFan::PWM(int percent) {
if (percent < mMinPWM) { if (percent < mMinPWM) {
mPWMControl->pwm(mMinPWM); mPWMControl->Power(mMinPWM);
} else { } else {
mPWMControl->pwm(percent); mPWMControl->Power(percent);
} }
} }
@@ -31,41 +32,59 @@ void HwmonFan::Label(std::string label) { mLabel = label; }
void HwmonFan::MinPWM(int value) { mMinPWM = value; } void HwmonFan::MinPWM(int value) { mMinPWM = value; }
int HwmonFan::MinPWM() { return mMinPWM; }
int HwmonFan::StartPWM() { return mStartPWM; }
void HwmonFan::StartPWM(int value) { mStartPWM = value; } void HwmonFan::StartPWM(int value) { mStartPWM = value; }
void HwmonFan::FindMinPWM() { void HwmonFan::FindPWMLimits() {
cout << "Looking for minimal PWM" << endl; cout << "Looking for minimal PWM" << endl;
int minPWM = 0; int minPWM = 0;
mMinPWM = 0; mMinPWM = 0;
mStartPWM = 0;
for (int curPWM = 100; curPWM > 0; curPWM -= 5) { for (int curPWM = 100; curPWM > 0; curPWM -= STEP) {
PWM(curPWM); PWM(curPWM);
this_thread::sleep_for(chrono::seconds(TIMEOUT)); this_thread::sleep_for(chrono::seconds(TIMEOUT));
int curRPM = RPM(); if (RPM() <= 0) {
minPWM = curPWM + STEP;
if (curRPM <= 0) {
minPWM = curPWM + 5;
break; break;
} }
} }
cout << "Setting minimal PWM: " << minPWM << endl;
mMinPWM = minPWM;
if (minPWM == 0) { if (minPWM == 0) {
cout << "Fan never stopped. "; cout << "Fan never stopped. ";
} } else {
cout << "Setting minimal PWM: " << minPWM << endl; int startPWM = 0;
mMinPWM = minPWM; cout << "Looking for start PWM!" << endl;
for (int curPWM = minPWM - STEP; curPWM < 100; curPWM += STEP) {
PWM(curPWM);
this_thread::sleep_for(chrono::seconds(TIMEOUT));
if (RPM() > 0) {
cout << "Setting start PWM: " << startPWM << endl;
startPWM = curPWM;
break;
}
} }
void HwmonFan::FindStartPWM() {} mStartPWM = startPWM;
}
}
json HwmonFan::toJson() const { json HwmonFan::toJson() const {
json obj; json obj;
obj = {mPWMControl->toJson(), obj = {mPWMControl->toJson(),
mRpmSensor->toJson(), mRpmSensor->toJson(),
{"Label", mLabel}, {"Label", mLabel},
{"MinPWM", mMinPWM}}; {"MinPWM", mMinPWM},
{"StartPWM", mStartPWM}};
return obj; return obj;
} }

View File

@@ -1,15 +1,29 @@
#include <csignal>
#include <iostream>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/expressions/formatters/named_scope.hpp>
#include <boost/log/expressions/message.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/program_options.hpp>
#include <boost/program_options/options_description.hpp> #include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp> #include <boost/program_options/parsers.hpp>
#include <boost/program_options/value_semantic.hpp> #include <boost/program_options/value_semantic.hpp>
#include <boost/program_options/variables_map.hpp> #include <boost/program_options/variables_map.hpp>
#include <csignal>
#include <iostream>
#include <boost/program_options.hpp>
#include <App.h> #include <App.h>
#define PROJECT_VERSION "v0.1.5"
namespace po = boost::program_options; namespace po = boost::program_options;
namespace logging = boost::log;
App app; App app;
@@ -17,18 +31,54 @@ static int doInitialSetup = 0;
void signal_handler(int s) { app.Shutdown(); } void signal_handler(int s) { app.Shutdown(); }
void InitLogging(bool verbose) {
logging::add_console_log(
std::clog,
logging::keywords::format =
(logging::expressions::stream
<< "["
<< logging::expressions::format_date_time<boost::posix_time::ptime>(
"TimeStamp", "%Y-%m-%d %H:%M:%S")
<< "]["
<< logging::expressions::format_named_scope(
"Scope", logging::keywords::format = "%c")
<< "]"
<< "[" << logging::trivial::severity << "] "
<< logging::expressions::smessage));
logging::add_common_attributes();
logging::core::get()->add_global_attribute(
"Scope", logging::attributes::named_scope());
BOOST_LOG_FUNCTION();
if (!verbose) {
logging::core::get()->set_filter(logging::trivial::severity >=
logging::trivial::info);
} else {
BOOST_LOG_TRIVIAL(info) << "Verbose logging enabled";
}
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
BOOST_LOG_FUNCTION()
BOOST_LOG_TRIVIAL(info) << "Version: " << PROJECT_VERSION;
signal(SIGINT, signal_handler); signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
po::options_description desc("Allowed options"); po::options_description desc("Allowed options");
desc.add_options()("help", "produce help message")("setup", po::bool_switch(), desc.add_options()("help,h", "produce help message")(
"run initial setup"); "setup,s", po::bool_switch(),
"run initial setup")("verbose,v", po::bool_switch(), "print debug info");
po::variables_map vm; po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm); po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm); po::notify(vm);
try { try {
InitLogging(vm["verbose"].as<bool>());
if (vm.count("help")) { if (vm.count("help")) {
std::cout << desc << "\n"; std::cout << desc << "\n";
return 0; return 0;

View File

@@ -1,8 +1,10 @@
#include <boost/json/object.hpp> #include <boost/log/attributes/named_scope.hpp>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <boost/log/trivial.hpp>
#include <pwm/PWMControl.h> #include <pwm/PWMControl.h>
#define PWM_POSTFIX_ENABLE "_enable" #define PWM_POSTFIX_ENABLE "_enable"
@@ -22,6 +24,8 @@ PWMControl::PWMControl(string controlPath) : mControlPath(controlPath) {
ifstream istrm; ifstream istrm;
mCurrentValue = Power();
istrm.open(mEnablePath); istrm.open(mEnablePath);
istrm >> mInitialEnable; istrm >> mInitialEnable;
istrm.close(); istrm.close();
@@ -32,26 +36,36 @@ PWMControl::PWMControl(string controlPath) : mControlPath(controlPath) {
} }
PWMControl::~PWMControl() { PWMControl::~PWMControl() {
cout << "Cleanup" << endl; BOOST_LOG_FUNCTION();
BOOST_LOG_TRIVIAL(trace) << "Cleanup";
Reset(); Reset();
} }
void PWMControl::pwm(int percent) { void PWMControl::Power(int percent) {
int pwmValue = PWM_MAX_VALUE * percent / 100; BOOST_LOG_FUNCTION();
int pwmValue = (PWM_MAX_VALUE * percent) / 100;
if (percent != mCurrentValue) {
BOOST_LOG_TRIVIAL(trace)
<< "Updating control value to " << percent << "% (" << pwmValue << ")";
ofstream ostrm(mControlPath, ios::trunc); ofstream ostrm(mControlPath, ios::trunc);
ostrm << pwmValue; ostrm << pwmValue;
ostrm.close(); ostrm.close();
mCurrentValue = percent;
}
} }
int PWMControl::pwm() { int PWMControl::Power() {
int value; int value;
ifstream istrm; ifstream istrm;
istrm.open(mControlPath); istrm.open(mControlPath);
istrm >> value; istrm >> value;
return value; return (value * 100) / PWM_MAX_VALUE;
} }
void PWMControl::EnableManualControl() { void PWMControl::EnableManualControl() {