19 Commits

Author SHA1 Message Date
Tabascl
8ff1d8be9d Bump version numbers 2024-07-07 23:52:03 +02:00
Tabascl
78b2a62643 Remove aggregator, fix changing HWMON paths
Add logic to use actual device paths for the PWM sensors instead of their
standart HWMON paths.

This was done after noticing that due to the load order of kernel modules,
the index of HWMON paths could change.
2024-07-07 22:19:27 +02:00
e2509cea8b Various fixes
- Change unit path
- Rename method
- Make CurrentPWM method return its value in PWM instead of percent
2023-10-08 13:50:33 +02:00
0a6bab36be Hysteresis, Inhibit-Stop Timeframe 2023-08-28 14:55:07 +02:00
d1649b7de1 Bump version 2023-07-24 13:58:12 +02:00
ff72f8d2ea Add support for zero-fan mode, fix sensor identifiers 2023-07-24 13:55:53 +02:00
ad12f7a981 Switch to meson build system 2023-07-23 23:43:47 +02:00
33a760489e Remove NVIDIA stuff 2023-07-23 18:04:17 +02:00
10fbcdb0f6 Add missing classes 2023-03-15 15:20:35 +01:00
f383eb1b79 Add average and max aggregator functions 2023-03-15 15:16:56 +01:00
7b0b419a22 Fix crash on startup 2023-01-19 13:32:56 +01:00
ec480f5a17 Remove leftover include statement 2022-11-05 02:50:18 +01:00
0109f66be3 Remove leftover include statement 2022-11-05 02:47:44 +01:00
75d6af4c8b Remove leftover include statement 2022-11-05 02:47:13 +01:00
815b78aa67 Change required version of boost libs 2022-11-05 02:45:12 +01:00
f718f82ad1 Add option to set frequency in json 2022-10-18 11:05:05 +02:00
596a30b76a Improve logic to handle fans stopping 2022-10-10 00:25:09 +02:00
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
32 changed files with 493 additions and 148 deletions

2
.gitignore vendored
View File

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

View File

@@ -1,19 +1,21 @@
pkgname=fantasize pkgname=fantasize
pkgver=0.1.4 pkgver=0.3.0
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'
source=("$pkgname-$pkgver.tar.gz::https://github.com/Tabascl/fantasize/archive/refs/tags/v$pkgver.tar.gz") source=("$pkgname-$pkgver.tar.gz::https://github.com/Tabascl/fantasize/archive/refs/tags/v$pkgver.tar.gz")
arch=('x86_64') arch=('x86_64')
license=('GPL3') license=('GPL3')
makedepends=('git' 'cmake' 'nlohmann-json' 'boost' 'cuda') makedepends=('git' 'meson' 'nlohmann-json' 'boost')
sha256sums=('SKIP') sha256sums=('SKIP')
build() { build() {
cmake -S "$pkgname-$pkgver/app" -B build -DCMAKE_BUILD_TYPE=Release meson setup build "$pkgname-$pkgver/app" -Dbuildtype=Release
cmake --build build cd build
meson compile
} }
package() { package() {
cmake --install build --prefix "$pkgdir/" --verbose cd build
meson install
} }

View File

@@ -1,35 +0,0 @@
cmake_minimum_required(VERSION 3.0)
project(fantasize VERSION 0.1.4)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(nlohmann_json 3.11.2 REQUIRED)
find_package(Boost 1.80 COMPONENTS program_options log log_setup date_time REQUIRED)
add_executable(${PROJECT_NAME}
src/main.cxx
src/sensor/LMSensorsFacade.cxx
src/sensor/GPUSensorsFacade.cxx
src/sensor/Sensor.cxx
src/sensor/NvidiaSensor.cxx
src/sensor/LMSensor.cxx
src/pwm/PWMControl.cxx
src/pwm/PWMControlFacade.cxx
src/fan/HwmonFan.cxx
src/fan/FanCurve.cxx
src/fan/FanLabeler.cxx
src/FanGenerator.cxx
src/Serializer.cxx
src/sensor/SensorManager.cxx
src/Controller.cxx
src/App.cxx
)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
target_include_directories(${PROJECT_NAME} PUBLIC include /opt/cuda)
target_link_libraries(${PROJECT_NAME} PUBLIC sensors nvidia-ml nlohmann_json::nlohmann_json tbb ${Boost_LIBRARIES})
install(TARGETS ${PROJECT_NAME} DESTINATION usr/local/bin)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/unit/fantasize.service DESTINATION usr/lib/systemd/system)

View File

@@ -26,6 +26,8 @@ private:
FanLabeler mFanLabeler; FanLabeler mFanLabeler;
FanGenerator mFanGenerator; FanGenerator mFanGenerator;
std::shared_ptr<Settings> mSettings;
std::unique_ptr<Controller> mController; std::unique_ptr<Controller> mController;
std::vector<std::shared_ptr<Fan>> mFans; std::vector<std::shared_ptr<Fan>> mFans;

View File

@@ -5,11 +5,13 @@
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <Settings.h>
#include <fan/FanCurve.h> #include <fan/FanCurve.h>
class Controller { class Controller {
public: public:
Controller(std::vector<std::shared_ptr<FanCurve>> curves); Controller(std::shared_ptr<Settings> settings,
std::vector<std::shared_ptr<FanCurve>> curves);
~Controller(); ~Controller();
void StartFanControlLoop(); void StartFanControlLoop();
@@ -18,6 +20,8 @@ public:
private: private:
void Loop(); void Loop();
int mTimeout;
std::vector<std::shared_ptr<FanCurve>> mFanCurves; std::vector<std::shared_ptr<FanCurve>> mFanCurves;
std::atomic<bool> mRun; std::atomic<bool> mRun;

View File

@@ -1,12 +1,13 @@
#ifndef SERIALIZER_H_ #ifndef SERIALIZER_H_
#define SERIALIZER_H_ #define SERIALIZER_H_
#include "fan/HwmonFan.h"
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <Settings.h>
#include <fan/Aggregators.h>
#include <fan/Fan.h> #include <fan/Fan.h>
#include <fan/FanCurve.h> #include <fan/FanCurve.h>
#include <sensor/Sensor.h> #include <sensor/Sensor.h>
@@ -24,11 +25,14 @@ public:
DeserializeFans(std::vector<std::shared_ptr<Sensor>> availableSensors); DeserializeFans(std::vector<std::shared_ptr<Sensor>> availableSensors);
std::vector<std::shared_ptr<FanCurve>> std::vector<std::shared_ptr<FanCurve>>
DeserializeFanCurves(std::vector<std::shared_ptr<Sensor>> availableSensors, DeserializeFanCurves(std::vector<std::shared_ptr<Sensor>> availableSensors,
std::vector<std::shared_ptr<Fan>> availableFans); std::vector<std::shared_ptr<Fan>> availableFans,
bool &everythingFound);
std::shared_ptr<Settings> DeserializeSettings();
private: private:
void WriteJson(json o); void WriteJson(json o);
json ReadJson(); json ReadJson();
std::unique_ptr<Aggregator> aggregatorFromString(std::string str) const;
}; };
#endif // SERIALIZER_H_ #endif // SERIALIZER_H_

17
app/include/Settings.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef SETTINGS_H_
#define SETTINGS_H_
#define FREQUENCY_DEFAULT 1
class Settings {
public:
Settings(int frequency);
int Frequency() const;
private:
void LogSettings();
int mFrequency;
};
#endif // SETTINGS_H_

View File

@@ -0,0 +1,27 @@
#ifndef AGGREGATORS_H_
#define AGGREGATORS_H_
#include <sensor/Sensor.h>
class Aggregator
{
public:
virtual int aggregate(std::vector<std::shared_ptr<Sensor>> sensors) const = 0;
virtual const std::string toString() const = 0;
};
class AverageAggregator : public Aggregator
{
public:
int aggregate(std::vector<std::shared_ptr<Sensor>> sensors) const override;
const std::string toString() const override;
};
class MaxAggregator : public Aggregator
{
public:
int aggregate(std::vector<std::shared_ptr<Sensor>> sensors) const override;
const std::string toString() const override;
};
#endif // AGGERGATORS_H_

View File

@@ -17,7 +17,13 @@ public:
virtual void StartPWM(int value) = 0; virtual void StartPWM(int value) = 0;
virtual int StartPWM() = 0; virtual int StartPWM() = 0;
virtual void ZeroFanModeSupported(bool value) = 0;
virtual bool ZeroFanModeSupported() = 0;
virtual void FindPWMLimits() = 0; virtual void FindPWMLimits() = 0;
virtual void AdjustPWMLimits() = 0;
virtual void EnforceSetValue() = 0;
}; };
#endif // FAN_H_ #endif // FAN_H_

View File

@@ -4,6 +4,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <fan/Aggregators.h>
#include <fan/HwmonFan.h> #include <fan/HwmonFan.h>
#include <sensor/Sensor.h> #include <sensor/Sensor.h>
@@ -16,17 +17,22 @@ class FanCurve {
public: public:
FanCurve(std::vector<FanStep> steps, 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,
std::unique_ptr<Aggregator> aggregator, int hysteresis);
void DoFanControl(); void DoFanControl();
private: private:
int AggregateTemperature();
void PrintInfo(); void PrintInfo();
bool ExceedsHysteresis(int temperature);
void ApplyFanPower(std::shared_ptr<Fan> fan, int targetFanPower);
std::vector<FanStep> mSteps; std::vector<FanStep> mSteps;
std::vector<std::shared_ptr<Sensor>> mTempSensors; std::vector<std::shared_ptr<Sensor>> mTempSensors;
std::vector<std::shared_ptr<Fan>> mFans; std::vector<std::shared_ptr<Fan>> mFans;
std::unique_ptr<Aggregator> mAggregator;
int mHystersis;
int mLastTemperature;
}; };
#endif // FANCURVE_H_ #endif // FANCURVE_H_

View File

@@ -1,7 +1,7 @@
#ifndef HWMONFAN_H_ #ifndef HWMONFAN_H_
#define HWMONFAN_H_ #define HWMONFAN_H_
#include <boost/json/object.hpp> #include <chrono>
#include <memory> #include <memory>
#include <fan/Fan.h> #include <fan/Fan.h>
@@ -24,19 +24,33 @@ public:
void StartPWM(int value) override; void StartPWM(int value) override;
int StartPWM() override; int StartPWM() override;
void ZeroFanModeSupported(bool value) override;
bool ZeroFanModeSupported() override;
void FindPWMLimits() override; void FindPWMLimits() override;
void AdjustPWMLimits() override;
void EnforceSetValue() override;
json toJson() const override; json toJson() const override;
const std::string toString() const override; const std::string toString() const override;
private: private:
bool InhibitStopPeriodExpired();
void SetPower(int percent);
std::shared_ptr<PWMControl> mPWMControl; std::shared_ptr<PWMControl> mPWMControl;
std::shared_ptr<Sensor> mRpmSensor; std::shared_ptr<Sensor> mRpmSensor;
std::string mLabel; std::string mLabel;
int mMinPWM; int mMinPWM = 0;
int mStartPWM; int mStartPWM = 0;
bool mZeroFanModeSupported = false;
std::chrono::time_point<std::chrono::steady_clock> mLastAdjustmentTime;
std::chrono::time_point<std::chrono::steady_clock> mLastStartTime;
int mSetValue = 0;
bool mWasStopped = false;
}; };
#endif // HWMONFAN_H_ #endif // HWMONFAN_H_

View File

@@ -1,7 +1,6 @@
#ifndef PWMCONTROL_H_ #ifndef PWMCONTROL_H_
#define PWMCONTROL_H_ #define PWMCONTROL_H_
#include <boost/json/object.hpp>
#include <string> #include <string>
#include <Printable.h> #include <Printable.h>
@@ -13,11 +12,11 @@ enum class PWM_MODE { DC = 0, PWM };
class PWMControl : public Printable, public Serializable { class PWMControl : public Printable, public Serializable {
public: public:
PWMControl(std::string controlPath); PWMControl(std::string controlPath, int deviceIndex);
~PWMControl(); ~PWMControl();
void Power(int percent); void SetPower(int percent);
int Power(); int CurrentPWM();
void EnableManualControl(); void EnableManualControl();
void Reset(); void Reset();
@@ -27,11 +26,11 @@ public:
json toJson() const override; json toJson() const override;
private: private:
int mCurrentValue; std::string mConfigPath;
std::string mControlPath; std::string mControlPath;
std::string mEnablePath; std::string mEnablePath;
std::string mModePath; std::string mModePath;
int mDeviceIndex;
std::string mInitialEnable; std::string mInitialEnable;
std::string mInitialMode; std::string mInitialMode;

View File

@@ -16,9 +16,13 @@ public:
std::vector<std::shared_ptr<Sensor>> TemperatureSensors(); std::vector<std::shared_ptr<Sensor>> TemperatureSensors();
std::vector<std::shared_ptr<Sensor>> RPMSensors(); std::vector<std::shared_ptr<Sensor>> RPMSensors();
void ReloadSensors();
private: private:
template <sensors_subfeature_type T> template <sensors_subfeature_type T>
std::vector<std::shared_ptr<Sensor>> Sensors(); std::vector<std::shared_ptr<Sensor>> Sensors();
void InitSensors();
void CleanupSensors();
private: private:
FILE *mConfigFile; FILE *mConfigFile;

View File

@@ -12,6 +12,8 @@ public:
std::vector<std::shared_ptr<Sensor>> TemperatureSensors(); std::vector<std::shared_ptr<Sensor>> TemperatureSensors();
std::vector<std::shared_ptr<Sensor>> RPMSensors(); std::vector<std::shared_ptr<Sensor>> RPMSensors();
void ReloadSensors();
private: private:
std::unique_ptr<LMSensorsFacade> mLMSensorsFacade; std::unique_ptr<LMSensorsFacade> mLMSensorsFacade;
std::unique_ptr<GPUSensorsFacade> mGPUSensorsFacade; std::unique_ptr<GPUSensorsFacade> mGPUSensorsFacade;

37
app/meson.build Normal file
View File

@@ -0,0 +1,37 @@
project('fantasize', 'cpp', version : '0.3.0', default_options : 'cpp_std=c++20')
src = [
'src/main.cxx',
'src/sensor/LMSensorsFacade.cxx',
'src/sensor/Sensor.cxx',
'src/sensor/LMSensor.cxx',
'src/pwm/PWMControl.cxx',
'src/pwm/PWMControlFacade.cxx',
'src/fan/HwmonFan.cxx',
'src/fan/FanCurve.cxx',
'src/fan/FanLabeler.cxx',
'src/fan/Aggregators.cxx',
'src/FanGenerator.cxx',
'src/Serializer.cxx',
'src/sensor/SensorManager.cxx',
'src/Controller.cxx',
'src/Settings.cxx',
'src/App.cxx'
]
deps = [
dependency('nlohmann_json'),
dependency('boost', modules : ['program_options', 'log', 'log_setup', 'date_time', 'thread']),
dependency('tbb'),
meson.get_compiler('cpp').find_library('sensors')
]
inc = include_directories('include')
exe = executable('fantasize',
src,
dependencies: deps,
install: true,
include_directories: inc)
install_data('unit/fantasize.service', install_dir : '/usr/lib/systemd/system')

View File

@@ -1,16 +1,39 @@
#include "fan/Fan.h"
#include "fan/FanCurve.h"
#include <chrono>
#include <execution> #include <execution>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp>
#include <App.h> #include <App.h>
#include <memory>
#include <thread>
using namespace std; using namespace std;
void App::Init() { void App::Init() {
BOOST_LOG_FUNCTION();
bool everythingFound = false;
mFans = mSerializer.DeserializeFans(mSensorManager.RPMSensors()); mFans = mSerializer.DeserializeFans(mSensorManager.RPMSensors());
auto fanCurves = mSerializer.DeserializeFanCurves( auto fanCurves = mSerializer.DeserializeFanCurves(
mSensorManager.TemperatureSensors(), mFans); mSensorManager.TemperatureSensors(), mFans, everythingFound);
mController = make_unique<Controller>(fanCurves); 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();
mController = make_unique<Controller>(mSettings, fanCurves);
} }
void App::InitialSetup() { void App::InitialSetup() {

View File

@@ -6,10 +6,10 @@
using namespace std; using namespace std;
#define TIMEOUT 500 Controller::Controller(shared_ptr<Settings> settings,
vector<shared_ptr<FanCurve>> curves)
Controller::Controller(vector<shared_ptr<FanCurve>> curves) : mTimeout((1 / settings->Frequency()) * 1000), mFanCurves(curves),
: mFanCurves(curves), mRun(false) {} mRun(false) {}
Controller::~Controller() { StopFanControlLoop(); } Controller::~Controller() { StopFanControlLoop(); }
@@ -32,6 +32,6 @@ void Controller::Loop() {
c->DoFanControl(); c->DoFanControl();
} }
this_thread::sleep_for(chrono::milliseconds(TIMEOUT)); this_thread::sleep_for(chrono::milliseconds(mTimeout));
} }
} }

View File

@@ -8,7 +8,7 @@
#include <fan/HwmonFan.h> #include <fan/HwmonFan.h>
#include <pwm/PWMControl.h> #include <pwm/PWMControl.h>
#define SETTLE_TIMEOUT 5 #define SETTLE_TIMEOUT 30
using namespace std; using namespace std;
@@ -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->Power(100); c->SetPower(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->Power(50); c->SetPower(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->Power(100); c->SetPower(100);
} }
return mapping; return mapping;

View File

@@ -1,15 +1,16 @@
#include "fan/FanCurve.h"
#include "sensor/Sensor.h"
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp>
#include <Serializer.h> #include <Serializer.h>
#include <fan/HwmonFan.h> #include <fan/HwmonFan.h>
#include <pwm/PWMControl.h> #include <pwm/PWMControl.h>
#include <sensor/LMSensor.h> #include <sensor/LMSensor.h>
#include <string>
using namespace std; using namespace std;
namespace fs = filesystem; namespace fs = filesystem;
@@ -32,9 +33,11 @@ void Serializer::SerializeFans(vector<shared_ptr<Fan>> fans) {
vector<shared_ptr<Fan>> vector<shared_ptr<Fan>>
Serializer::DeserializeFans(vector<shared_ptr<Sensor>> availableSensors) { Serializer::DeserializeFans(vector<shared_ptr<Sensor>> availableSensors) {
BOOST_LOG_FUNCTION();
vector<shared_ptr<Fan>> fans; vector<shared_ptr<Fan>> fans;
// Create a for the sensors first, then searching becomes cheaper // Create a map for the sensors first, then searching becomes cheaper
map<string, shared_ptr<Sensor>> sensorMap; map<string, shared_ptr<Sensor>> sensorMap;
for (auto s : availableSensors) { for (auto s : availableSensors) {
sensorMap[s->toString()] = s; sensorMap[s->toString()] = s;
@@ -43,23 +46,31 @@ Serializer::DeserializeFans(vector<shared_ptr<Sensor>> availableSensors) {
auto data = ReadJson(); auto data = ReadJson();
try { try {
for (auto &el : data["fans"].items()) { for (auto &el : data["fans"].items()) {
auto pwmControl = make_shared<PWMControl>(el.value()["PWMControl"]); auto pwmControl = make_shared<PWMControl>(el.value()["PWMControl"]["Path"], el.value()["PWMControl"]["Index"]);
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"]; int startPWM = el.value()["StartPWM"];
string label = el.value()["Label"]; string label = el.value()["Label"];
bool zeroFan = el.value()["ZeroFan"];
auto fan = make_shared<HwmonFan>(pwmControl, rpmSensor); auto fan = make_shared<HwmonFan>(pwmControl, rpmSensor);
fan->MinPWM(minPWM); fan->MinPWM(minPWM);
fan->StartPWM(startPWM); fan->StartPWM(startPWM);
fan->Label(label); fan->Label(label);
fan->ZeroFanModeSupported(zeroFan);
fans.push_back(fan); fans.push_back(fan);
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cout << "Deserialization error! Message: " << e.what() << std::endl; 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; return fans;
} }
@@ -85,7 +96,10 @@ json Serializer::ReadJson() {
vector<shared_ptr<FanCurve>> Serializer::DeserializeFanCurves( vector<shared_ptr<FanCurve>> Serializer::DeserializeFanCurves(
std::vector<std::shared_ptr<Sensor>> availableSensors, std::vector<std::shared_ptr<Sensor>> availableSensors,
std::vector<std::shared_ptr<Fan>> availableFans) { std::vector<std::shared_ptr<Fan>> availableFans, bool &everythingFound) {
BOOST_LOG_FUNCTION();
everythingFound = true;
auto data = ReadJson(); auto data = ReadJson();
map<string, shared_ptr<Sensor>> sensorMap; map<string, shared_ptr<Sensor>> sensorMap;
@@ -112,6 +126,11 @@ vector<shared_ptr<FanCurve>> Serializer::DeserializeFanCurves(
for (auto &sensor : el.value()["Sensors"].items()) { for (auto &sensor : el.value()["Sensors"].items()) {
if (sensorMap.contains(sensor.value())) if (sensorMap.contains(sensor.value()))
sensors.push_back(sensorMap[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()) { for (auto &fan : el.value()["Fans"].items()) {
@@ -119,8 +138,37 @@ vector<shared_ptr<FanCurve>> Serializer::DeserializeFanCurves(
fans.push_back(fanMap[fan.value()]); fans.push_back(fanMap[fan.value()]);
} }
curves.push_back(make_shared<FanCurve>(steps, sensors, fans)); std::unique_ptr<Aggregator> aggregator =
aggregatorFromString(el.value()["AggregateFunction"]);
int hysteresis = el.value()["Hysteresis"];
curves.push_back(make_shared<FanCurve>(steps, sensors, fans,
std::move(aggregator), hysteresis));
} }
return curves; return curves;
} }
std::unique_ptr<Aggregator>
Serializer::aggregatorFromString(std::string str) const {
if (str == "max")
return std::make_unique<MaxAggregator>();
else
return std::make_unique<AverageAggregator>();
}
shared_ptr<Settings> Serializer::DeserializeSettings() {
int frequency = FREQUENCY_DEFAULT;
auto data = ReadJson();
if (data.contains("settings")) {
auto items = data["settings"];
if (items.contains("frequency"))
frequency = items["frequency"];
}
return make_shared<Settings>(frequency);
}

17
app/src/Settings.cxx Normal file
View File

@@ -0,0 +1,17 @@
#include <iostream>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp>
#include <Settings.h>
Settings::Settings(int frequency) : mFrequency(frequency) { LogSettings(); }
int Settings::Frequency() const { return mFrequency; }
void Settings::LogSettings() {
BOOST_LOG_FUNCTION();
BOOST_LOG_TRIVIAL(info) << "### Settings";
BOOST_LOG_TRIVIAL(info) << "Frequency: " << mFrequency << "Hz";
}

View File

@@ -0,0 +1,39 @@
#include <climits>
#include <fan/Aggregators.h>
int
AverageAggregator::aggregate(std::vector<std::shared_ptr<Sensor>> sensors) const
{
int sum = 0;
for (auto sensor : sensors) {
sum += sensor->value();
}
return sum / sensors.size();
}
const std::string
AverageAggregator::toString() const
{
return "Average";
}
int
MaxAggregator::aggregate(std::vector<std::shared_ptr<Sensor>> sensors) const
{
int max = INT_MIN;
for (auto sensor : sensors) {
if (sensor->value() > max)
max = sensor->value();
}
return max;
}
const std::string
MaxAggregator::toString() const
{
return "Max";
}

View File

@@ -1,25 +1,37 @@
#include <boost/log/attributes/named_scope.hpp>
#include <iostream> #include <iostream>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <fan/FanCurve.h> #include <fan/FanCurve.h>
#include <memory>
using namespace std; using namespace std;
FanCurve::FanCurve(std::vector<FanStep> steps, 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) { std::unique_ptr<Aggregator> aggregator, int hysteresis)
: mSteps(steps), mTempSensors(sensors), mFans(fans),
mAggregator(std::move(aggregator)), mHystersis(hysteresis),
mLastTemperature(INT_MIN) {
PrintInfo(); PrintInfo();
} }
void FanCurve::DoFanControl() { void FanCurve::DoFanControl() {
BOOST_LOG_FUNCTION(); BOOST_LOG_FUNCTION()
int temp = AggregateTemperature(); BOOST_LOG_TRIVIAL(trace) << "## Fans in curve";
for (auto f : mFans)
BOOST_LOG_TRIVIAL(trace) << f->toString();
int t0, t1, p0, p1; BOOST_LOG_TRIVIAL(trace) << "## Sensors in curve";
for (auto s : mTempSensors)
BOOST_LOG_TRIVIAL(trace) << s->toString();
int temp = mAggregator->aggregate(mTempSensors);
int t0 = 0, t1 = 0, p0 = 0, p1 = 0;
int targetFanPower; int targetFanPower;
if (temp <= mSteps[0].Temp) { if (temp <= mSteps[0].Temp) {
@@ -27,7 +39,7 @@ void FanCurve::DoFanControl() {
} else if (temp > mSteps[mSteps.size() - 1].Temp) { } else if (temp > mSteps[mSteps.size() - 1].Temp) {
targetFanPower = 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 < (int)mSteps.size(); i++) {
if (temp > mSteps[i].Temp) { if (temp > mSteps[i].Temp) {
t0 = mSteps[i].Temp; t0 = mSteps[i].Temp;
p0 = mSteps[i].Percent; p0 = mSteps[i].Percent;
@@ -40,27 +52,23 @@ void FanCurve::DoFanControl() {
targetFanPower = p0 + ((p1 - p0) / (t1 - t0)) * (temp - t0); targetFanPower = p0 + ((p1 - p0) / (t1 - t0)) * (temp - t0);
} }
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) { for (auto f : mFans) {
if (f->RPM() <= 0) { ApplyFanPower(f, targetFanPower);
BOOST_LOG_TRIVIAL(warning) << "Fan stopped completely!";
f->PWM(f->StartPWM());
BOOST_LOG_TRIVIAL(info) << "Adjusting minPWM of fan " << f->toString(); mLastTemperature = temp;
f->MinPWM(f->MinPWM() + 2); }
} else { } else {
f->PWM(targetFanPower); for (auto f : mFans)
} f->EnforceSetValue();
}
}
// Dummy Implementation using AVG BOOST_LOG_TRIVIAL(trace) << "not passed";
int FanCurve::AggregateTemperature() {
int sum = 0;
for (auto s : mTempSensors) {
sum += s->value();
} }
return sum / mTempSensors.size();
} }
void FanCurve::PrintInfo() { void FanCurve::PrintInfo() {
@@ -92,4 +100,29 @@ void FanCurve::PrintInfo() {
} }
BOOST_LOG_TRIVIAL(info) << sStream.str(); BOOST_LOG_TRIVIAL(info) << sStream.str();
sStream.str(string());
sStream << "Aggregate function: " << mAggregator->toString();
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> fan, int targetFanPower) {
BOOST_LOG_FUNCTION();
if (!fan->ZeroFanModeSupported() && fan->RPM() <= 0) {
BOOST_LOG_TRIVIAL(warning) << "Fan " << fan->toString() << " stopped completely!";
fan->PWM(fan->StartPWM());
fan->AdjustPWMLimits();
} else {
fan->PWM(targetFanPower);
}
} }

View File

@@ -16,7 +16,7 @@ void FanLabeler::RunFanLabelInteraction(
cout << endl; cout << endl;
for (auto f : fans) { for (auto f : fans) {
cout << "Setting fan to max power" << endl; cout << "Setting fan " << f->toString() << " to max power" << endl;
f->PWM(100); f->PWM(100);

View File

@@ -3,11 +3,15 @@
#include <ostream> #include <ostream>
#include <thread> #include <thread>
#include "pwm/PWMControl.h" #include <boost/log/attributes/named_scope.hpp>
#include <boost/json/object.hpp> #include <boost/log/trivial.hpp>
#include <fan/HwmonFan.h> #include <fan/HwmonFan.h>
#include <pwm/PWMControl.h>
#define TIMEOUT 10 #define TIMEOUT 10
#define INHIBIT_STOP_PERIOD 120
#define STEP 2
using namespace std; using namespace std;
@@ -19,10 +23,38 @@ HwmonFan::HwmonFan(std::shared_ptr<PWMControl> pwmControl,
void HwmonFan::PWM(int percent) { void HwmonFan::PWM(int percent) {
if (percent < mMinPWM) { if (percent < mMinPWM) {
mPWMControl->Power(mMinPWM); if (mZeroFanModeSupported && InhibitStopPeriodExpired()) {
SetPower(percent);
mWasStopped = true;
} else { } else {
mPWMControl->Power(percent); SetPower(mMinPWM);
} }
} else {
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->SetPower(percent);
mSetValue = percent;
} }
int HwmonFan::RPM() { return mRpmSensor->value(); } int HwmonFan::RPM() { return mRpmSensor->value(); }
@@ -33,9 +65,15 @@ void HwmonFan::MinPWM(int value) { mMinPWM = value; }
int HwmonFan::MinPWM() { return mMinPWM; } int HwmonFan::MinPWM() { return mMinPWM; }
void HwmonFan::StartPWM(int value) { mStartPWM = value; }
int HwmonFan::StartPWM() { return mStartPWM; } int HwmonFan::StartPWM() { return mStartPWM; }
void HwmonFan::StartPWM(int value) { mStartPWM = value; } void HwmonFan::ZeroFanModeSupported(bool value) {
mZeroFanModeSupported = value;
}
bool HwmonFan::ZeroFanModeSupported() { return mZeroFanModeSupported; }
void HwmonFan::FindPWMLimits() { void HwmonFan::FindPWMLimits() {
cout << "Looking for minimal PWM" << endl; cout << "Looking for minimal PWM" << endl;
@@ -43,12 +81,12 @@ void HwmonFan::FindPWMLimits() {
mMinPWM = 0; mMinPWM = 0;
mStartPWM = 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));
if (RPM() <= 0) { if (RPM() <= 0) {
minPWM = curPWM + 5; minPWM = curPWM + STEP;
break; break;
} }
} }
@@ -62,7 +100,7 @@ void HwmonFan::FindPWMLimits() {
int startPWM = 0; int startPWM = 0;
cout << "Looking for start PWM!" << endl; cout << "Looking for start PWM!" << endl;
for (int curPWM = minPWM - 5; curPWM < 100; curPWM += 5) { for (int curPWM = minPWM - STEP; curPWM < 100; curPWM += STEP) {
PWM(curPWM); PWM(curPWM);
this_thread::sleep_for(chrono::seconds(TIMEOUT)); this_thread::sleep_for(chrono::seconds(TIMEOUT));
@@ -77,13 +115,27 @@ void HwmonFan::FindPWMLimits() {
} }
} }
void HwmonFan::AdjustPWMLimits() {
BOOST_LOG_FUNCTION()
chrono::time_point now = chrono::steady_clock::now();
if ((now - mLastAdjustmentTime) >
chrono::duration(chrono::seconds(TIMEOUT))) {
BOOST_LOG_TRIVIAL(info) << "Increasing minimal fan speed";
mLastAdjustmentTime = now;
mMinPWM += 2;
}
}
void HwmonFan::EnforceSetValue() { mPWMControl->SetPower(mSetValue); }
json HwmonFan::toJson() const { json HwmonFan::toJson() const {
json obj; json obj;
obj = {mPWMControl->toJson(), obj = {mPWMControl->toJson(), mRpmSensor->toJson(),
mRpmSensor->toJson(), {"Label", mLabel}, {"MinPWM", mMinPWM},
{"Label", mLabel}, {"StartPWM", mStartPWM}, {"ZeroFan", mZeroFanModeSupported}};
{"MinPWM", mMinPWM},
{"StartPWM", mStartPWM}};
return obj; return obj;
} }
@@ -91,6 +143,6 @@ const string HwmonFan::toString() const {
if (!mLabel.empty()) { if (!mLabel.empty()) {
return mLabel; return mLabel;
} else { } else {
return "fan:" + mPWMControl->toString(); return mPWMControl->toString();
} }
} }

View File

@@ -1,4 +1,3 @@
#include <boost/log/expressions/formatters/named_scope.hpp>
#include <csignal> #include <csignal>
#include <iostream> #include <iostream>
@@ -6,6 +5,7 @@
#include <boost/log/attributes/named_scope.hpp> #include <boost/log/attributes/named_scope.hpp>
#include <boost/log/core.hpp> #include <boost/log/core.hpp>
#include <boost/log/expressions.hpp> #include <boost/log/expressions.hpp>
#include <boost/log/expressions/formatters/named_scope.hpp>
#include <boost/log/expressions/message.hpp> #include <boost/log/expressions/message.hpp>
#include <boost/log/support/date_time.hpp> #include <boost/log/support/date_time.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
@@ -20,13 +20,13 @@
#include <App.h> #include <App.h>
#define PROJECT_VERSION "v0.3.0"
namespace po = boost::program_options; namespace po = boost::program_options;
namespace logging = boost::log; namespace logging = boost::log;
App app; App app;
static int doInitialSetup = 0;
void signal_handler(int s) { app.Shutdown(); } void signal_handler(int s) { app.Shutdown(); }
void InitLogging(bool verbose) { void InitLogging(bool verbose) {
@@ -59,7 +59,11 @@ void InitLogging(bool verbose) {
} }
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,h", "produce help message")( desc.add_options()("help,h", "produce help message")(

View File

@@ -1,8 +1,8 @@
#include <boost/log/attributes/named_scope.hpp>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <pwm/PWMControl.h> #include <pwm/PWMControl.h>
@@ -15,17 +15,27 @@
using namespace std; using namespace std;
namespace fs = filesystem; namespace fs = filesystem;
PWMControl::PWMControl(string controlPath) : mControlPath(controlPath) { PWMControl::PWMControl(string controlPath, int deviceIndex)
fs::path pathEnable(mControlPath + PWM_POSTFIX_ENABLE); : mConfigPath(controlPath), mDeviceIndex(deviceIndex) {
fs::path pathMode(mControlPath + PWM_POSTFIX_MODE); auto path = fs::path{controlPath};
mEnablePath = pathEnable; int fileCount =
mModePath = pathMode; distance(fs::directory_iterator(path), fs::directory_iterator{});
if (fileCount != 1)
throw runtime_error("More than one HWMON device present, unsupported");
for (auto de : fs::directory_iterator(path)) {
auto testPath = de.path();
mControlPath =
fs::path{de.path() / (string("pwm").append(to_string(deviceIndex)))}
.string();
}
mEnablePath = fs::path{mControlPath + PWM_POSTFIX_ENABLE}.string();
mModePath = fs::path{mControlPath + PWM_POSTFIX_MODE}.string();
ifstream istrm; ifstream istrm;
mCurrentValue = Power();
istrm.open(mEnablePath); istrm.open(mEnablePath);
istrm >> mInitialEnable; istrm >> mInitialEnable;
istrm.close(); istrm.close();
@@ -42,30 +52,28 @@ PWMControl::~PWMControl() {
Reset(); Reset();
} }
void PWMControl::Power(int percent) { void PWMControl::SetPower(int percent) {
BOOST_LOG_FUNCTION(); BOOST_LOG_FUNCTION();
int pwmValue = (PWM_MAX_VALUE * percent) / 100; int pwmValue = (PWM_MAX_VALUE * percent) / 100;
if (percent != mCurrentValue) { if (pwmValue != CurrentPWM()) {
BOOST_LOG_TRIVIAL(trace) BOOST_LOG_TRIVIAL(trace) << "Updating control value of " << toString()
<< "Updating control value to " << percent << "% (" << pwmValue << ")"; << " 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::Power() { int PWMControl::CurrentPWM() {
int value; int value;
ifstream istrm; ifstream istrm;
istrm.open(mControlPath); istrm.open(mControlPath);
istrm >> value; istrm >> value;
return (value * 100) / PWM_MAX_VALUE; return value;
} }
void PWMControl::EnableManualControl() { void PWMControl::EnableManualControl() {
@@ -86,11 +94,9 @@ void PWMControl::Reset() {
ostrm.close(); ostrm.close();
} }
const string PWMControl::toString() const { const string PWMControl::toString() const { return fs::path(mControlPath); }
return fs::path(mControlPath).filename();
}
json PWMControl::toJson() const { json PWMControl::toJson() const {
json obj = {"PWMControl", mControlPath}; json obj = {"PWMControl", {{"Path", mConfigPath}, {"Index", mDeviceIndex}}};
return obj; return obj;
} }

View File

@@ -1,6 +1,7 @@
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
#include <regex> #include <regex>
#include <string>
#include <pwm/PWMControlFacade.h> #include <pwm/PWMControlFacade.h>
@@ -20,14 +21,20 @@ vector<shared_ptr<PWMControl>> PWMControlFacade::PWMControls() {
for (const fs::directory_entry &hwmon_device : for (const fs::directory_entry &hwmon_device :
fs::directory_iterator{HWMON_BASE_PATH}) { fs::directory_iterator{HWMON_BASE_PATH}) {
// Resolve symlink to get device path instead of hwmon path, the latter of
// which can change
fs::path actual_path =
fs::canonical(HWMON_BASE_PATH / fs::read_symlink(hwmon_device));
for (const fs::directory_entry &control : for (const fs::directory_entry &control :
fs::directory_iterator{hwmon_device}) { fs::directory_iterator{actual_path}) {
auto filename = control.path().filename().string(); auto filename = control.path().filename().string();
if (regex_match(filename, re_ctrl)) { if (regex_match(filename, re_ctrl)) {
auto controlPath = control.path().string(); auto controlIndex = filename.back() - '0';
auto controlPath = actual_path.parent_path();
controls.push_back(make_shared<PWMControl>(controlPath)); controls.push_back(make_shared<PWMControl>(controlPath, controlIndex));
} }
} }
} }

View File

@@ -1,3 +1,5 @@
#include <iostream>
#include <boost/json/kind.hpp> #include <boost/json/kind.hpp>
#include <sensor/LMSensor.h> #include <sensor/LMSensor.h>
#include <sensors/sensors.h> #include <sensors/sensors.h>
@@ -17,7 +19,9 @@ int LMSensor::value() {
} }
const string LMSensor::toString() const { const string LMSensor::toString() const {
return sensors_get_label(mChipName, mFeature); ostringstream os;
os << mChipName->prefix << "." << sensors_get_label(mChipName, mFeature);
return os.str();
} }
json LMSensor::toJson() const { json LMSensor::toJson() const {

View File

@@ -12,12 +12,10 @@ using namespace std;
#define CONFIG_FILE "/etc/conf.d/sensors" #define CONFIG_FILE "/etc/conf.d/sensors"
LMSensorsFacade::LMSensorsFacade() : mConfigFile(fopen(CONFIG_FILE, "r")) { LMSensorsFacade::LMSensorsFacade() : mConfigFile(fopen(CONFIG_FILE, "r")) {
if (sensors_init(mConfigFile) != 0) { InitSensors();
throw runtime_error("Config file doesn't exist");
}
} }
LMSensorsFacade::~LMSensorsFacade() { sensors_cleanup(); } LMSensorsFacade::~LMSensorsFacade() { CleanupSensors(); }
std::vector<std::shared_ptr<Sensor>> LMSensorsFacade::TemperatureSensors() { std::vector<std::shared_ptr<Sensor>> LMSensorsFacade::TemperatureSensors() {
return Sensors<SENSORS_SUBFEATURE_TEMP_INPUT>(); return Sensors<SENSORS_SUBFEATURE_TEMP_INPUT>();
@@ -48,3 +46,16 @@ std::vector<std::shared_ptr<Sensor>> LMSensorsFacade::Sensors() {
return 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(); }

View File

@@ -1,4 +1,3 @@
#include <boost/json/object.hpp>
#include <include/nvml.h> #include <include/nvml.h>
#include <sensor/NvidiaSensor.h> #include <sensor/NvidiaSensor.h>

View File

@@ -1,5 +1,8 @@
#include <sensor/SensorManager.h> #include <sensor/SensorManager.h>
#include <boost/log/attributes/named_scope.hpp>
#include <boost/log/trivial.hpp>
using namespace std; using namespace std;
SensorManager::SensorManager() SensorManager::SensorManager()
@@ -7,12 +10,20 @@ SensorManager::SensorManager()
mGPUSensorsFacade(make_unique<GPUSensorsFacade>()) {} mGPUSensorsFacade(make_unique<GPUSensorsFacade>()) {}
vector<shared_ptr<Sensor>> SensorManager::TemperatureSensors() { vector<shared_ptr<Sensor>> SensorManager::TemperatureSensors() {
BOOST_LOG_FUNCTION();
vector<shared_ptr<Sensor>> tempSensors; vector<shared_ptr<Sensor>> tempSensors;
tempSensors = mLMSensorsFacade->TemperatureSensors(); tempSensors = mLMSensorsFacade->TemperatureSensors();
auto gpuSensors = mGPUSensorsFacade->TemperatureSensors(); BOOST_LOG_TRIVIAL(trace) << "### Temperature sensors:";
tempSensors.insert(tempSensors.end(), gpuSensors.begin(), gpuSensors.end()); for (auto s : tempSensors) {
BOOST_LOG_TRIVIAL(trace) << s->toString();
}
// auto gpuSensors = mGPUSensorsFacade->TemperatureSensors();
// tempSensors.insert(tempSensors.end(), gpuSensors.begin(),
// gpuSensors.end());
return tempSensors; return tempSensors;
} }
@@ -20,3 +31,5 @@ vector<shared_ptr<Sensor>> SensorManager::TemperatureSensors() {
vector<shared_ptr<Sensor>> SensorManager::RPMSensors() { vector<shared_ptr<Sensor>> SensorManager::RPMSensors() {
return mLMSensorsFacade->RPMSensors(); return mLMSensorsFacade->RPMSensors();
} }
void SensorManager::ReloadSensors() { mLMSensorsFacade->ReloadSensors(); }

View File

@@ -7,4 +7,4 @@ ExecStart=/usr/local/bin/fantasize
Restart=always Restart=always
[Install] [Install]
WantedBy=multi-user.target WantedBy=graphical.target